目前主流的负载方案有两种:一种是集中式负载均衡,在消费者和服务者提供中间使用独立的代理方式进行负载,比如 Nginx,另一种就是客户端自己做负载均衡,Ribbon 就是其中一种。
使用 Ribbon
在 Eureka 的案例中,消费者 consumer 模块需要使用 DiscoveryClient 来获取服务实例信息,然后获取ip和端口来访问:
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/callhello")
public String callHello(){
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
ServiceInstance instance = instances.get(0);
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/user/hello";
System.out.println(url);
return restTemplate.getForObject(url,String.class);
}
这里有个问题,实际开发中,user-service 往往会开启很多个集群,这个时候我们获取了很多个实例,那么到底用哪个?一般情况下,我们需要编写均衡算法,而 Ribbon 已经帮我们实现了:
首先修改 RestConfig ,加入 @LoadBalanced 注解:
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
修改 ConsumeController ,把 ip 和 端口替换成服务名就好了,这样就完成了。
@RestController
public class ConsumeController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/callhello")
public String callHello(){
String url = "http://user-service/user/hello";
return restTemplate.getForObject(url,String.class);
}
}
Ribbon 负载均衡策略
Ribbon 默认的负载均衡策略是轮询,修改均衡负载策略,也是在消费者 consume 配置加入:
user-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
然后测试,我注册了两个 user-service,一个端口 8081,一个是 8083。
@SpringBootTest
class ConsumeApplicationTests {
@Autowired
RibbonLoadBalancerClient client;
@Test
void contextLoads() {
for (int i = 0; i < 100; i++) {
ServiceInstance instance = this.client.choose("user-service");
System.out.println(instance.getHost() + ":" + instance.getPort());
}
}
}
测试看到变成了随机了:
image.png
如果想自定义负载策略,可以实现 IRule ,Ribbon 的负载均衡策略有:
- BestAvailabl:选择一个最小的并发请求的 Server,逐个考察 Server,如果 Server 被标记为错误则跳过,然后在选择 ActiveRequestCount 中最小的;
- AvailabilityFilteringRule:过滤掉那些一直连接失败的且被标记为 circuit tripped 的后端 Server ,并过滤掉那些高并发的;
- ZoneAvoidanceRule:根据运行性能和连接数选择 Server;
- RandomRule:随机选择一个 Server;
- RoundRobinRule:轮询选择;
- RetryRule:对选定的负载均衡策略机上重试机制;
- WeightedResponseTimeRule:根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
Ribbon 重试机制
由于 Eureka 是基于 AP 原则构建的,牺牲了数据的一致性,每个 Eureka 服务都会保存注册的服务信息,当注册的客户端与 Eureka 心跳无法保持时,有可能是网络原因,也有可能是服务挂了,在这种情况下,Eureka 还会在一段时间内保存注册信息,这个时候客户端就有可能拿到已经挂掉的服务信息,也就是 Ribbon 拿到了失效的服务信息,这样就导致请求失败了。Ribbon 重试机制可以解决这问题。
使用 RetryRule 重试
最简单的就是利用 Ribbon 自带的重试策略:
user-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RetryRule
使用 Spring Retry 重试
添加 Spring Retry 依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
配置重试信息:
#当前实例的重试次数
user-service.ribbon.maxAutoRetries = 1
#切换实例的重试次数
user-service.ribbon.maxAutoRetriesNextServer = 1
#对所以操作请求都进行重试
user-service.ribbon.okToRetryOnAllOperations = true
网友评论