springcloud Ribbon详解

发布时间:2024-01-10 16:00

微服务间的调用,网关请求转发,feign都是通过ribbon实现的,因此学习ribbon的原理还是很重要的,而ribbon的作用是用于负载均衡,springcloud自动化整合配置ribbon是RibbonEurekaAutoConfiguration这个类。对于开发者来说,使用ribbon只需要在RestTemplate上添加@LoadBalanced注解即可实现消费方的负载均衡

RestTemplate

resttemplate用于不同服务间的通信和访问,主要有发送GET,POST,PUT,DELETE(*ForEntity方法访问另一个服务的提供的接口返回需要的结果)

开头我们提到在RestTemplate上添加@LoadBalancer注解即可实现负载均衡,通过源码我们可以知道实现的原理。

ribbon源码

1.原理

我们可以找到LoadBalancerClient接口

public interface LoadBalancerClient extends ServiceInstanceChooser {
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

    URI reconstructURI(ServiceInstance instance, URI original);
}

该接口为客户端负载均衡的接口,我们可以找到客户端的自动配置类LoadBalancerAutoConfiguration类


@ConditionalOnClass({RestTemplate.class}) //实现自动化配置 resttemplate类需存在于当前工程中
@ConditionalOnBean({LoadBalancerClient.class}) //实现自动化配置 LoadBalancerClient类的实现bean 需存在于当前工程中
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
	    @LoadBalanced
	    @Autowired(
	        required = false
	    )
	    private List<RestTemplate> restTemplates = Collections.emptyList();//创建空的集合存放RestTemplate进行初始化
	
		@Bean
	    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
	        return () -> {
	            restTemplateCustomizers.ifAvailable((customizers) -> {
	                Iterator var2 = this.restTemplates.iterator();
	
	                while(var2.hasNext()) {
	                    RestTemplate restTemplate = (RestTemplate)var2.next();
	                    Iterator var4 = customizers.iterator();
	
	                    while(var4.hasNext()) {
	                        RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
	                        customizer.customize(restTemplate); //消费RestTemplate,具体消费方法在下面
	                    }
	                }
	
	            });
	        };
	    }
	    
	    @ConditionalOnClass({RetryTemplate.class}) //假如工程中使用的是RetryTemplate的实现bean则使用该消费方法
	    public static class RetryInterceptorAutoConfiguration {
	        public RetryInterceptorAutoConfiguration() {
	        }
	
	        @Bean //注入RetryLoadBalancerInterceptor bean 用于消费(对请求进行拦截)
	        @ConditionalOnMissingBean
	        public RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory) {
	            return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, requestFactory, loadBalancedRetryFactory);
	        }
	
	        @Bean //具体消费方法
	        @ConditionalOnMissingBean
	        public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
	            return (restTemplate) -> {
	                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
	                list.add(loadBalancerInterceptor);
	                restTemplate.setInterceptors(list); //给restTemplate添加拦截器
	            };
	        }
	    }
	    
	    ...
    }

可以看出自动化配置类实现的原理是给被@LoadBalancer修饰的resttemplate对象发起请求时,对其进行拦截,负载均衡的是通过拦截器实现的,接下来我们看拦截器的具体实现:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer; //构造方法中传入了LoadBalancerClient负载均衡客户端接口
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        //根据服务名和端口获取到具体的url,调用客户端的execute方法进行拦截实现负载均衡 注意到 this.loadBalancer.execute(执行请求) 和this.requestFactory.createRequest(组装请求)方法 
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); 
    }
}
  • this.loadBalancer.execute(执行请求)

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号