RestTemplate是spring-web.jar包下的一个类,简化了http的请求过程,在应用中能够方便地调用rest服务。
默认情况下,使用无参构造器创建的RestTemplate对象用的是java.net包的实现完成http的连接。
可以使用带参构造器RestTemplate(ClientHttpRequestFactory requestFactory)创建用HttpClient实现http连接的RestTemplate对象
我们这里用到不仅仅是Netflix Ribbon这个基础的Ribbon组件,还包含springframework.cloud.netflix.ribbon 这个基于Netflix Ribbon再次封装的负载均衡框架。主要功能就是通过负载均衡算法完成服务调用。Ribbon可以从服务注册中心拿到具体的服务地址列表,根据负载均衡算法选择调用其中一个服务。
所有规则(算法)都要实现IRule接口,所以先看接口结构
package com.netflix.loadbalancer;
public interface IRule {
Server choose(Object var1); // 选择一个服务
void setLoadBalancer(ILoadBalancer var1); // 设置负载均衡器
ILoadBalancer getLoadBalancer(); // 获取负载均衡器
}
主要的方法就是那个choose()方法,用来选择服务,暂不深究,
查看IRule接口的实现类就能知道Ribbon有哪些内置算法。

| 名称 | 说明 |
|---|---|
| RoundRobinRule | 轮询算法 |
| RandomRule | 随机算法 |
| AvailabilityFilteringRule | 先过滤掉不可用服务,再使用轮询算法 |
| WeightedResponseTimeRule | 自动根据响应时间计算权重,统计信息不足时使用轮询算法 |
| RetryRule | 使用轮询算法获取服务,如遇失败在规定时间内重试 |
| BestAvaliableRule | 先过滤掉不可用服务,选择并发量最小的服务 |
| ZoneAvoidanceRule | 根据服务的性能和可用性去选择服务 |
在原本的Netflix Ribbon 组件中的BaseLoadBalancer中,可以看到默认的负载均衡策略是RoundRobinRule轮询算法。
package com.netflix.loadbalancer;
public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnectionListener, IClientConfigAware {
...
private static final IRule DEFAULT_RULE = new RoundRobinRule();
...
}
但是在springframework.cloud.netflix.ribbon框架中重新定义了负载均衡的初始化逻辑
@Bean
@ConditionalOnMissingBean // 当容器没有 IRule 这个 Bean 对象的时候进行初始化
public IRule ribbonRule(IClientConfig config) {
// 先去检查有没有配置对应的规则
if (this.propertiesFactory.isSet(IRule.class, this.name)) {
return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
} else { // 没有的话直接创建一个 ZoneAvoidanceRule 对象交给容器管理
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
}
所以在springframework.cloud.netflix.ribbon框架中Ribbon Client的默认负载均衡策略是ZoneAvoidanceRule根据服务的性能和可用性去选择服务。
基于springframework.cloud.netflix.ribbon框架中负载均衡的初始化逻辑,可以知道它在容器中没有IRule对象的时候会先检查配置,如果没有就创建默认的规则。所以我们可以通过注入IRule对象的方式修改成自己想要的规则。
@Configuration
public class RuleConfig {
@Bean
public IRule injectRule() {
return new RandomRule(); // 创建一个 RandomRule 交给容器管理
}
}
创建一个配置文件,注入RestTemplate 对象
@Configuration
public class GenericConfig {
@Bean
@LoadBalanced // 必须添加负载均衡注解
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
因为前面已经设置过Ribbon的规则了,注意这个@LoadBalanced,它能让RestTemplate 对象直接被Ribbon代理,所以后面我们直接使用RestTemplate 对象去调用服务就行,它会自动均衡负载。
用这个RestTemplate 对象远程调用服务,我这里用了了nacos作为服务注册中心。
@RestController
public class TestController {
@Value("${server.port}")
private String port;
private final String SERVER_URL = "http://nacos-provider"; // 这里不写具体的某个服务的ip和端口,而是通过注册到 Nacos 的服务名代替
@Resource
private RestTemplate restTemplate;
@RequestMapping("/test")
public String test() {
String result = restTemplate.getForObject(SERVER_URL + "/test", String.class);
return "①号消费者(" + port + "):" + result;
}
}
最简单的方法就是模仿,随便挑一个规则看看它是怎么实现的,这里挑最简单的RandomRule
public class RandomRule extends AbstractLoadBalancerRule {
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object key) {
...
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
可以看到它继承了AbstractLoadBalancerRule,然后重写(这里应该叫实现比较合适)了它的choose()方法和initWithNiwsConfig(),还有两个内部的算法函数,里面是具体的负载均衡算法,暂不深究。
依葫芦画瓢,写个简单的算法
@Component
public class CustomRule extends AbstractLoadBalancerRule {
private AtomicInteger atomicInteger = new AtomicInteger(0); //
public Server choose(Object key) {
return chooseLogic(getLoadBalancer(), key);
}
public Server chooseLogic(ILoadBalancer loadBalancer, Object key) {
if (loadBalancer == null) {
return null;
}
List<Server> allServers = loadBalancer.getAllServers(); // 获取服务列表
int requestNum = this.atomicInteger.incrementAndGet(); // 自增
if (requestNum >= Integer.MAX_VALUE) {
atomicInteger = new AtomicInteger(0); // 重置
}
if (allServers != null) {
int size = allServers.size();
int index = requestNum % size;
Server server = allServers.get(index);
if (server == null || !server.isAlive()) {
return null;
}
return server;
}
return null;
}
public void initWithNiwsConfig(IClientConfig iClientConfig) { } // 空实现
}
同样的,注入容器即可
@Configuration
public class RuleConfig {
@Bean
public IRule injectRule() {
return new CustomRule ();
}
}
RestTemplate+Ribbon这个远程调用方式是比较老的,而且在调用的时候都要填写地址。现在一般都用OpenFeign或者Dubbo Spring Cloud代替它,更加方便简洁。
承上:SpringCloudAlibaba系列微服务搭建笔记一_Nacos
启下:SpringCloudAlibaba系列微服务搭建笔记三_Sentinel
