1 Ribbon
1.1 Ribbon概述
SpringCloudRibbon是基于NetflixRibbon实现的一套客户端负载均衡的工具
主要功能是提供客户端的软件负载均衡算法和服务调用,Ribbon会提供一系列完善的配置项如连接超时、重试等。简单的说就是在配合文件中列出Load Balancer后面所有的机器,Ribbon会自动帮你基于某种规则(简单轮询、随机连接等)去连接这些机器
LoadBalance负载均衡:
LB将用户的请求分摊到多个服务上,从而达到系统的高可用
常见的负载均衡由软件Nginx、LVS,硬件F5等Ribbon和Nginx的区别: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代替OpenFeign是SpringCloud在Feign的基础上支持了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调用细节: