SpringCloud负载均衡服务调用详解

2022-06-14 10:37:59

1 Ribbon

1.1 Ribbon概述

SpringCloudRibbon是基于NetflixRibbon实现的一套客户端负载均衡的工具
主要功能是提供客户端的软件负载均衡算法和服务调用,Ribbon会提供一系列完善的配置项如连接超时、重试等。简单的说就是在配合文件中列出Load Balancer后面所有的机器,Ribbon会自动帮你基于某种规则(简单轮询、随机连接等)去连接这些机器

LoadBalance负载均衡:
LB将用户的请求分摊到多个服务上,从而达到系统的高可用
常见的负载均衡由软件Nginx、LVS,硬件F5等RibbonNginx的区别:Nginx是服务器负载均衡(集中式LB)Ribbon是客户端负载均衡(进程内LB)Niginx会收到客户端的所有请求,这些请求由Nginx来转发,LB是在服务端实现Ribbon应用于客户端,当客户端调用微服务解救时,会在注册中心上获取注册表并缓存在本地JVM
从而根据某种策略选择某台机器在本地实现RPC远程服务调用

先前Consumer使用的spring-cloud-starter-netflix-eureka-client依赖中就已经包含了Ribbon
请添加图片描述

1.2 替换不同的负载策略

Ribbon的负载均衡功能起源于接口IRule
请添加图片描述
其实现:
在这里插入图片描述
这些具体实现就是客户端负载均衡策略的不同选择,默认使用轮询

RoundRobinRule: 轮询RandomRule: 随机RetryRule: 先按照轮询策略获取服务地址,如失败会在指定时间内重试WeightedResponseTimeRule: 对轮询的扩展,响应速度越快的实例选择权重越大,越容易被选择BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务AvailabilityFilteringRule: 先过滤掉故障实例,再选择并发较小的实例ZoneAvoidanceRule: 默认规则,复和判断server所在区域的性能和server的可用性来选择服务器

想要使用Ribbon其他的负载均衡策略,只需通过简单的配置即可:
1、新建package,新建配置类配置负载策略Bean
请添加图片描述
2、在主启动类中使用@RibbonClient注解声明将要访问的微服务名和使用的负载策略
请添加图片描述

1.3 轮询策略分析

轮询的原理很简单,通过discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”)
获得CLOUD-PAYMENT-SERVICE集群的所有机器地址信息(ip+port)
装在一个List中缓存在客户端本地JVM中

如此时集群中有两台机器,则:List[0] instanceA=127.0.0.1:8001List[1] instanceB=127.0.0.1:8002

轮询策略选择的机器= 第n次请求数% 集群中机器总数= 数组中实例下标
如请求为1时 instance=List.get(1%2)= instanceB
此时选择了机器127.0.0.1:8002

回到源码,从IRule开始,choose方法就是从集群中选择机器的策略
请添加图片描述
找到IRule的一个实现类RoundRobinRule,即轮询策略:

publicclassRoundRobinRuleextendsAbstractLoadBalancerRule{privateAtomicInteger nextServerCyclicCounter;privatestaticfinalboolean AVAILABLE_ONLY_SERVERS=true;privatestaticfinalboolean ALL_SERVERS=false;privatestaticLogger log=LoggerFactory.getLogger(RoundRobinRule.class);publicRoundRobinRule(){this.nextServerCyclicCounter=newAtomicInteger(0);}publicRoundRobinRule(ILoadBalancer lb){this();this.setLoadBalancer(lb);}publicServerchoose(ILoadBalancer lb,Object key){if(lb==null){
            log.warn("no load balancer");returnnull;}else{Server server=null;int count=0;while(true){if(server==null&& count++<10){// 获取健康的服务器信息listList<Server> reachableServers= lb.getReachableServers();// 获取所有的服务器listList<Server> allServers= lb.getAllServers();int upCount= reachableServers.size();int serverCount= allServers.size();// 集群中的可用服务器个数和全部服务器个数不为0才能继续选择if(upCount!=0&& serverCount!=0){// 根据当前请求数%集群中总机器数获取下标int nextServerIndex=this.incrementAndGetModulo(serverCount);// 根据下标从所有服务器list中获取服务器地址实例
                        server=(Server)allServers.get(nextServerIndex);// 该地址不可用 之后继续重试if(server==null){Thread.yield();}else{// 该地址可用 直接返回并使用if(server.isAlive()&& server.isReadyToServe()){return server;}

                            server=null;}continue;}

                    log.warn("No up servers available from load balancer: "+ lb);returnnull;}// 重试10次后仍然失败 返回nullif(count>=10){
                    log.warn("No available alive servers after 10 tries from load balancer: "+ lb);}return server;}}}privateintincrementAndGetModulo(int modulo){int current;int next;do{// 获得当前请求次数 这个次数是AtomicInteger
            current=this.nextServerCyclicCounter.get();
            next=(current+1)% modulo;// 通过CAS来保证访问次数的原子性 保证多线程调用同一微服务时也依次轮询}while(!this.nextServerCyclicCounter.compareAndSet(current, next));return next;}publicServerchoose(Object key){returnthis.choose(this.getLoadBalancer(), key);}publicvoidinitWithNiwsConfig(IClientConfig clientConfig){}}

2 OpenFeign

2.1 OpenFeign简介

先前做服务调用时使用RestTemplate+Ribbon,RestTemplate需要指明调用的url
但在实际开发中,Consumer会调用多个Provider来完成业务,直接用RestTemplate显得复杂
为此在Feign的支持下,只需要创建一个接口并使用注解来配置,就可以绑定Provider

Feign已停止维护,目前可以使用OpenFeign代替OpenFeignSpringCloudFeign的基础上支持了SpringMVC的注解
如@RequestMapping等等OpenFeign@FeignClient可以解析SpringMVC@RequestMapping下的接口
并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务

2.2 OpenFeign的使用

OpenFigen用于Consumer侧,通过微服务调用接口+@FeignClient调用Provider提供的Rest接口

新建子模块以Feign客户端形式调用支付服务
1、创建模块Cloud-Consumerfeign-Order80并引入依赖:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

2、创建yaml文件并配置

server:port:80spring:application:name: cloud-order-serviceeureka:instance:instance-id: order80prefer-ip-address:trueclient:register-with-eureka:truefetch-registry:trueservice-url:defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

3、完成主启动类,在主启动类上使用@EnableFeignClients注解
请添加图片描述
4、创建目标Provider的微服务接口并使用@FeignClient配置
Provider提供的Service:
请添加图片描述
在Consumer中创建对应的Feign接口,并使用@FeignClient注明Provider的名称
请添加图片描述
在Controller层调用Feign接口中的方法:
请添加图片描述
调用成功:
请添加图片描述
OpenFeign整合了RestTemplate+Ribbon,在Consumer侧编写对应的微服务接口并配置
就可以通过Rest方式访问到Provider的Controller层中的方法

2.3 OpenFeign补充

2.3.1 超时控制

当Consumer调用Provider的服务时,存在Provider业务复杂时可能导致超时的现象
OpenFeign客户端默认等待1s,超过1s后就会报TimeOut异常
为此可以在yaml中配置超时时间,这个超时时间由Ribbon决定

ribbon:# 建立连接所用的时间ReadTimeout:5000# 建立连接后从服务端读取可用资源所用时间ConnectTimeout:5000

2.3.2 日志打印

OpenFeign提供了日志打印功能,可以通过配置来调整日志级别
从而了解OpenFeign中Http请求的细节,即对OpenFeign接口的调用情况进行监控和输出

日志级别:

NONE: 默认,不显示任何日志

BASIC: 仅记录请求方法、URL、响应状态码及执行时间

HEADERS: 除了BASIC中定义的信息外,还有请求和响应的头信息

FULL: 除了HEADERS中定义的信息外,还有请求和响应的正文及元数据

开启日志功能需要配置相关的Bean:

@ConfigurationpublicclassFeignConfig{@BeanpublicLogger.LevelfeignLoggerLevel(){returnLogger.Level.FULL;}}

在yaml中配置日志监控级别和接口:

logging:level:# 配置监控的接口 及监控级别com.coisini.springcloud.service.PaymentFeignService: debug

通过Feign提供的日志系统可以看到详细的Http调用细节:
请添加图片描述

  • 作者:雫#1999
  • 原文链接:https://blog.csdn.net/weixin_43541094/article/details/124169234
    更新时间:2022-06-14 10:37:59