SpringCloud-负载均衡(Ribbon)_服务调用_负载均衡(OpenFeign)
Ribbon
what
https://github.com/netflix/ribbon
SpringCloud Ribbon 是基于Netflix Ribbon实现的一套 客户端 负载均衡的工具;
主要功能
提供 客户端软件 负载均衡算法、服务调用;
Ribbon客户端组件 提供一系列的配置项(连接超时、重试等);
{简单来讲,就是在配置文件中列出负载均衡的所有机器,Ribbon会基于某种规则(简单轮询、随机连接等)去连接配置的机器}
Ribbon处于维护状态,替代方案
Ribbon负载均衡LoadBalance
what
将用户的请求 平均分配到 多个服务上,从而达到系统的高可用;
常见的负载均衡软件:Nginx、LVS、硬件F5等;
Ribbon负载均衡与Nginx负载均衡的差异
Nginx是服务器负载均衡,客户端所有的请求都会交给Nginx,由Nginx负责请求转发(服务端实现);
Ribbon是本地负载均衡,在调用服务时,在注册中心获取注册的服务列表缓存到本地,从而在本地实现RPC调用(客户端实现);
集中式LB
在 服务的提供方与消费方之间 使用独立的LB设施(可以是软件Nginx、LVS、硬件F5等),由设施 将请求根据某种规则转发至服务提供方;
进程内LB
将LB逻辑 集成到服务消费方,消费方从注册中心获取可用的服务,然后根据某种规则选择一个服务提供方进行调用;
Ribbon架构说明
Ribbon pom
org.springframework.cloud#spring-cloud-dependencies Hoxton.SR1版本的 org.springframework.cloud#spring-cloud-netflix-dependencies的 org.springframework.cloud#spring-cloud-starter-netflix-eureka-client 已经集成了org.springframework.cloud#spring-cloud-starter-netflix-ribbon
Ribbon核心组件IRule
what
根据特定算法 从服务列表中 选择一个要访问的服务;
com.netflix.loadbalancer.IRule // Interface that defines a "Rule" for a LoadBalancer. 负载均衡的规则 // A Rule can be thought of as a Strategy for loadbalacing. // Well known loadbalancing strategies include Round Robin, Response Time based etc. public interface IRule{ /* * choose one alive server from lb.allServers or * lb.upServers according to key * * @return choosen Server object. NULL is returned if none * server is available */ public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }
Ribbon预定义实现IRule
com.netflix.loadbalancer.RoundRobinRule
轮询
com.netflix.loadbalancer.RandomRule
随机
com.netflix.loadbalancer.RetryRule
先按照 轮询策略 获取服务,如果获取服务失败,则在指定时间内进行重试;
com.netflix.loadbalancer.WeightedResponseTimeRule
对 轮询策略 的扩展,响应越快的服务获得的权重越大,越容易被选择;
com.netflix.loadbalancer.BestAvailableRule
先过滤掉由于多次访问故障 而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
com.netflix.loadbalancer.AvailabilityFilteringRule
先过滤故障服务,再选择并发量较小的服务;
com.netflix.loadbalancer.ZoneAvoidanceRule
默认规则,复合判断 server所在区域的性能、server的可用性 来选择服务;
Ribbon自定义IRule
前言
自定义IRule不能被@ComponentScan扫描;
How
1、自定义IRule @Configuration public class MyIRule { @Bean public IRule iRule(){ return new RandomRule(); } } 2、启动类增加@RibbonClient指向自定义IRule @EnableEurekaClient @SpringBootApplication @RibbonClient(name = "payment-service", configuration = MyIRule.class) public class OrderStarter80 { public static void main(String[] args) { SpringApplication.run(OrderStarter80.class, args); } }
Ribbon负载均衡算法
轮询算法原理
rest接口第几次请求 % 服务总数 = 实际调用服务的下标;
轮询源码
public class RoundRobinRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; public RoundRobinRule() { nextServerCyclicCounter = new AtomicInteger(0); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; int count = 0; while (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { log.warn("No up servers available from load balancer: " + lb); return null; } int nextServerIndex = incrementAndGetModulo(serverCount); server = allServers.get(nextServerIndex); if (server == null) { /* Transient. */ Thread.yield(); continue; } if (server.isAlive() && (server.isReadyToServe())) { return (server); } // Next. server = null; } if (count >= 10) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } /** * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}. * * @param modulo The modulo to bound the value of the counter. * @return The next value. */ private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextServerCyclicCounter.get(); int next = (current + 1) % modulo; if (nextServerCyclicCounter.compareAndSet(current, next)) return next; } } }
OpenFeign
https://docs.spring.io/spring-cloud-openfeign/docs/3.1.3/reference/html/
https://github.com/spring-cloud/spring-cloud-openfeign
what
Feign is a declarative web service client.It makes writing web service clients easier.
声明式的web服务client,OpenFeign使得写web服务更容易;
To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations.
使用OpenFeign需要定义一个接口,然后添加注解;Feign注解支持Feign注解和JAX-RS注解;
Feign also supports pluggable encoders and decoders.
Feign支持可插拔的 编码器、解码器;
Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web.
SpringCloud 提供了SpringMVC注解支持,默认使用HttpMessageConverters;
Spring Cloud integrates Eureka, Spring Cloud CircuitBreaker, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
SpringCloud集成Eureka、CircuitBreaker、LoadBalancer 提供负载均衡;
已经有Ribbon,whyFeign?
Feign旨在让编写http client更简单;
使用Ribbon时,需要Ribbon+RestTemplate,利用RestTemplate对http请求做处理;
但实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多个地方调用,通常会自定义一些client包装这些依赖服务的调用;
所以,Feign在Ribbon基础上做了封装,有Feign来定义和实现依赖服务接口;
在Feign的实现下,只需要创建一个接口 然后注解接口,即可完成对服务提供方的接口绑定,简化了使用Ribbon时,自定义client的开发;
Feign与OpenFeign区别
工程
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 2、开启Feign支持 @EnableFeignClients // 开启Feign支持 @SpringBootApplication public class OpenFeignOrderStarter80 { public static void main(String[] args) { SpringApplication.run(OpenFeignOrderStarter80.class, args); } } 3、新增FeignClient @Component @FeignClient(value = "eureka-payment-service") public interface PaymentService { @GetMapping(value = "/get/{id}") CommonResult<Payment> getById(@PathVariable("id") Integer id); } 4、test @RestController public class OrderController { @Autowired private PaymentService paymentService; @GetMapping(value = "/order/getById/{id}") public CommonResult<Payment> getById(@PathVariable("id") Integer id){ return paymentService.getById(id); } }
OpenFeign超时
what
OpenFeign默认超时1s,如果服务提供方 超过1s,需要设置超时时间;
OpenFeign的超时实际由Ribbon实现(OpenFeign集成了Ribbon);
How
#设置Feign客户端超时 ribbon: ReadTimeout: 5000 #建立连接所用的时间 ConnectTimeout: 5000 #建立连接后,响应结束所用的时间
OpenFeign日志打印
what
OpenFeign提供了日志打印功能,可以通过调整日志级别 了解Feign的HTTP请求的细节;
日志级别
NONE:
默认的,不显示日志;
BASIC:
记录 请求方式、URL、响应状态码、执行时间;
HEADERS:
在BASIC的基础上,增加 请求头、响应头;
FULL:
在HEADERS的基础上,增加了 请求和响应的正文、元数据;
How
@Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } } #feign日志 logging: level: #feign日志以什么级别、作用在哪些接口 com.an.service.PaymentService: debug