Ribbon使用及其客户端负载均衡实现原理分析
1、ribbon负载均衡测试
(1)consumer工程添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> </dependency>
说明:
①由于spring-cloud-starter-eureka已经依赖了spring-cloud-starter-ribbon,所以不用再添加spring-cloud-starter-ribbon依赖了
②Spring cloud 引入ribbon 配合restTemplate 实现客户端负载均衡、此处需要引入Okhttp依赖 (也可以使用ApacheClient等其他远程调用技术)
(2)配置ribbon参数
#配置ribbon ribbon: MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是连接不上时将会重试 MaxAutoRetriesNextServer: 3 #切换实例的重试次数、高可用场景 OkToRetryOnAllOperation: false #对所有操作请求都进行重试,如果是get则可以,如果是post、put有重复提交的危险,建议设置为false ConnectTimeout: 5000 #请求连接的超时时间 ReadTimeout: 6000 #请求处理的超时时间
(3)定义RestTemplate
@Bean @LoadBalanced //使用该注解表示实现客户端负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); }
(4)启动两各Producer工程,注意端口不同,注册到Eureka中
(5)测试代码
@SpringBootTest @RunWith(SpringRunner.class) public class TestRibbon { @Autowired private RestTemplate restTemplate; @Test public void testRibbon(){ String serviceId = "Producer"; for(int i=0;i<10;i++){ //通过服务id调用 ResponseEntity<User> user= restTemplate.getForEntity("http://"+serviceId+"/user/get/5a754adf6abb500ad05688d9", User.class); System.out.println(JSONObject.toJSONString(user)); } } }
2、客户端负载均衡实现原理
(1)区别服务端负载均衡和客户端负载均衡
向Nginx 、F5 等在请求发出之后,被负载均衡服务器拦截再分发到具体服务的方式是服务端负载均衡,而Ribbon是客户端先从Eureka Server获取服务列表,自己维护服务列表,根据负载均衡算法直接请求资源服务器的方式叫服务端负载均衡
(2)Ribbon 实现客户端负载均衡细节
在定义RestTempalte时加@LoadBalanced注解后、restTemplate会走LoadbanlanceInterceptor拦截器
LoadbanlanceInterceptor.class
@Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); //调用RibbonLoadBalancerClient return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); }
RibbonLoadBalancerClient.class
@Override public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { //获取服务列表 ILoadBalancer loadBalancer = getLoadBalancer(serviceId); //根据负载均衡算法从服务列表中获取本次调用服务的地址 Server server = getServer(loadBalancer); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); return execute(serviceId, ribbonServer, request); }