1.概述
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具.
【Ribbon框架自带的负载策略类】
- com.netflix.loadbalancer.RoundRobinRule - 轮询
- com.netflix.loadbalancer.RandomRule - 随机
- com.netflix.loadbalancer.RetryRule - 重试,先按RoundRobinRule进行轮询,如果失败就在指定时间内进行重试
- com.netflix.loadbalancer.WeightedResponseTimeRule - 权重,响应速度越快,权重越大,越容易被选中。
- com.netflix.loadbalancer.BestAvailableRule - 先过滤掉不可用的处于断路器跳闸转态的服务,然后选择一个并发量最小的服务
- com.netflix.loadbalancer.AvailabilityFilteringRule - 先过滤掉故障实例,再选择并发量较小的实例
- com.netflix.loadbalancer.ZoneAvoidanceRule - 默认规则,复合判断server所在区域的性能和server的可用性进行服务的选择。
2.使用案例
服务端
服务端无需额外配置,仅仅只要启动服务,将服务注册到nacos注册中心. ```java @RestController @Slf4j @RequestMapping(“/ribbon”) public class RibbonController {
@Value("${server.port}")
private String port;
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return "hello " + name + " come from :" + port;
}
}
<a name="aoAOV"></a>
## 客户端
<a name="Z8lZh"></a>
### 初始化RestTemplate
客户端需要使用**RestTemplate**进行服务的调用.其中 RestTemplate是Spring提供<br />注意:
1. 使用前需要初始化@Bean交给Spring容器进行管理.
1. 使用@LoadBalanced 开启客户端负载均衡
```java
@Configuration
public class RestTemplateConfig {
@LoadBalanced//开启负载均衡
@Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
注入RestTemplate并调用服务端
@RequestMapping("/ribbon")
@RestController
public class RibbonController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return restTemplate.getForObject("http://my-product/ribbon/hello?name={name}", String.class, name);
}
}
测试负载均衡
- 分别使用端口8003,8009启动2个服务端实例
- 启动客户端,并调用 http://localhost:8001/ribbon/hello?name=miaock
注意:使用idea启动多实例,需要开启并行配置
两次点击显示如下:
3.源码解析
源码猜测
- 客户端想要进行负载均衡,必须要有服务端的地址列表信息(这里地址列表需要通过心跳定期维护)
- 拿到地址列表信息通过负载均衡算法进行选择每次请求要调用的服务地址
-
源码验证
client客户端对server服务端地址列表如何维护
- client客户端对每次server服务端的时候rule的选择
- 请求如何通过http进行
验证ribbon负载均衡
通过spring-cloud-starter-alibaba-nacos-discovery 下META-INF\spring.factories
找到 LoadBalancerAutoConfiguration 当前类初始化了RestTemplate
从代码看出就是针对RestTemplate添加拦截器,拦截后做一些初始化操作
- 获取负载均衡器
- 获取服务端地址
- 调用服务端
通过代码跟进 ILoadBalancer loadBalancer = getLoadBalancer(serviceId);可知ILoadBalancer 是从容器中获取的.那么说明当前ILoadBalancer 肯定是在容器启动的时候进行初始化了.
通过 org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration 可知.ILoadBalancer默认是ZoneAwareLoadBalancer
回到刚才位置可知
rule初始化在org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonRule
通过上面可知,进入到com.netflix.loadbalancer.PredicateBasedRule
从这里可以知道是通过ILoadBalancer获取所有的server信息,然后通过规则去获取一个返回
lb.getAllServers():从ILoadBalancer获取所有的server的信息.那么server信息哪里得到的?
关键点.通过下面可知采用轮询方式进行选则服务
解析:
modulo=1 return 0
modulo=2 return 0 ,1
modulo=3 return 0 ,1 ,2
AtomicInteger类compareAndSet通过原子操作实现了CAS操作,最底层基于汇编语言实现
compareAndSet(expect, update)先进行比较,如果相比较的两个值是相等的,那么就进行更新操作
验证客户端如何获取server信息
通过上面负载均衡中一段代码,如下
进去可以看出,列表信息存放在allServerList,现在只需要验证初始化时候获取的信息放在这里一份就ok
RibbonClientConfiguration
最后得到的是所有的服务列表赋值给allServerList.
验证通过http调用
通过restTemplate.getForObject()进入不难发现接口通过http进行调用
org.springframework.web.client.RestTemplate#getForObject(…)