今天在学习微服务中负载均衡特性的时候,注意到了@LoadBalanced注解,这个注解用法如下:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
通过增加@LoadBalanced注解,使RestTemplate调用远程服务时,具有负载均衡的特性。
同时使用RestTemplate对象也会有相应的变化,如果不增加@LoadBalanced注解,则远程调用的方式如下:
//根据微服务名称获取服务实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
//随机选取其中一个实例,手动负载均衡
int index = new Random().nextInt(instances.size());
ServiceInstance serviceInstance = instances.get(index);
//组装服务URL
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
//使用组装的服务URL调用远程服务
Product product = restTemplate.getForObject(
"http://" + url + "/product/" + id, Product.class);
增加了@LoadBalanced注解后,上述代码就会出问题:
2022-11-24 23:46:14.629 ERROR 130888 --- [nio-8091-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No instances available for 169.254.145.20] with root cause
java.lang.IllegalStateException: No instances available for 169.254.145.20
at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:105) ~[spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar:2.1.0.RELEASE]
后台提示“169.254.145.20”没有有效的实例。
实际当中,增加@LoadBalanced注解后,远程调用的方法也需要进行相应的调整,代码如下:
//直接使用微服务名
String url = "service-product";
//直接通过服务名调用远程服务
Product product = restTemplate.getForObject(
"http://" + url + "/product/" + pid, Product.class);
代码简洁了很多!那如果上述代码如果在没有@LoadBalanced注解的情况下,会发生什么呢?我们来看一下:
2022-11-24 23:54:35.161 ERROR 117980 --- [nio-8091-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://service-product/product/2": service-product; nested exception is java.net.UnknownHostException: service-product] with root cause
java.net.UnknownHostException: service-product
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) ~[na:1.8.0_161]
后台提示:service-product 为未知主机。
从上述现象看,我们可以推测@LoadBalanced注解大概做了什么事,即:@LoadBalanced注解按照一定的负载均衡算法,自动将服务名称转换为某一个节点的地址,最后进行服务的调用。
以下是可选的负载均衡策略:
策略名 | 策略描述 | 实现说明 |
BestAvailableRule | 选择一个最小的并发 请求的server | 逐个考察Server,如果Server被 tripped了,则忽略,在选择其中 ActiveRequestsCount最小的server |
AvailabilityFilteringRule | 过滤掉那些因为一直 连接失败的被标记为 circuit tripped的后 端server,并过滤掉 那些高并发的的后端 server(active connections 超过配 置的阈值) | 使用一个AvailabilityPredicate来包含 过滤server的逻辑,其实就就是检查 status里记录的各个server的运行状 态 |
WeightedResponseTimeRule | 根据相应时间分配一 个weight,相应时 间越长,weight越 小,被选中的可能性 越低。 | 一个后台线程定期的从status里面读 取评价响应时间,为每个server计算 一个weight。Weight的计算也比较简 单responsetime 减去每个server自己 平均的responsetime是server的权 重。当刚开始运行,没有形成statas 时,使用roubine策略选择server。 |
RetryRule | 对选定的负载均衡策 略机上重试机制。 | 在一个配置时间段内当选择server不 成功,则一直尝试使用subRule的方 式选择一个可用的server |
RoundRobinRule | 轮询方式轮询选择 server | 轮询index,选择index对应位置的 server |
RandomRule | 随机选择一个server | 在index上随机,选择index对应位置 的server |
ZoneAvoidanceRule | 复合判断server所在 区域的性能和server 的可用性选择server | 使用ZoneAvoidancePredicate和 AvailabilityPredicate来判断是否选择 某个server,前一个判断判定一个 zone的运行性能是否可用,剔除不可 用的zone(的所有server), AvailabilityPredicate用于过滤掉连接 数过多的Server。 |
那么如何设置负载均衡的策略呢?进行如下配置即可:
service-product: # 调用的提供者的名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule