客户端负载均衡Ribbon和LoadBalancer
转载于:https://www.modb.pro/db/380587
2020年以后随着Netflix的相关SpringCloud组件进入停更状态,Cloud吸收前人经验,自己创造了一套相关组件,今天就跟大家揭秘一下客户端负载均衡组件Ribbon和LoadBalancer。
现状大PK
Ribbon:
目前处于停更维护阶段,由于Ribbon比较优秀,生命力顽强,在生产环境还处在大规模使用中, 暂时还没有完全被替换,但是在2020年以后的cloud版本中已经删除了Ribbon的依赖。
LoadBalancer:
-
cloud正在大规模引入LoadBalancer,还未全部占领高地,随着时间的推移,再加上cloud的大力扶持,未来迟早有一天LoadBalancer会一统江湖。
-
还支持响应式客户端的负载均衡,Spring Cloud LoadBalancer结合Spring Web Flux实现客户端负载均衡调用。即:WebClient。

Ribbon简介&实战
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如轮询,随机等)去连接这些机器,很容易使用Ribbon实现自定义的负载均衡算法。
LB负载均衡是什么
-
我给大家讲一下什么是客户端负载均衡,服务消费者从注册中心拿到服务提供者集群,自己决定使用何种算法找到目标服务,这个过程就是客户端负载均衡,即主动权掌握在自己手里
-
相对客户端负载均衡,服务端负载均衡,就是消费者把请求交给服务端,由服务端来负责找到目标服务提供者,即主动权掌握在被人手里。
-
我看大家还有一种分类方式:集中式LB和进程内LB,都是一个意思。
Ribbon工作流程
-
从注册中心获取服务列表
-
根据用户指定的策略,找到目标服务提供者
-
通过Feign或者OpenFeign,Web客户端调用工具完成用户请求
负载均衡策略
-
RoundRobinRule:轮询
-
RandomRule:随机
-
RetryRule:先按照轮询策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
-
WeightedResponseTimeRule:对轮询的扩展,响应速度越快的实例选择权重越大,越容易被选择
-
BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
-
AvailabiliyFilteringRule:先过滤掉故障实例,再选择并发较小的实例
-
ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

自定义负载均衡策略
-
Ribbon官方明确规定,自动以的配置类,不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们定义的配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的。
-
在某些特殊场景下,我们可能需要targetId相同,所有请求应该落到同一服务上。这个时候随机和轮询都不满足,所以就需要自定义一致性hash算法。
-
在某一个服务中,如果只调用一个服务提供方,可以写在@ComponentScan所扫描的包及子包下。如果是调用多个服务提供方,需要不同的策略,且互不干扰。就需要遵守第一条原则。
-
一般boot工程默认配置了@ComponentScan所扫描的包是启动类Application所在包及子包,所以在有时候我们不配置自动扫描也能正常运行的原因。

-
自定义配置类,比如使用随机替换默认的轮询
@Configurationpublic class MyRule{@Beanpublic IRule myRule(){return new RandomRule();}}
-
在启动类上使用@RibbonClient注解,设置自定义负载均衡策略使其生效,name是服务名,即调用哪个服务集群的时候使用自定义规则。
@SpringBootApplication@EnableEurekaClient@RibbonClient(name = "cloud-payment-service",configuration=MyRule.class)public class Application8080 {public static void main(String[] args) {SpringApplication.run(Application8080.class, args);}}
LoadBalancer简介&实战
LoadBalancer的优势
-
跟Ribbon一样也支持RestTemplate,使用方法一样,由于cloud的其他组件还在使用Ribbon,所以在使用LoadBalancer的时候要屏蔽Ribbon。
-
LoadBalancer还支持响应式编程方式的异步访问客户端的负载均衡,依赖Spring Web Flux实现客户端负载均衡调用,底层依赖Netty的AIO。
引入LoadBalancer依赖jar包
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
配置文件application.yml屏蔽Ribbon负载均衡
在Ribbon和LoadBalancer依赖都引入的情况,cloud默认优先启用Ribbon,当我们想使用LoadBalancer的时候就要先屏蔽到Ribbon
spring:cloud:loadbalancer:ribbon:enabled: false
业务类和启动类都没有区别,还是使用RestTemplate作为客户端来完成服务调用,此方式跟Ribbon在编码方面没什么区别。是Ribbon还是LoadBalancer,主要体现在jar包引入和yml文件关闭默认Ribbon。
//配置RestTemplate@Configurationpublic class ApplicationConfig {@Bean@LoadBalancedpublic RestTemplate getRestTemplate(){return new RestTemplate();}}//controller部分@RestController@RequestMapping("/consumer")public class ConsumerCtrol {private static String PAYMENTURL = "http://CLOUD-PAYMENT-SERVICE";@Resourceprivate RestTemplate restTemplate;@GetMapping("/getPayment/{id}")public MsgResponseBody getPaymet(@PathVariable("id") Long id){System.out.println("adada");return restTemplate.getForObject(PAYMENTURL+"/payment/getPayment/"+id+"?userName=123", MsgResponseBody.class);}}
WebClient方式发起服务调用
引入webflux的相关依赖
<dependency><!-- Spring Webflux响应式web编程--><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>
配置类中声明要注入的WebClient.Builder,是WebClient的内部类,也是编程的入口
//配置RestTemplate@Configurationpublic class ApplicationConfig {@Bean@LoadBalancedpublic RestTemplate getRestTemplate(){return new RestTemplate();}@Bean@LoadBalancedpublic WebClient.Builder builder() {return WebClient.builder();}}//controller部分@RestController@RequestMapping("/consumer")public class ConsumerCtrol {private static String PAYMENTURL = "http://CLOUD-PAYMENT-SERVICE";@Resourceprivate RestTemplate restTemplate;@GetMapping("/getPayment/{id}")public MsgResponseBody getPaymet(@PathVariable("id") Long id){System.out.println("adada");return restTemplate.getForObject(PAYMENTURL+"/payment/getPayment/"+id+"?userName=123", MsgResponseBody.class);}@GetMapping("/getPayment/webClient/{id}")public Mono<MsgResponseBody> getPaymetByWebClient(@PathVariable("id") Long id) {System.out.println("bbbbbbb");//return restTemplate.getForObject(PAYMENTURL + "/payment/getPayment/" + id + "?userName=123", MsgResponseBody.class);return clientBuilder.baseUrl(PAYMENTURL).build().get().uri("/payment/getPayment/" + id + "?userName=123").retrieve().bodyToMono(MsgResponseBody.class);}}
默认跟Ribbon是一样的,都是轮询策略,自定义配置使用的是@LoadBalancerClient,用法跟Ribbon的@RibbonClient一样。

在Springcloud alibaba的2.2.6版本对应的Spring-cloud版本是Hoxton.SR9,而spring-cloud-loadbalancer2.2.6版本负载均衡策略仅支持轮询策略。

Hoxton.SR10版本以后增加了随机负载均衡策略

切换随机负载均衡策略,新建两个Module分别绑定8004和8005端口,服务名是CLOUD-PAYMENT-SERVICE1,使用轮询策略,上面说了,MyLoadBalancerRandomConfig要放在@ComponentScan不能扫描到的package中。
@Configurationpublic class MyLoadBalancerRandomConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}}
启动类配置@LoadBalancerClient,配置原来的8001和8002,CLOUD-PAYMENT-SERVICE服务使用随机策略
@SpringBootApplication@EnableEurekaClient@LoadBalancerClient(value = "CLOUD-PAYMENT-SERVICE", configuration = MyLoadBalancerRandomConfig.class)public class Application6060 {public static void main(String[] args) {SpringApplication.run(Application6060.class,args);}}
controller类的修改,getPayment对应轮询,getPaymentWebClient对应随机
@RestController@RequestMapping("/consumer")public class ConsumerCtrol {private static final String PAYMENTURL = "https://CLOUD-PAYMENT-SERVICE";//随机private static final String PAYMENTURL2 = "https://CLOUD-PAYMENT-SERVICE1";//轮询@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate WebClient.Builder clientBuilder;@GetMapping("/getPayment/{id}")public MsgResponseBody getPaymet(@PathVariable("id") Long id) {System.out.println("adada");return restTemplate.getForObject(PAYMENTURL2 + "/payment/getPayment/" + id + "?userName=123", MsgResponseBody.class);}@GetMapping("/getPayment/webClient/{id}")public Mono<MsgResponseBody> getPaymetByWebClient(@PathVariable("id") Long id) {System.out.println("bbbbbbb");//return restTemplate.getForObject(PAYMENTURL + "/payment/getPayment/" + id + "?userName=123", MsgResponseBody.class);return clientBuilder.baseUrl(PAYMENTURL).build().get().uri("/payment/getPayment/" + id + "?userName=123").retrieve().bodyToMono(MsgResponseBody.class);}}
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/17528391.html

浙公网安备 33010602011771号