美文网首页
SpringCloud解析三:Ribbon源码分析(下)

SpringCloud解析三:Ribbon源码分析(下)

作者: 一根线条 | 来源:发表于2019-12-20 11:39 被阅读0次

    一:简要回顾

    先来回顾下上个章节讲到的sping cloud提供的负载均衡逻辑。spring-cloud-commons中对负载均衡做了几个抽象,

    1,LoadBalancerInterceptor类

    负载均衡拦截器,用于添加到RestTemplate的拦截器链中从而拦截请求。其intercept方法会执行LoadBalancerClient的execute方法。

    对于一般的拦截器来说,在拦截器的intercept方法中经过处理后就直接调用ClientHttpRequestExecution的execute方法迭代执行完所有的拦截器,并将最后个拦截器的执行结果返回。而在这里却会先将其执行代码封装到LoadBalancerRequest类型的对象中,等到确认了要访问的服务实例后再执行【在LoadBalancerClient中】。

    2,LoadBalancerClient接口

    定义了负载均衡客户端所需要实现的方法。继承自ServiceInstanceChooser接口实现从负载均衡器中筛选出服务实例的功能;同时定义了一个供 LoadBalancerInterceptor 调用的execute方法,该方法接收服务id(serviceId)与LoadBalancerRequest类型实例作为参数,返回ClientHttpResponse 类型的响应结果。

    3,LoadBalancerRequest接口

    定义LoadBalancerClient接口中execute方法执行的参数类型,该接口中只有一个接收ServiceInstance参数的apply方法。该接口的目的是封装拦截器的执行逻辑,等到后面确认服务实例后再调用。 该类型的实例由LoadBalancerRequestFactory创建返回。

    4,LoadBalancerRequestFactory类

    只是一个普通的工具类工厂,目的是创建并返回LoadBalancerRequest类型实例对象。默认的实现是在createRequest方法中将ServiceInstance、HttpRequest 、LoadBalancerClient 封装到ServiceRequestWrapper包装器对象中,最后将包装器对象作为参数继续调用ClientHttpRequestExecution 的execute方法从而继续调用其它的拦截器对象直到请求完毕并返回。

    ServiceRequestWrapper主要是重写了getURI方法使用LoadBalancerClient的reconstructURI方法得到真正的服务实例访问地址。

    整个的过程大致是

    1. LoadBalancerInterceptor拦截请求执行intercept方法
    2. 使用intercept方法中调用LoadBalancerRequestFactory来创建封装了拦截器处理逻辑的LoadBalancerRequest类型实例对象
    3. 使用serviceId和LoadBalancerRequest作为参数调用LoadBalancerClient实例的execute方法,在得到访问的服务实例后执行LoadBalancerRequest的apply方法并返回

    二,SpringCloud Ribbon源码分析

    针对spring-cloud-netflix-ribbon-2.2.0.RELEASE的源码

    我们继续分析Ribbon是怎么让RestTemplate达到负载均衡的目的的。
    在其/META-INF/spring.factories文件中对启动类进行了如下的配置

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
    

    由上个章节所知,只要在IOC中具备LoadBalancerClient的实现类即可让RestTemplate实现负载均衡,Ribbon也正是这么做的,先看RibbonAutoConfiguration的里面都做了些啥

    @Configuration
    @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
    //对Ribbon客户端进行配置
    @RibbonClients
    //在EurekaClientAutoConfiguration配置类执行后执行
    @AutoConfigureAfter(
            name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
    //在LoadBalancerAutoConfiguration与AsyncLoadBalancerAutoConfiguration前执行
    @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
            AsyncLoadBalancerAutoConfiguration.class })
    //读取配置数据
    @EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
            ServerIntrospectorProperties.class })
    public class RibbonAutoConfiguration {
        
        //收集IOC中所有的RibbonClient的配置信息
        //【通过上面的@RibbonClients注解生成的RibbonClientSpecification对象】
        @Autowired(required = false)
        private List<RibbonClientSpecification> configurations = new ArrayList<>();
    
        //读取“ribbon.eager-load.enabled”以及“ribbon.eager-load.clients”的配置
        //用于判断是否启动“饥饿加载”【应用启动后立即创建指定的Ribbon客户端】
        @Autowired
        private RibbonEagerLoadProperties ribbonEagerLoadProperties;
    
        @Bean
        public HasFeatures ribbonFeature() {
            return HasFeatures.namedFeature("Ribbon", Ribbon.class);
        }
    
        //创建Ribbon使用的SpringClientFactory对象,该对象会读取Ribbon客户端的配置信息,
        //并为每个客户端创建一个AnnotationConfigApplicationContext上下文【首次调用某个客户端的时候才创建,
        //可以使用“饥饿加载”让指定的服务一开始就创建】。
        //内部使用org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
        //作为默认的配置配置项
        @Bean
        public SpringClientFactory springClientFactory() {
            SpringClientFactory factory = new SpringClientFactory();
            factory.setConfigurations(this.configurations);
            return factory;
        }
    
        //创建LoadBalancerClient 类型的RibbonLoadBalancerClient对象【这里是最关键的地方】,
        //当这个实例存在后,就可以使用RestTemplate进行客户端负载均衡了
        @Bean
        @ConditionalOnMissingBean(LoadBalancerClient.class)
        public LoadBalancerClient loadBalancerClient() {
            return new RibbonLoadBalancerClient(springClientFactory());
        }
    
        //创建可以进行“重试”的LoadBalancedRetryFactory类型对象
        @Bean
        @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
        @ConditionalOnMissingBean
        public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
                final SpringClientFactory clientFactory) {
            return new RibbonLoadBalancedRetryFactory(clientFactory);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public PropertiesFactory propertiesFactory() {
            return new PropertiesFactory();
        }
    
        //立马执行“饥饿加载”
        @Bean
        @ConditionalOnProperty("ribbon.eager-load.enabled")
        public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
            return new RibbonApplicationContextInitializer(springClientFactory(),
                    ribbonEagerLoadProperties.getClients());
        }
    
        @Configuration(proxyBeanMethods = false)
        @ConditionalOnClass(HttpRequest.class)
        //当ribbon.restclient.enabled参数设置为ture时执行以下配置【如果这里为true,那么拦截器将不能被设置】
        @ConditionalOnRibbonRestClient
        protected static class RibbonClientHttpRequestFactoryConfiguration {
    
            @Autowired
            private SpringClientFactory springClientFactory;
    
            @Bean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
                return restTemplate -> restTemplate
                        .setRequestFactory(ribbonClientHttpRequestFactory);
            }
    
            @Bean
            public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
                return new RibbonClientHttpRequestFactory(this.springClientFactory);
            }
        }
    }
    

    从上可知,@RibbonClients注解会首先执行,然后如果有EurekaClientAutoConfiguration配置类(Eureka Starter中)存在则会等待该配置类结束后再执行RibbonAutoConfiguration,最后执行Spring Cloud Commos里面的LoadBalancerAutoConfiguration与AsyncLoadBalancerAutoConfiguration配置。如下图所示:

    注解和配置类的执行顺序

    小结:从上面的代码可知,该配置类的目的就是创建LoadBalancerClient 的实例对象RibbonLoadBalancerClient。
    首先得到每个客户端的配置(RibbonClientSpecification),然后将这些配置传递给SpringClientFactory 来创建每个Ribbon客户端,接着使用SpringClientFactory作为构造参数创建LoadBalancerClient类型的RibbonLoadBalancerClient实例,从而便与RestTemplate集成在了一起【SprigClientFactory中还会读取配置文件中的Ribbon客户端配置】。


    1,@RibbonClients

    @Configuration(proxyBeanMethods = false)
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE })
    @Documented
    //这里是关键的地方
    @Import(RibbonClientConfigurationRegistrar.class)
    public @interface RibbonClients {
    
        RibbonClient[] value() default {};
    
        Class<?>[] defaultConfiguration() default {};
    }
    

    其中的RibbonClient注解源码如下

    @Configuration(proxyBeanMethods = false)
    //同@RibbonClients 注解一样引入同一个类
    @Import(RibbonClientConfigurationRegistrar.class)
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RibbonClient {
        /**
         *  Ribbon client 的名字,同name属性
         */
        String value() default "";
        /**
         * Ribbon client 的名字 
         */
        String name() default "";
        /**
         * 自定义的@Configuration类,用于配置该Ribbon 客户端
         * 可以配置ILoadBalancer,ServerListFilter,IRule等类型的bean,将会覆盖默认的配置 (RibbonClientConfiguration )
         */
        Class<?>[] configuration() default {};
    }
    

    RibbonClientConfigurationRegistrar
    该类的目的是加载在注解中配置的Ribbon客户端的配置信息,默认情况下由于并没有设置任何值,所以并不会加载任何的配置到IOC中。但我们完全可以自定义@Configuration来定制Ribbon客户端的行为。

    public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
    
        //该方法被调用对注解进行解析,然后将解析结果封装成RibbonClientSpecification实例对象加入IOC中
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
            Map<String, Object> attrs = metadata
                    .getAnnotationAttributes(RibbonClients.class.getName(), true);
            if (attrs != null && attrs.containsKey("value")) {
                //解析各个设置到这里的@RibbonClient注解
                AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
                for (AnnotationAttributes client : clients) {
                    //将解析出的每个注解注册到IOC中【服务名 - 服务对应的配置】
                    registerClientConfiguration(registry, getClientName(client),
                            client.get("configuration"));
                }
            }
            if (attrs != null && attrs.containsKey("defaultConfiguration")) {
                //添加默认的配置【如果有设置】
                String name;
                if (metadata.hasEnclosingClass()) {
                    name = "default." + metadata.getEnclosingClassName();
                }
                else {
                    name = "default." + metadata.getClassName();
                }
                //注册客户端的配置信息
                registerClientConfiguration(registry, name,
                        attrs.get("defaultConfiguration"));
            }
            Map<String, Object> client = metadata
                    .getAnnotationAttributes(RibbonClient.class.getName(), true);
            String name = getClientName(client);
            if (name != null) {
                registerClientConfiguration(registry, name, client.get("configuration"));
            }
        }
    
        private String getClientName(Map<String, Object> client) {
            if (client == null) {
                return null;
            }
            String value = (String) client.get("value");
            if (!StringUtils.hasText(value)) {
                value = (String) client.get("name");
            }
            if (StringUtils.hasText(value)) {
                return value;
            }
            throw new IllegalStateException(
                    "Either 'name' or 'value' must be provided in @RibbonClient");
        }
    
        private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
                Object configuration) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder
                    .genericBeanDefinition(RibbonClientSpecification.class);
            //Ribbon客户端名字【RestTemplate中通过使用该名字替换URL中的主机地址进行请求】
            builder.addConstructorArgValue(name);
            //Ribbon客户端使用的配置类(用于覆盖全局默认配置)
            builder.addConstructorArgValue(configuration);
            //将RibbonClientSpecification实例定义信息注册到Spring中
            registry.registerBeanDefinition(name + ".RibbonClientSpecification",
                    builder.getBeanDefinition());
        }
    }
    

    如果在代码中我们通过@RibbonClients或@RibbonClient注解对Ribbon客户端进行了配置(也可在配置文件中配置),那么这些配置都会被封装到RibbonClientSpecification类型的并添加到IOC,从而供后面的注入所使用。

    完全可以在其它地方再次使用@RibbonClients来定义其全局默认配置,从而影响使用Ribbon客户端发起请求时的行为,如与服务发现集成。


    2,SpringClientFactory

    该类是Ribbon的关键工厂类,它主要负责 创建客户端(Client)、负载平衡器(ILoadBalancer)和客户端配置实例(IClientConfig)。它为每个Ribbon 客户端 创建一个Spring ApplicationContext,并从中提取它所需的bean。

    public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
    
        static final String NAMESPACE = "ribbon";
    
        public SpringClientFactory() {
            //重点:加载默认的配置,该配置的内容在后面说明
            //三个参数分别为:默认配置类,属性源(指定读取什么开头的配置),属性名
            super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
        }
    
        /**
         * Get the rest client associated with the name.
         */
        public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {
            return getInstance(name, clientClass);
        }
    
        /**
         * Get the load balancer associated with the name.
         */
        public ILoadBalancer getLoadBalancer(String name) {
            return getInstance(name, ILoadBalancer.class);
        }
    
        /**
         * Get the client config associated with the name.
         */
        public IClientConfig getClientConfig(String name) {
            return getInstance(name, IClientConfig.class);
        }
    
        /**
         * Get the load balancer context associated with the name.
         */
        public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
            return getInstance(serviceId, RibbonLoadBalancerContext.class);
        }
    
        static <C> C instantiateWithConfig(Class<C> clazz, IClientConfig config) {
            return instantiateWithConfig(null, clazz, config);
        }
    
        static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
                Class<C> clazz, IClientConfig config) {
            C result = null;
            try {
                Constructor<C> constructor = clazz.getConstructor(IClientConfig.class);
                result = constructor.newInstance(config);
            }
            catch (Throwable e) {
                // Ignored
            }
    
            if (result == null) {
                result = BeanUtils.instantiate(clazz);
                if (result instanceof IClientConfigAware) {
                    ((IClientConfigAware) result).initWithNiwsConfig(config);
                }
                if (context != null) {
                    context.getAutowireCapableBeanFactory().autowireBean(result);
                }
            }
            return result;
        }
    
        @Override
        public <C> C getInstance(String name, Class<C> type) {
            C instance = super.getInstance(name, type);
            if (instance != null) {
                return instance;
            }
            IClientConfig config = getInstance(name, IClientConfig.class);
            //实例化对象
            return instantiateWithConfig(getContext(name), type, config);
        }
    
        //判断缓存中是否有ApplicationContext,如果游就直接返回,
        //否则创建AnnotationConfigApplicationContext,同时初始化再缓存【在创建ApplicationContext的过程中将会读取配置数据、加载默认的配置等,具体过程在父类中】
        @Override
        protected AnnotationConfigApplicationContext getContext(String name) {
            return super.getContext(name);
        }
    }
    

    由于上面getContext(String name)方法很关键,所以特将父类中的代码摘出来

        protected AnnotationConfigApplicationContext getContext(String name) {
            //判断缓存中是否有指定名的ApplicationContext,如果有就直接取得并返回,否则创建
            if (!this.contexts.containsKey(name)) {
                synchronized (this.contexts) {
                    if (!this.contexts.containsKey(name)) {
                        //创建ApplicationContext并缓存
                        this.contexts.put(name, createContext(name));
                    }
                }
            }
            return this.contexts.get(name);
        }
    
        protected AnnotationConfigApplicationContext createContext(String name) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            //configurations就是RibbonAutoConfiguration配置类中收集的所有通过注解配置的客户端配置(RibbonClientSpecification)
            if (this.configurations.containsKey(name)) {
                //如果有针对该客户端的配置,则注册到ApplicationContext中
                for (Class<?> configuration : this.configurations.get(name)
                        .getConfiguration()) {
                    context.register(configuration);
                }
            }
            for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
                //查找默认配置项并加载到IOC中
                if (entry.getKey().startsWith("default.")) {
                    for (Class<?> configuration : entry.getValue().getConfiguration()) {
                        context.register(configuration);
                    }
                }
            }
            //将PropertyPlaceholderAutoConfiguration以及默认配置RibbonClientConfiguration注册到IOC中
            context.register(PropertyPlaceholderAutoConfiguration.class,
                    this.defaultConfigType);
            context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
                    this.propertySourceName,
                    Collections.<String, Object>singletonMap(this.propertyName, name)));
            if (this.parent != null) {
                // Uses Environment from parent as well as beans
                context.setParent(this.parent);
                context.setClassLoader(this.parent.getClassLoader());
            }
            context.setDisplayName(generateDisplayName(name));
            context.refresh();
            return context;
        }
    

    接下来看下其默认的配置类RibbonClientConfiguration
    该类中在Bean创建的时候都添加了@ConditionalOnMissingBean注解,说明我们可以将其默认的Bean给替换掉

    @SuppressWarnings("deprecation")
    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties
    // 导入客户端的配置,默认使用ApacheHttpClient创建RibbonLoadBalancingHttpClient客户端
    @Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
            RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
    public class RibbonClientConfiguration {
        /**
         * Ribbon client default connect timeout.
         */
        public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
        /**
         * Ribbon client default read timeout.
         */
        public static final int DEFAULT_READ_TIMEOUT = 1000;
        /**
         * Ribbon client default Gzip Payload flag.
         */
        public static final boolean DEFAULT_GZIP_PAYLOAD = true;
    
        //配置文件中的 ribbon.client.name
        @RibbonClientName
        private String name = "client";
    
         /*
          * 通过该对象可以读取配置文件中定义的实例对象,包含以下几个类型
          *  ILoadBalancer、IPing、IRule、ServerList、ServerListFilter
          *  对应的添加以下配置即可替换默认的
          *  {ribbon.client.name}.ribbon.NFLoadBalancerClassName
          *  {ribbon.client.name}.ribbon.NFLoadBalancerPingClassName
          *  {ribbon.client.name}.ribbon.NFLoadBalancerRuleClassName
          *  {ribbon.client.name}.ribbon.NIWSServerListClassName
          *  {ribbon.client.name}.ribbon.NIWSServerListFilterClassName
          */
        @Autowired
        private PropertiesFactory propertiesFactory;
    
        //用于解析ribbon客户端的配置数据
        @Bean
        @ConditionalOnMissingBean
        public IClientConfig ribbonClientConfig() {
            DefaultClientConfigImpl config = new DefaultClientConfigImpl();
            config.loadProperties(this.name);
            config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
            config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
            config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
            return config;
        }
        //Ribbon负载均衡策略。默认的策略能够在多区域环境下选出最佳区域的实例进行访问
        @Bean
        @ConditionalOnMissingBean
        public IRule ribbonRule(IClientConfig config) {
            if (this.propertiesFactory.isSet(IRule.class, name)) {
                return this.propertiesFactory.get(IRule.class, config, name);
            }
            ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
            rule.initWithNiwsConfig(config);
            return rule;
        }
        //Ribbon实例检查策略。默认 这实际上并不会检查实例是否可用而始终返回true。
        @Bean
        @ConditionalOnMissingBean
        public IPing ribbonPing(IClientConfig config) {
            if (this.propertiesFactory.isSet(IPing.class, name)) {
                return this.propertiesFactory.get(IPing.class, config, name);
            }
            return new DummyPing();
        }
        //服务实例清单的维护机制
        @Bean
        @ConditionalOnMissingBean
        @SuppressWarnings("unchecked")
        public ServerList<Server> ribbonServerList(IClientConfig config) {
            if (this.propertiesFactory.isSet(ServerList.class, name)) {
                return this.propertiesFactory.get(ServerList.class, config, name);
            }
            ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
            serverList.initWithNiwsConfig(config);
            return serverList;
        }
        
        @Bean
        @ConditionalOnMissingBean
        public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
            return new PollingServerListUpdater(config);
        }
        //负载均衡器。默认的具备了区域感知的能力
        @Bean
        @ConditionalOnMissingBean
        public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
                ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
                IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
            if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
                return this.propertiesFactory.get(ILoadBalancer.class, config, name);
            }
            return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
                    serverListFilter, serverListUpdater);
        }
        //服务实例清单过滤机制。默认的策略能够优先过滤出与请求调用方处于同区域的服务实例
        @Bean
        @ConditionalOnMissingBean
        @SuppressWarnings("unchecked")
        public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
            if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
                return this.propertiesFactory.get(ServerListFilter.class, config, name);
            }
            ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
            filter.initWithNiwsConfig(config);
            return filter;
        }
    
        @Bean
        @ConditionalOnMissingBean
        public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
                IClientConfig config, RetryHandler retryHandler) {
            return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public RetryHandler retryHandler(IClientConfig config) {
            return new DefaultLoadBalancerRetryHandler(config);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public ServerIntrospector serverIntrospector() {
            return new DefaultServerIntrospector();
        }
    
        //初始化方法
        @PostConstruct
        public void preprocess() {
            setRibbonProperty(name, DeploymentContextBasedVipAddresses.key(), name);
        }
    
        static class OverrideRestClient extends RestClient {
    
            private IClientConfig config;
    
            private ServerIntrospector serverIntrospector;
    
            protected OverrideRestClient(IClientConfig config,
                    ServerIntrospector serverIntrospector) {
                super();
                this.config = config;
                this.serverIntrospector = serverIntrospector;
                initWithNiwsConfig(this.config);
            }
    
            @Override
            public URI reconstructURIWithServer(Server server, URI original) {
                URI uri = updateToSecureConnectionIfNeeded(original, this.config,
                        this.serverIntrospector, server);
                return super.reconstructURIWithServer(server, uri);
            }
    
            @Override
            protected Client apacheHttpClientSpecificInitialization() {
                ApacheHttpClient4 apache = (ApacheHttpClient4) super.apacheHttpClientSpecificInitialization();
                apache.getClientHandler().getHttpClient().getParams().setParameter(
                        ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
                return apache;
            }
        }
    }
    

    3,RibbonLoadBalancerClient

    org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient 的源码如下

    public class RibbonLoadBalancerClient implements LoadBalancerClient {
    
        private SpringClientFactory clientFactory;
    
        public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
            this.clientFactory = clientFactory;
        }
    
        @Override
        public URI reconstructURI(ServiceInstance instance, URI original) {
            Assert.notNull(instance, "instance can not be null");
            String serviceId = instance.getServiceId();
            RibbonLoadBalancerContext context = this.clientFactory
                    .getLoadBalancerContext(serviceId);
    
            URI uri;
            Server server;
            if (instance instanceof RibbonServer) {
                RibbonServer ribbonServer = (RibbonServer) instance;
                server = ribbonServer.getServer();
                uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
            }
            else {
                server = new Server(instance.getScheme(), instance.getHost(),
                        instance.getPort());
                IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
                ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
                uri = updateToSecureConnectionIfNeeded(original, clientConfig,
                        serverIntrospector, server);
            }
            return context.reconstructURIWithServer(server, uri);
        }
    
        @Override
        public ServiceInstance choose(String serviceId) {
            return choose(serviceId, null);
        }
    
        /**
         * New: Select a server using a 'key'.
         * @param serviceId of the service to choose an instance for
         * @param hint to specify the service instance
         * @return the selected {@link ServiceInstance}
         */
        public ServiceInstance choose(String serviceId, Object hint) {
            Server server = getServer(getLoadBalancer(serviceId), hint);
            if (server == null) {
                return null;
            }
            return new RibbonServer(serviceId, server, isSecure(server, serviceId),
                    serverIntrospector(serviceId).getMetadata(server));
        }
    
        //在拦截器中被调用的方法。通过该方法调用Ribbon的负载均衡器得到需要访问的服务实例,然后执行LoadBalancerRequest的apply方法并将最后的结果返回。
        @Override
        public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
                throws IOException {
            return execute(serviceId, request, null);
        }
    
        /**
         * New: Execute a request by selecting server using a 'key'. The hint will have to be
         * the last parameter to not mess with the `execute(serviceId, ServiceInstance,
         * request)` method. This somewhat breaks the fluent coding style when using a lambda
         * to define the LoadBalancerRequest.
         * @param <T> returned request execution result type
         * @param serviceId id of the service to execute the request to
         * @param request to be executed
         * @param hint used to choose appropriate {@link Server} instance
         * @return request execution result
         * @throws IOException executing the request may result in an {@link IOException}
         */
        public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
                throws IOException {
            //获取Ribbon的LoadBalancer实例对象
            ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
            //从LoadBalancer中得到选出的服务实例
            Server server = getServer(loadBalancer, hint);
            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 {
                //调用LoadBalanceClient的apply方法【内部会执行】
                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;
        }
    
        private ServerIntrospector serverIntrospector(String serviceId) {
            ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
                    ServerIntrospector.class);
            if (serverIntrospector == null) {
                serverIntrospector = new DefaultServerIntrospector();
            }
            return serverIntrospector;
        }
    
        private boolean isSecure(Server server, String serviceId) {
            IClientConfig config = this.clientFactory.getClientConfig(serviceId);
            ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
            return RibbonUtils.isSecure(config, serverIntrospector, server);
        }
    
        // Note: This method could be removed?
        protected Server getServer(String serviceId) {
            return getServer(getLoadBalancer(serviceId), null);
        }
    
        protected Server getServer(ILoadBalancer loadBalancer) {
            return getServer(loadBalancer, null);
        }
    
        protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
            if (loadBalancer == null) {
                return null;
            }
            // Use 'default' on a null hint, or just pass it on?
            return loadBalancer.chooseServer(hint != null ? hint : "default");
        }
    
        protected ILoadBalancer getLoadBalancer(String serviceId) {
            return this.clientFactory.getLoadBalancer(serviceId);
        }
    
        /**
         * Ribbon-server-specific {@link ServiceInstance} implementation.
         */
        public static class RibbonServer implements ServiceInstance {
    
            private final String serviceId;
    
            private final Server server;
    
            private final boolean secure;
    
            private Map<String, String> metadata;
    
            public RibbonServer(String serviceId, Server server) {
                this(serviceId, server, false, Collections.emptyMap());
            }
    
            public RibbonServer(String serviceId, Server server, boolean secure,
                    Map<String, String> metadata) {
                this.serviceId = serviceId;
                this.server = server;
                this.secure = secure;
                this.metadata = metadata;
            }
    
            @Override
            public String getInstanceId() {
                return this.server.getId();
            }
    
            @Override
            public String getServiceId() {
                return this.serviceId;
            }
    
            @Override
            public String getHost() {
                return this.server.getHost();
            }
    
            @Override
            public int getPort() {
                return this.server.getPort();
            }
    
            @Override
            public boolean isSecure() {
                return this.secure;
            }
    
            @Override
            public URI getUri() {
                return DefaultServiceInstance.getUri(this);
            }
    
            @Override
            public Map<String, String> getMetadata() {
                return this.metadata;
            }
    
            public Server getServer() {
                return this.server;
            }
    
            @Override
            public String getScheme() {
                return this.server.getScheme();
            }
    
            @Override
            public String toString() {
                final StringBuilder sb = new StringBuilder("RibbonServer{");
                sb.append("serviceId='").append(serviceId).append('\'');
                sb.append(", server=").append(server);
                sb.append(", secure=").append(secure);
                sb.append(", metadata=").append(metadata);
                sb.append('}');
                return sb.toString();
            }
        }
    }
    

    至此,Spring cloud Ribbon的解析基本告一段落。总的原理就是Spring cloud coomms已经提供了让RestTemplate进行负载均衡调用的“规范”,Spring cloud Ribbion基于该规范实现了LoadBalancerClient接口提供一个负载均衡的客户端,然后就可以使用netfix的Ribbon进行一系列的操作从而达到负载均衡的目的。 后面章节还将继续分析Ribbon与Eureka等服务发现组件配合使用的原理。

    Ribbon提供的负载均衡策略

    相关文章

      网友评论

          本文标题:SpringCloud解析三:Ribbon源码分析(下)

          本文链接:https://www.haomeiwen.com/subject/wimmnctx.html