Ribbon原理和源码分析

Spring Cloud Ribbon是Netflix开源客户端负载均衡器,底层基于RestTemplate拦截器(ClientHttpRequestInterceptor)扩展实现,提供多种负载均衡策略支持,能够无缝集成Spring Cloud组件。

01 自动装配

1.1 客户端配置

Ribbon依赖spring-cloud-netflix-ribbon-xxx.jar配置LoadBalancerClient与SpringClientFactory实例,提供自动注入客户端配置。

java 复制代码
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class })
public class RibbonAutoConfiguration {
    // 客户端配置: 通过@RibbonClients和@RibbonClient配置
    @Autowired(required = false)
    private List<RibbonClientSpecification> configurations = new ArrayList<>();
    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;

    @Bean
    @ConditionalOnMissingBean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }
    
    // ...
    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }
    // ...
}

Ribbon通过SpringClientFactory#getContext方法创建客户端配置IOC容器,全局默认客户端配置实现就是RibbonClientConfiguration ,主要包括客户端默认负载均衡规则(IRule)、服务端心跳检测(IPing)、服务端列表(ServerList<Server>)和客户端负载均衡器(ILoadBalancer)组件配置。

java 复制代码
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
    static final String NAMESPACE = "ribbon";
    public SpringClientFactory() {
        // 默认全局配置: RibbonClientConfiguration.class
        super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
    }
}

Ribbon客户端配置是通过**@RibbonClients****@RibbonClient**注解提供开发人员配置,具体解析过程详见([1.1.2 客户端配置](#1.1.2 客户端配置 "#csjUW")),创建SpringClientFactory实例使用NamedContextFactory#setConfigurations方法注入客户端配置。

java 复制代码
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
    implements DisposableBean, ApplicationContextAware {
    private final String propertySourceName;
    private final String propertyName;
    // 客户端容器缓存
    private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
    // 配置类
    private Map<String, C> configurations = new ConcurrentHashMap<>();
    // 父容器
    private ApplicationContext parent;
    // 默认全局配置
    private Class<?> defaultConfigType;

    public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName, String propertyName) {
        this.defaultConfigType = defaultConfigType;
        this.propertySourceName = propertySourceName;
        this.propertyName = propertyName;
    }
        
    protected AnnotationConfigApplicationContext getContext(String name) {
        if (!this.contexts.containsKey(name)) {
            synchronized (this.contexts) {
                if (!this.contexts.containsKey(name)) {
                    this.contexts.put(name, createContext(name));
                }
            }
        }
        return this.contexts.get(name);
    }

    // 创建上下文: 每个客户端使用一个IOC容器管理
    protected AnnotationConfigApplicationContext createContext(String name) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 指定客户端配置
        if (this.configurations.containsKey(name)) {
            for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
                // 注册配置实例
                context.register(configuration);
            }
        }
        // 全局默认配置
        for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
            if (entry.getKey().startsWith("default.")) {
                for (Class<?> configuration: entry.getValue().getConfiguration()) {
                    context.register(configuration);
                }
            }
        }
        // 全局配置类
        context.register(PropertyPlaceholderAutoConfiguration.class, defaultConfigType);
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource (
                this.propertySourceName,
                Collections.<String, Object>singletonMap(this.propertyName, name))
        );
        // 设置IOC父容器
        if (this.parent != null) {
            context.setParent(this.parent);
            context.setClassLoader(this.parent.getClassLoader());
        }
        context.setDisplayName(generateDisplayName(name));
        // 刷新IOC容器
        context.refresh();
        return context;
    }
    protected String generateDisplayName(String name) {
        return this.getClass().getSimpleName() + "-" + name;
    }
}

1.1.1 默认全局配置类

Ribbon全局默认配置类就是RibbonClientConfiguration,内置客户端默认负载均衡规则(IRule)、服务端心跳检测(IPing)、服务端列表(ServerList)和客户端负载均衡器(ILoadBalancer)组件配置。

java 复制代码
@Import({ HttpClientConfiguration.class, 
          OkHttpRibbonConfiguration.class,
          RestClientRibbonConfiguration.class, 
          HttpClientRibbonConfiguration.class 
})
public class RibbonClientConfiguration {
    public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    public static final int DEFAULT_READ_TIMEOUT = 1000;
    public static final boolean DEFAULT_GZIP_PAYLOAD = true;

    @RibbonClientName
    private String name = "client";

    @Autowired
    private PropertiesFactory propertiesFactory;

    @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;
    }

    @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;
    }

    @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;
    }
}

1.1.2 客户端配置

Ribbon申明@RibbonClients@RibbonClient注解提供开发人员进行客户端配置,底层使用客户端配置注册者(RibbonClientConfigurationRegistrar)解析注解为RibbonClientSpecification,接着直接注入IOC容器。所以,自动化配置SpringClientFactory实例就能够找到这些客户端配置。

java 复制代码
@Configuration(proxyBeanMethods = false)
@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClient {
    String value() default "";
    String name() default "";
    Class<?>[] configuration() default {};
}
java 复制代码
public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 获取注解@RibbonClients属性
        Map<String, Object> attrs = metadata.getAnnotationAttributes(RibbonClients.class.getName(), true);
        if (attrs != null && attrs.containsKey("value")) {
            AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
            for (AnnotationAttributes client : clients) {
                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"));
        }
        // 获取获取注解@RibbonClient属性
        Map<String, Object> client = metadata.getAnnotationAttributes(RibbonClient.class.getName(), true);
        // 获取客户端名字
        String name = getClientName(client);
        if (name != null) {
            registerClientConfiguration(registry, name, client.get("configuration"));
        }
    }

    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RibbonClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(name + ".RibbonClientSpecification", builder.getBeanDefinition());
    }
}

1.1.3 创建负载均衡器

Ribbon客户端工厂SpringClientFactory提供负载均衡各组件获取,比如负载均衡器(ILoadBalancer)、客户端配置(IClientConfig)、其他组件(负载均衡规则),大多场景是第一次使用客户端时进行初始化。

java 复制代码
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
    // 获取服务负载均衡器
    public ILoadBalancer getLoadBalancer(String name) {
        return getInstance(name, ILoadBalancer.class);
    }

    // 获取服务客户端配置
    public IClientConfig getClientConfig(String name) {
        return getInstance(name, IClientConfig.class);
    }

    public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
        return getInstance(serviceId, RibbonLoadBalancerContext.class);
    }

    static <C> C instantiateWithConfig(Class<C> clazz, IClientConfig config) {
        return instantiateWithConfig(null, clazz, config);
    }

    @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);
    }

    // 指定配置初始化实例
    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 ignored) {

        }
        if (result == null) {
            result = BeanUtils.instantiateClass(clazz);
            if (result instanceof IClientConfigAware) {
                ((IClientConfigAware) result).initWithNiwsConfig(config);
            }
            if (context != null) {
                context.getAutowireCapableBeanFactory().autowireBean(result);
            }
        }
        return result;
    }

    @Override
    protected AnnotationConfigApplicationContext getContext(String name) {
        return super.getContext(name);
    }
}
java 复制代码
public abstract class NamedContextFactory<C extends Specification>
    implements DisposableBean, ApplicationContextAware {
    public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        try {
            return context.getBean(type);
        } catch (NoSuchBeanDefinitionException ignore) {
            // ignore
        }
        return null;
    }

    public <T> T getInstance(String name, Class<?> clazz, Class<?>... generics) {
        ResolvableType type = ResolvableType.forClassWithGenerics(clazz, generics);
        return getInstance(name, type);
    }

    @SuppressWarnings("unchecked")
    public <T> T getInstance(String name, ResolvableType type) {
        AnnotationConfigApplicationContext context = getContext(name);
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type);
        if (beanNames.length > 0) {
            for (String beanName : beanNames) {
                if (context.isTypeMatch(beanName, type)) {
                    return (T) context.getBean(beanName);
                }
            }
        }
        return null;
    }

    public <T> Map<String, T> getInstances(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
    }

    public <T> ObjectProvider<T> getLazyProvider(String name, Class<T> type) {
        return new ClientFactoryObjectProvider<>(this, name, type);
    }

    public <T> ObjectProvider<T> getProvider(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        return context.getBeanProvider(type);
    }
}

1.2 拦截器配置

Ribbon依赖spring-cloud-commons-xxx.jar提供自动配置类LoadBalancerAutoConfiguration,用于申明RestTemplateCustomizer封装拦截器LoadBalancerInterceptor注入RestTemplate组件。

Spring所有单例初始化后筛选SmartInitializingSingleton实例进行调用,主要调用入口就是IOC容器单实例初始化方法DefaultListableBeanFactory#preInstantiateSingletons

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(required = false)
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
        final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                // 循环RestTemplateCustomizer初始化RestTemplate
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
    }

    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {
        // 负载均衡拦截器
        @Bean
        public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
    }
}

02 RestTemplate拦截器

Ribbon基于RestTemplate组件基础,通过扩展RestTemplate拦截器实现客户端服务发现、负载均衡功能,LoadBalancerInterceptor底层依赖LoadBalancerRequestFactory和LoadBalancerClient实现负载均衡。

java 复制代码
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) {
        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();
        return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

2.1 创建请求

装饰器模式允许运行时动态添加新功能,而无需修改对象原始代码。所以,Ribbon拦截器也是采用装饰器模式增强请求行为,从而根据服务实例获取URL。

java 复制代码
public class LoadBalancerRequestFactory {
    private LoadBalancerClient loadBalancer;
    private List<LoadBalancerRequestTransformer> transformers;

    public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer,
        List<LoadBalancerRequestTransformer> transformers) {
        this.loadBalancer = loadBalancer;
        this.transformers = transformers;
    }

    public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request, 
    final byte[] body, final ClientHttpRequestExecution execution) {
        return instance -> {
            HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);
            if (this.transformers != null) {
                for (LoadBalancerRequestTransformer transformer : this.transformers) {
                    serviceRequest = transformer.transformRequest(serviceRequest, instance);
                }
            }
            return execution.execute(serviceRequest, body);
        };
    }
}
java 复制代码
public class ServiceRequestWrapper extends HttpRequestWrapper {
    private final ServiceInstance instance;
    private final LoadBalancerClient loadBalancer;
    public ServiceRequestWrapper(HttpRequest request, ServiceInstance instance, LoadBalancerClient loadBalancer) {
            super(request);
            this.instance = instance;
            this.loadBalancer = loadBalancer;
    }

    @Override
    public URI getURI() {
            URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
            return uri;
    }
}

2.2 发送请求

Ribbon负载均衡器客户端RibbonLoadBalancerClient发送请求,分为获取负载均衡器、选择负载均衡服务、执行请求发送三个步骤进行处理,负载均衡组件通过SpringClientFactory提供。

java 复制代码
public class RibbonLoadBalancerClient implements LoadBalancerClient {
    private SpringClientFactory clientFactory;

    public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
            this.clientFactory = clientFactory;
    }

    @Override
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
            return execute(serviceId, request, null);
    }

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
        // 获取指定客户端负载均衡器, serviceId也就是客户端服务
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        // 负载均衡规则获取服务
            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 {
        // 发送请求
                    T returnVal = request.apply(serviceInstance);
                    statsRecorder.recordStats(returnVal);
                    return returnVal;
            } catch (IOException ex) {
                    statsRecorder.recordStats(ex);
                    throw ex;
            }
            catch (Exception ex) {
                    statsRecorder.recordStats(ex);
                    ReflectionUtils.rethrowRuntimeException(ex);
            }
            return null;
        }
    protected ILoadBalancer getLoadBalancer(String serviceId) {
        // 获取客户端配置
        return this.clientFactory.getLoadBalancer(serviceId);
    }
    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        if (loadBalancer == null) {
             return null;
        }
        // 选择服务器
        return loadBalancer.chooseServer(hint != null ? hint : "default");
    }
}

03 负载均衡器

Ribbon全局默认配置类RibbonClientConfiguration,提供负载均衡器配置为ZoneAwareLoadBalancer,负载均衡器主要提供服务列表获取、服务选择2个核心功能。

java 复制代码
// 负载均衡器接口
public interface ILoadBalancer {
    // 服务器初始列表
    public void addServers(List<Server> newServers);
    // 负载选择一个服务器
    public Server chooseServer(Object key);
    // 标注服务器已下线
    public void markServerDown(Server server);
    @Deprecated
    public List<Server> getServerList(boolean availableOnly);
    // 获取可触达服务器
    public List<Server> getReachableServers();
    // 获取所有服务器
    public List<Server> getAllServers();
}

Ribbon默认负载均衡器ZoneAwareLoadBalancer ,如果不存在多区域情况下直接通过IRule选择可调用客户端服务,默认IRule=ZoneAvoidanceRule

java 复制代码
public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {
    private static final DynamicBooleanProperty ENABLED = DynamicPropertyFactory.getInstance().getBooleanProperty("ZoneAwareNIWSDiscoveryLoadBalancer.enabled", true);
    
    @Override
    public Server chooseServer(Object key) {
        // 负载均衡逻辑, 默认没有多区域
        // 1. 没有开启ZoneAware
        // 2. 或者区域不存在
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            return super.chooseServer(key);
        }
        Server server = null;
        try {
            LoadBalancerStats lbStats = getLoadBalancerStats();
            // 创建快照
            Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
            if (triggeringLoad == null) {
                triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
            }

            if (triggeringBlackoutPercentage == null) {
                triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
            }
            Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
            logger.debug("Available zones: {}", availableZones);
            if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
                String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                if (zone != null) {
                    BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
                    server = zoneLoadBalancer.chooseServer(key);
                }
            }
        } catch (Exception e) {
            logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
        }
        if (server != null) {
            return server;
        } else {
            logger.debug("Zone avoidance logic is not invoked.");
            return super.chooseServer(key);
        }
    }
}

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    
}

public class BaseLoadBalancer extends AbstractLoadBalancer implements
    PrimeConnections.PrimeConnectionListener, IClientConfigAware {
    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                return null;
            }
        }
    }
}

3.1 初始定时任务

Ribbon默认全局配置类RibbonClientConfiguration会创建ZoneAwareLoadBalancer,初始化实例需要写道服务列表(ConfigurationBasedServerList)、服务列表更新器(ServerListUpdater)以及过滤器,主要目的是使得负载均衡器具备获取服务功能。

java 复制代码
public class RibbonClientConfiguration {
    @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;
    }
}

ZoneAwareLoadBalancer核心实现在DynamicServerListLoadBalancer,用于获取服务列表、刷新服务列表、负载均衡规则选择服务。

java 复制代码
public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {
    public ZoneAwareLoadBalancer(IClientConfig clientConfig, 
        IRule rule, 
        IPing ping, 
        ServerList<T> serverList, 
        ServerListFilter<T> filter, 
        ServerListUpdater serverListUpdater) {
        super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
    }
}

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    volatile ServerList<T> serverListImpl;
    protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
        @Override
        public void doUpdate() {
            updateListOfServers();
        }
    };
    
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
        ServerList<T> serverList, ServerListFilter<T> filter, ServerListUpdater serverListUpdater) {
        super(clientConfig, rule, ping);
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
            ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
        }
        // 初始化操作
        restOfInit(clientConfig);
    }
    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        this.setEnablePrimingConnections(false);
        // 初始化服务发现
        enableAndInitLearnNewServersFeature();
        // 更新服务列表
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections().primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
    }
    public void enableAndInitLearnNewServersFeature() {
        // 调度更新服务列表
        serverListUpdater.start(updateAction);
    }
}

3.2 更新服务列表

负载均衡器ZoneAwareLoadBalancer通过ServerList 获取服务列表,提供开发者自定义实现进行扩展,比如Nacos扩展实现就是NacosServerList。

java 复制代码
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    volatile ServerList<T> serverListImpl;
    
    @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
            // 获取已更新列表
            servers = serverListImpl.getUpdatedListOfServers();
            if (filter != null) {
                // 过滤服务
                servers = filter.getFilteredListOfServers(servers);
            }
        }
        updateAllServerList(servers);
    }
    protected void updateAllServerList(List<T> ls) {
        if (serverListUpdateInProgress.compareAndSet(false, true)) {
            try {
                for (T s : ls) {
                    s.setAlive(true); 
                }
                setServersList(ls);
                super.forceQuickPing();
            } finally {
                serverListUpdateInProgress.set(false);
            }
        }
    }
    @Override
    public void setServersList(List lsrv) {
        super.setServersList(lsrv);
        List<T> serverList = (List<T>) lsrv;
        Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();
        for (Server server : serverList) {
            getLoadBalancerStats().getSingleServerStat(server);
            String zone = server.getZone();
            if (zone != null) {
                zone = zone.toLowerCase();
                List<Server> servers = serversInZones.get(zone);
                if (servers == null) {
                    servers = new ArrayList<Server>();
                    serversInZones.put(zone, servers);
                }
                servers.add(server);
            }
        }
        setServerListForZones(serversInZones);
    }
}
java 复制代码
public class BaseLoadBalancer extends AbstractLoadBalancer implements
    PrimeConnections.PrimeConnectionListener, IClientConfigAware {
    @Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> allServerList = Collections
            .synchronizedList(new ArrayList<Server>());
    
    public void setServersList(List lsrv) {
        Lock writeLock = allServerLock.writeLock();
        ArrayList<Server> newServers = new ArrayList<Server>();
        writeLock.lock();
        try {
            ArrayList<Server> allServers = new ArrayList<Server>();
            for (Object server : lsrv) {
                if (server == null) {
                    continue;
                }
                if (server instanceof String) {
                    server = new Server((String) server);
                }
                if (server instanceof Server) {
                    allServers.add((Server) server);
                } else {
                    throw new IllegalArgumentException(...);
                }
            }
            boolean listChanged = false;
            if (!allServerList.equals(allServers)) {
                listChanged = true;
                if (changeListeners != null && changeListeners.size() > 0) {
                   List<Server> oldList = ImmutableList.copyOf(allServerList);
                   List<Server> newList = ImmutableList.copyOf(allServers);                   
                   for (ServerListChangeListener l: changeListeners) {
                       try {
                           l.serverListChanged(oldList, newList);
                       } catch (Exception e) {
                           logger.error("LoadBalancer [{}]: Error invoking server list change listener", name, e);
                       }
                   }
                }
            }
            if (isEnablePrimingConnections()) {
                for (Server server : allServers) {
                    if (!allServerList.contains(server)) {
                        server.setReadyToServe(false);
                        newServers.add((Server) server);
                    }
                }
                if (primeConnections != null) {
                    primeConnections.primeConnectionsAsync(newServers, this);
                }
            }
            allServerList = allServers;
            if (canSkipPing()) {
                for (Server s : allServerList) {
                    s.setAlive(true);
                }
                upServerList = allServerList;
            } else if (listChanged) {
                forceQuickPing();
            }
        } finally {
            writeLock.unlock();
        }
    }
}

3.3 Nacos扩展服务列表

Nacos依赖spring-cloud-starter-alibaba-nacos-discovery.jar提供NacosServerList实例申明,用于Nacos客户端SDK拉取服务实现,通过spring.factories配置文件进行维护。

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnRibbonNacos
public class NacosRibbonClientConfiguration {
    @Autowired
    private PropertiesFactory propertiesFactory;

    @Bean
    @ConditionalOnMissingBean
    public ServerList<?> ribbonServerList(IClientConfig config,
            NacosDiscoveryProperties nacosDiscoveryProperties) {
            if (this.propertiesFactory.isSet(ServerList.class, config.getClientName())) {
                    ServerList serverList = this.propertiesFactory.get(ServerList.class, config, config.getClientName());
                    return serverList;
            }
            NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);
            serverList.initWithNiwsConfig(config);
            return serverList;
    }

    @Bean
    @ConditionalOnMissingBean
    public NacosServerIntrospector nacosServerIntrospector() {
            return new NacosServerIntrospector();
    }
}
java 复制代码
public abstract class AbstractServerList<T extends Server> implements ServerList<T>, IClientConfigAware {   
    
}

public class NacosServerList extends AbstractServerList<NacosServer> {
    private List<NacosServer> getServers() {
		try {
			String group = discoveryProperties.getGroup();
			List<Instance> instances = discoveryProperties.namingServiceInstance().selectInstances(serviceId, group, true);
			return instancesToServerList(instances);
		} catch (Exception e) {
			throw new IllegalStateException(...);
		}
	}
}

04 负载均衡规则

Ribbon全局默认配置类(RibbonClientConfiguration)提供负载均衡规则为ZoneAvoidanceRule,实现客户端多实例规则选择。

全局默认配置类创建ZoneAvoidanceRule实例,还会调用ZoneAvoidanceRule#initWithNiwsConfig进行初始化配置,可以看出负载均衡是通过ZoneAvoidancePredicate和AvailabilityPredicate实现。

java 复制代码
public class ZoneAvoidanceRule extends PredicateBasedRule {
    private CompositePredicate compositePredicate;
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this, clientConfig);
        AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this, clientConfig);
        compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
    }
    private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
        return CompositePredicate.withPredicates(p1, p2)
                             .addFallbackPredicate(p2)
                             .addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
                             .build();
        
    }
}

public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
    public abstract AbstractServerPredicate getPredicate();
    
    @Override
    public Server choose(Object key) {
        // 获取负载均衡器
        ILoadBalancer lb = getLoadBalancer();
        // 选择服务
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }
}
相关推荐
掘金-我是哪吒31 分钟前
分布式微服务系统架构第158集:JavaPlus技术文档平台日更-JVM基础知识
jvm·分布式·微服务·架构·系统架构
Kookoos2 小时前
ABP VNext + Tye:本地微服务编排与调试
微服务·云原生·架构·tye
掘金-我是哪吒7 小时前
分布式微服务系统架构第157集:JavaPlus技术文档平台日更-Java多线程编程技巧
java·分布式·微服务·云原生·架构
掘金-我是哪吒8 小时前
分布式微服务系统架构第155集:JavaPlus技术文档平台日更-Java线程池实现原理
java·分布式·微服务·云原生·架构
Code季风21 小时前
深入理解微服务中的服务注册与发现(Consul)
java·运维·微服务·zookeeper·架构·go·consul
光军oi21 小时前
java微服务(Springboot篇)——————IDEA搭建第一个Springboot入门项目
java·spring boot·微服务
guojl1 天前
RestTemplate使用手册
spring cloud·微服务
guojl1 天前
RestTemplate原理分析
spring cloud·微服务
Ken_11151 天前
SpringCloud系列(51)--SpringCloud Stream之使用分组解决消息重复消费问题
spring cloud