实现微服务负载均衡(Ribbon )
实现负载均衡(Ribbon )
1、什么是负载均衡?
通俗讲,负载均衡就是将负载(⼯作任务,访问请求)进⾏分摊到多个操作单元(服务器,组件)上进⾏执⾏。根据负载均衡发⽣位置的不同,⼀般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发⽣在服务提供者⼀⽅,⽐如:常见的nginx负载均衡;⽽客户端负载均衡指的是发⽣在服务请求的⼀⽅,也就是在
发送请求之前已经选好了由哪个实例处理请求。
我们在微服务调⽤关系中⼀般会选择客户端负载均衡,也就是在服务调⽤的⼀⽅来决定服务由哪个提供者执⾏。
2、⾃定义实现负载均衡
第1步:启动多个相同的微服务。
第2步:通过Nacos查看微服务的注册启动情况。
第3步:修改服务调⽤代码,实现负载均衡第4步:启动多个服务提供者和⼀个服务消费者,多访问⼏次消费者测试其结果。
3、基于Ribbon 实现负载均衡
3.1、Ribbon 是什么?
Ribbon是Spring Cloud的⼀个组件,它可以让我们使⽤⼀个注解就能轻松的实现负载均衡; 是 Netflix 
公司的⼀个开源的负载均衡 项⽬;是⼀个基于HTTP 和 TCP的客户端/进程内负载均衡器,**运⾏在消费者端**。
3.2、Ribbon 的使⽤
第1步:在RestTemplate的⽣成⽅法上添加@LoadBalanced注解
@RestControllerpublic  class  OrderController {        @Autowried    private  RestTemplate restTemplate ;        @Autowired    private  DiscoveryClient discoveryClient ;            @GetMapping("/order/prod/{pid}")
public  Order order (@PathVariable("pid")Integer pid ){
//从nacos 中获取服务地址        //⾃定义规则实现随机挑选服务
List <ServiceInstance > instances = discoveryClient .getInstances ("service-provider");                int  index = new  Random ().nextInt (instances .size ());                ServiceInstance serviceInstance = instances .get (index );
String url = servieInstance .getHost () + ":" + serviceInstance .getPort ();
Product product = restTemplate .getForObject (""+url +"/product"+pid , Product .class );            }}
1234567891011
第2步:修改服务调⽤的⽅法
3.3、Ribbon ⽀持的负载均衡策略
Ribbon内置了多种负载均衡策略,内部负载均衡的顶级接⼝为comflix.loadbalancer.IRule,具体的负载策略如下:
策略名
策略描述
实现说明
BestAvailableRule 选择⼀个最⼩的并发请求的server
逐个考察Server,如果Server被tripped了,则忽略,再选择其中
ActiveRequestCount最⼩的server。
AvailabilityFilteingRule
nextint()方法过滤掉哪些因为⼀直连接失败的被标记为
circuit tripped的后端server,并过滤掉
那些⾼并发的后端server(active connections超过配置的阈值)
使⽤⼀个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查
status⾥纪录的各个server的运⾏状态
WeightedResponseTimeRule
根据响应时间分配⼀个weight,响应时间
越长,weight越⼩,被选中的可能性越
低。
⼀个后台线程定期的从status⾥⾯读取评价响应时间,为每个server计算⼀个weight。weight的计算也⽐较简单repsponse time 减去每个server⾃⼰平均的response time是server的权重。当开始运⾏,没有形成status
时,使⽤roubine策略选择server。
RetryRule
对选定的负载均衡策略server上执⾏重试
机制。
在⼀个配置时间段内,当选择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。
可以通过修改配置来调整Ribbon的负载均衡策略,具体代码如下:
4、基于LoadBalancer 实现负载均衡
4.1、LoadBalancer 是什么?
Spring Cloud原有的客户端负载均衡⽅案Ribbon已经被废弃(因为Netflix公司将Ribbon闭源),因此,Spring Cloud定义了LoadBalancer来替换Ribbon。
4.2、LoadBalancer 的使⽤
第1步:添加LoadBalancer依赖
@Bean
@LoadBalanced
public  RestTemplate restTemplate (){        return  new  RestTemplate ();}
12345
@RestControllerpublic  class  OrderController {        @Autowired
private  RestTemplate restTemplate ;          @GetMapping("/order/prod/{pid}")
public  Order order (@PathVariable("pid")Integer pid ){                String url = "service-product";
Product product = restTeplate .getForObject (""+url +"/product/"+pid ,Product .class );        }}
123456789
service-provider : #调⽤的服务提供者的服务名称 ribbon:  NFLoadBalancerRuleClassName: comflix.loadbalancer.RandomRule
1
第2步:在RestTemplate的⽣成⽅法上添加@LoadBalanced注解
或者 使⽤LoadBalancerClient的客户端API 如果项⽬中引⼊了Ribbon,Spring Cloud默认优先使⽤Ribbon,因此使⽤Spring Cloud Load Balancer需要将Ribbon依赖排除掉或者关闭Ribbon,可以使⽤以下配置关闭Ribbon功能。
关闭ribbon之后,Spring Cloud LoadBalancer就会加载成默认的负载均衡器(BlockingLoadBalancerClient), 默认负载均衡器是轮询选择。
4.3、⾃定义负载均衡策略
可以在启动类上⾯使⽤@LoadBalancerClient注解,或者@LoadBalancerClients注解,指定服务级别的负载均衡策略。
⾃定义灰度负载均衡策略⽰例:
<dependency >
<groupId >org.springframework.cloud </groupId >        <artifactId >spring-cloud-starter-loadbalancer </artifactId >    <version >2.2.1.RELEASE </version ></dependency >
12345
@Bean
@LoadBalanced
public  RestTemplate restTemplate (){        return  new  RestTemplate ();}
12345
//在服务消费者的业务逻辑代码中注⼊LoadBalancerClient 的实例@Autowired
LoadBalancerClient lbc ;@Autowired RestTemplate rt ;
public  List <String > getOders (){
ServiceInstance si = lbc .choose ("order-service");    rt .getForObject (si .getUri () + "/order/list", List .class );}
1234567891011
spring :  cloud :
loadbalancer :      ribbon :        enabled : false
12345
@LoadBalancerClient(value = "demo-provider", configuration = RandomLoadbalancerConfig .class )public  class  RandomLoadbalancerConfig {    @Bean
public  ReactorLoadBalancer <ServiceInstance > reactorServiceInstanceLoadBalancer (Environment environment ,  LoadBalancerClientFactory loadBalanc
String name = environment .getProperty (LoadBalancerClientFactory .PROPERTY_NAME );          return  new  RandomLoadBalancer (
loadBalancerClientFactory .getLazyProvider (name , ServiceInstanceListSupplier .class ), name );    }}
123456789
@Slf4j
public  class  GrayRoundRobinLoadBalancer extends  RoundRobinLoadBalancer {
private  ObjectProvider <ServiceInstanceListSupplier > serviceInstanceListSupplierProvider ;    private  String serviceId ;    @Override
public  Mono <Response <ServiceInstance >> choose (Request request ) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider    .getIfAvailable (NoopServiceInstanceListSupplier ::new );          return  supplier .get (request ).next ().map (serviceInstances -> getInstanceResponse (serviceInstances , request ));    }
Response <ServiceInstance > getInstanceResponse (List <ServiceInstance > instances , Request request ) {  // 注册中⼼⽆可⽤实例 抛出异常          if  (CollUtil .isEmpty (instances )) {
log .warn ("No instance available {}", serviceId );              return  new  EmptyResponse ();          }
DefaultRequestContext requestContext = (DefaultRequestContext ) request .getContext ();        RequestData clientRequest = (RequestData ) requestContext .getClientRequest ();          HttpHeaders headers = clientRequest .getHeaders ();
String reqVersion = headers .getFirst (CommonConstants .VERSION );          if  (StrUtil .isBlank (reqVersion )) {              return  super .choose (request ).block ();          }  // 遍历可以实例元数据,若匹配则返回此实例          for  (ServiceInstance instance : instances ) {
NacosServiceInstance nacosInstance = (NacosServiceInstance ) instance ;              Map <String , String > metadata = nacosInstance .getMetadata ();
String targetVersion = MapUtil .getStr (metadata , CommonConstants .VERSION );              if  (reqVersion .equalsIgnoreCase (targetVersion )) {
log .debug ("gray requst match success :{} {}", reqVersion , nacosInstance );                        return  ne
w  DefaultResponse (nacosInstance );              }
}  // 降级策略,使⽤轮询策略          return  super .choose (request ).block ();    }}
123456789101112131415161718192021222324252627282930313233

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。