• Ribbon负载均衡原理学习记录


    ribbon项目通过RestTemplate发起微服务请求,但是知道的都是知道RestTemplate是spring自带的 那么和ribbon有什么关系呢?

     
    标识.png

    我们找到了@LoadBalanced标注的负载均衡标识
    点进去

     
    loadbalanced.png

    @Retention(RetentionPolicy.RUNTIME)
    ps:这里标识指的是 当编译完成后 运行保留 在jvm中运行 可以被反射调用
    注意上面的绿色注释LoadBalancerClient来配置它
    打开LoadBalancerClient类
    里面有三个方法

     
    image.png

    同时继承了ServiceInstanceChooser 类
    字面意思 服务选择实例

    public interface LoadBalancerClient extends ServiceInstanceChooser {
    
        /**
         * execute request using a ServiceInstance from the LoadBalancer for the specified
         * service
         * @param serviceId the service id to look up the LoadBalancer
         * @param request allows implementations to execute pre and post actions such as
         * incrementing metrics
         * @return the result of the LoadBalancerRequest callback on the selected
         * ServiceInstance
         */
        <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
    
        /**
         * execute request using a ServiceInstance from the LoadBalancer for the specified
         * service
         * @param serviceId the service id to look up the LoadBalancer
         * @param serviceInstance the service to execute the request to
         * @param request allows implementations to execute pre and post actions such as
         * incrementing metrics
         * @return the result of the LoadBalancerRequest callback on the selected
         * ServiceInstance
         */
        <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
    
        /**
         * Create a proper URI with a real host and port for systems to utilize.
         * Some systems use a URI with the logical serivce name as the host,
         * such as http://myservice/path/to/service.  This will replace the
         * service name with the host:port from the ServiceInstance.
         * @param instance
         * @param original a URI with the host as a logical service name
         * @return a reconstructed URI
         */
        URI reconstructURI(ServiceInstance instance, URI original);
    }
    
    public interface ServiceInstanceChooser {
    
        /**
         * Choose a ServiceInstance from the LoadBalancer for the specified service
         * @param serviceId the service id to look up the LoadBalancer
         * @return a ServiceInstance that matches the serviceId
         */
        ServiceInstance choose(String serviceId);
    }
    

    ServiceInstance choose(String serviceId); 根据serviceId即服务ID查询服务

    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; 根据服务来执行请求内容

    URI reconstructURI(ServiceInstance instance, URI original);
    拼接请求方式 传统中是ip:port 现在是服务名称:port 形式

    定位当前类位置 发现相关类

     
    image.png

    当然太多了 就不仔细说了 有空自己去看
    我们关注自动装配这个类 有关自动装配相关知识 后面再仔细说

    /**
     * Auto configuration for Ribbon (client side load balancing).
     *
     * @author Spencer Gibb
     * @author Dave Syer
     * @author Will Tran
     */
    @Configuration
    @ConditionalOnClass(RestTemplate.class)
    @ConditionalOnBean(LoadBalancerClient.class)
    @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
    public class LoadBalancerAutoConfiguration {
    
        @LoadBalanced
        @Autowired(required = false)
        private List<RestTemplate> restTemplates = Collections.emptyList();
    
        @Bean
        public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
                final List<RestTemplateCustomizer> customizers) {
            return new SmartInitializingSingleton() {
                @Override
                public void afterSingletonsInstantiated() {
                    for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                        for (RestTemplateCustomizer customizer : customizers) {
                            customizer.customize(restTemplate);
                        }
                    }
                }
            };
        }
    
        @Autowired(required = false)
        private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
    
        @Bean
        @ConditionalOnMissingBean
        public LoadBalancerRequestFactory loadBalancerRequestFactory(
                LoadBalancerClient loadBalancerClient) {
            return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
        }
    
        @Configuration
        @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
        static class LoadBalancerInterceptorConfig {
            @Bean
            public LoadBalancerInterceptor ribbonInterceptor(
                    LoadBalancerClient loadBalancerClient,
                    LoadBalancerRequestFactory requestFactory) {
                return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
            }
    
            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final LoadBalancerInterceptor loadBalancerInterceptor) {
                return new RestTemplateCustomizer() {
                    @Override
                    public void customize(RestTemplate restTemplate) {
                        List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                                restTemplate.getInterceptors());
                        list.add(loadBalancerInterceptor);
                        restTemplate.setInterceptors(list);
                    }
                };
            }
        }
    
        @Configuration
        @ConditionalOnClass(RetryTemplate.class)
        public static class RetryAutoConfiguration {
            @Bean
            public RetryTemplate retryTemplate() {
                RetryTemplate template =  new RetryTemplate();
                template.setThrowLastExceptionOnExhausted(true);
                return template;
            }
    
            @Bean
            @ConditionalOnMissingBean
            public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
                return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
            }
    
            @Bean
            @ConditionalOnMissingBean
            public LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory() {
                return new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
            }
        }
    
        @Configuration
        @ConditionalOnClass(RetryTemplate.class)
        public static class RetryInterceptorAutoConfiguration {
            @Bean
            @ConditionalOnMissingBean
            public RetryLoadBalancerInterceptor ribbonInterceptor(
                    LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
                    LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
                    LoadBalancerRequestFactory requestFactory,
                    LoadBalancedBackOffPolicyFactory backOffPolicyFactory) {
                return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
                        lbRetryPolicyFactory, requestFactory, backOffPolicyFactory);
            }
    
            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
                return new RestTemplateCustomizer() {
                    @Override
                    public void customize(RestTemplate restTemplate) {
                        List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                                restTemplate.getInterceptors());
                        list.add(loadBalancerInterceptor);
                        restTemplate.setInterceptors(list);
                    }
                };
            }
        }
    }
    

    ps:@ConditionalOnMissingClass当缺少指定的value类时 创建某类
    @ConditionalOnClass(RestTemplate.class) RestTemplate类必须存在当前工作环境中
    @ConditionalOnBean(LoadBalancerClient.class) 在spring的Bean工程中必须有LoadBalancerClient的实现Bean;
    该配置类中 主要做了三件事:
    1.创建了一个LoadBalancerInterceptor 负载均衡拦截器 用于对客户端发起的请求进行拦截,以实现客户端负载均衡.
    2.创建一个RestTemplateCustomizer的Bean,用于给RestTemplate增加
    LoadBalancerInterceptor拦截器。
    3.维护被@LoadBananced注解修饰的RestTemplate对象列表,并在初始化,通过调用RestTemplateCustomizer实例来给需要的客户端负载均衡RestTemplate增加拦截器LoadBalancerInterceptor 拦截器

    看LoadBalancerInterceptor拦截器是如何将一个普通RestTemplate实现负载均衡的

    **
     * @author Spencer Gibb
     * @author Dave Syer
     * @author Ryan Baxter
     * @author William Tran
     */
    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    
        private LoadBalancerClient loadBalancer;
        private LoadBalancerRequestFactory requestFactory;
    
        public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
            this.loadBalancer = loadBalancer;
            this.requestFactory = requestFactory;
        }
    
        public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
            // for backwards compatibility
            this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
        }
    
        @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);
            return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
        }
    }
    
    public interface ClientHttpRequestInterceptor {
    
        /**
         * Intercept the given request, and return a response. The given {@link ClientHttpRequestExecution} allows
         * the interceptor to pass on the request and response to the next entity in the chain.
         *
         * <p>A typical implementation of this method would follow the following pattern:
         * <ol>
         * <li>Examine the {@linkplain HttpRequest request} and body</li>
         * <li>Optionally {@linkplain org.springframework.http.client.support.HttpRequestWrapper wrap} the request to filter HTTP attributes.</li>
         * <li>Optionally modify the body of the request.</li>
         * <li><strong>Either</strong>
         * <ul>
         * <li>execute the request using {@link ClientHttpRequestExecution#execute(org.springframework.http.HttpRequest, byte[])},</li>
         * <strong>or</strong>
         * <li>do not execute the request to block the execution altogether.</li>
         * </ul>
         * <li>Optionally wrap the response to filter HTTP attributes.</li>
         * </ol>
         *
         * @param request the request, containing method, URI, and headers
         * @param body the body of the request
         * @param execution the request execution
         * @return the response
         * @throws IOException in case of I/O errors
         */
        ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
                throws IOException;
    }
    

    可以看到LoadBalancerInterceptor 实现了重写ClientHttpRequestInterceptor Http拦截的请求
    可以看到拦截器中注入了LoadBalancerClient的实现.当一个被@LoadBalanced 注解修饰的RestTemplate对象向外发起Http请求时用intercept方法 截取出 由于我们host用的是RPC框架 originalUri.getHost();获取的是服务名 然后在调用之前的LoadBalancerClient execute去执行请求
    继续往下 看LoadBalancerClient的execute实现

    @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);
        }
    
    @Override
        public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
            Server server = null;
            if(serviceInstance instanceof RibbonServer) {
                server = ((RibbonServer)serviceInstance).getServer();
            }
            if (server == null) {
                throw new IllegalStateException("No instances available for " + serviceId);
            }
    
            RibbonLoadBalancerContext context = this.clientFactory
                    .getLoadBalancerContext(serviceId);
            RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
    
            try {
                T returnVal = request.apply(serviceInstance);
                statsRecorder.recordStats(returnVal);
                return returnVal;
            }
            // catch IOException and rethrow so RestTemplate behaves correctly
            catch (IOException ex) {
                statsRecorder.recordStats(ex);
                throw ex;
            }
            catch (Exception ex) {
                statsRecorder.recordStats(ex);
                ReflectionUtils.rethrowRuntimeException(ex);
            }
            return null;
        }
    

    可以看到通过
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    Server server = getServer(loadBalancer);
    获取服务实例

    
        protected Server getServer(ILoadBalancer loadBalancer) {
            if (loadBalancer == null) {
                return null;
            }
            return loadBalancer.chooseServer("default"); // TODO: better handling of key
        }
    
     
    image.png

    并未使用之前的LoadBalancerClient类的choose来获取
    而是使用netflix ribbon来获取

    public interface ILoadBalancer {
        void addServers(List<Server> var1);
    
        Server chooseServer(Object var1);
    
        void markServerDown(Server var1);
    
        /** @deprecated */
        @Deprecated
        List<Server> getServerList(boolean var1);
    
        List<Server> getReachableServers();
    
        List<Server> getAllServers();
    }
    

    主要是这几个功能
    addServers添加服务
    chooseServer 选择服务
    markServerDown停止服务
    getReachableServers 获得正常能访问的服务
    getAllServers 获取所有服务 包括dang机的

     
    image.png


    ILoadBalancer 的实现扩展以上 而springCloud扩展默认采用ZoneAwareLoadBalancer

     
    image.png

    2019年4月8日23:59:53 明天上班 休息了

  • 相关阅读:
    Linux(Unix)时钟同步ntpd服务配置方法(转载)
    Linux SSH Publickey登录!
    三个最短路算法
    三个最短路算法
    最大子列和问题
    哈密尔顿环
    最小生成树应用解(超时)蓝桥杯2015初赛]灾后重建
    最小生成树应用解(超时)蓝桥杯2015初赛]灾后重建
    c++11的记录
    最大子列和问题
  • 原文地址:https://www.cnblogs.com/liaohongbin/p/10726176.html
Copyright © 2020-2023  润新知