Spring Cloud Ribbon 详解
客户端负载均衡, Ribbon的核心概念是命名的客户端.
使用
引入Ribbon依赖和配置
加入spring-cloud-starter-netflix-ribbon
依赖
代码中使用RibbonClient注解
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class) public class TestConfiguration {}
@Configuration protected static class FooConfiguration {
@Bean
public ZonePreferenceServerListFilter serverListFilter() {
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.setZone("myTestZone");
return filter;
}
@Bean
public IPing ribbonPing() {
return new PingUrl();
}
}
Ribbon客户端的配置, 如果不指定会使用默认的实现:
- IClientConfig 客户端相关配置
- IRule 定义负载均衡策略
- IPing 定义如何ping目标服务实例来判断是否存活, ribbon使用单独的线程每隔一段时间(默认10s)对本地缓存的ServerList做一次检查
- ServerList
定义如何获取服务实例列表. 两种实现基于配置的 ConfigurationBasedServerList
和基于Eureka服务发现的DiscoveryEnabledNIWSServerList
- ServerListFilter
用来使用期望的特征过滤静态配置动态获得的候选服务实例列表. 若未提供, 默认使用 ZoneAffinityServerListFilter
- ILoadBalancer 定义了软负载均衡器的操作的接口. 一个典型的负载均衡器至少需要一组用来做负载均衡的服务实例, 一个标记某个服务实例不在旋转中的方法, 和对应的方法调用从实例列表中选出某一个服务实例.
- ServerListUpdater DynamicServerListLoadBalancer用来更新实例列表的策略(推
EurekaNotificationServerListUpdater
/拉PollingServerListUpdater
, 默认是拉)
分析
类结构
实现
实际使用中, 服务调用使用RestTemplate, 请求地址为http://<serviceName>/<path>
, 如http://foo/
通过@RibbonClient
注解为服务创建ribbon客户端, 名字为方法名. RestTemplate发送请求的时候, 请求会被LoadBalancerInterceptor
拦截到, 使用服务对应的ribbon客户端. Ribbon客户端的LoadBalancer
会从ServerList
中根据IRule
的规则选择某个服务实例作为请求对象. ServerList
有动态的实现, 更新列表时会使用ServerListFilter
进行过滤.
RibbonClient注解
从注释上@RibbonClient
为一个ribbon客户端声明配置信息. 把这个注解加在任何@Configuration
标注的类上, 然后注入SpringClientFactory
来访问创建的客户端.
从代码上看@RibbonClient
引入了RibbonClientConfigurationRegistrar
. RibbonClientConfigurationRegistrar
实现了ImportBeanDefinitionRegistrar
接口, 在@Configuration
的解析极端调用接口的registerBeanDefinitions
方法, 为ribbon客户端创建BeanDefinition
使用name/value
和configuration
创建一个BeanDefinition
. Definition的名为<name>.RibbonClientSpecification
, class为RibbonClientSpecification
.
FooConfiguration.class
也要使用@Configuration
注解, 然后通过RibbonClientConfigurationRegistrar
关联到Ribbon客户端的BeanDefinition. 所以不能把FooConfiguration放到@ComponentScan的上下文中, 同样@SpringBootApplication也不行. 必要时使用exclude排除, 否则会变成所有Ribbon客户端共享.
RibbonAutoConfiguration
中在创建SpringClientFactory
bean时, 会注入这些RibbonClientSpecification
. SpringClientFactory
继承了类NamedContextFactory
. 从注释看NamedContextFactory
可以创建一组子上下文, 每个子上下文中可以使用一组的Specification来定义bean. 对于Ribbon来说, 每个ribbon客户端各自为一个子上下文, @RibbonClient
的configuration
指定的配置, 就是用来构建该子上下文的配置, 最终被用来构建ribbon客户端. 这些上下文有共同的父上下文, 即ApplicationContext
. 这就是为什么上面提到的FooConfiguration
不能置于ApplicationContext
中, 否则会被所有的Ribbon客户端共享配置.
LoadBalancerAutoConfiguration配置类
通过RibbonAutoConfiguration
引入, 定义了几个重要的bean:
-
LoadBalancerRequestFactory
: 1) 将Http请求封装成ServiceRequestWrapper
.ServiceRequestWrapper
继承并重写了HttpRquestWrapper
的getURI
方法: 调用LoadBalancerClient
的reconstructURI
方法,创建实际请求的地址. 2) 如果有提供LoadBalancerRequestTransformer
的实例, 则使用这些实例对相求进行响应的转换. -
LoadBalancerInterceptor
: Http请求拦截器, 将请求的host
作为serviceName
并使用LoadBalancerRequestFactory
封装请求, 调用LoadBalancerClient
的execute
方法, 发送请求到真实的服务实例地址, 返回响应 -
RestTemplateCustomizer
: 提供一个RestTemplateCustomizer
的匿名类实现, 为所有的RestTemplate实例添加一个LoadBalancerInterceptor
拦截器
RibbonAutoConfiguration配置类
通过spring.factories引入, RibbonAutoConfiguration
定义了几个重要的bean:
-
SpringClientFactory
: 使用@RibbonClient
注解引入的ribbon客户端的配置, 构建ribbon客户端的子上下文, 初始化ribbon客户端bean. 四个get方法, 分别返回对应service的IClient
,ILoadBalancer
,IClientConfig
,RibbonLoadBalancerContext
实例. -
LoadBalancerClient
: 使用Spring Cloud提供的实现RibbonLoadBalancerClient
. 通过SpringClientFactory
创建一个ILoadBalancer
实例, 通过ILoadBalancer
返回一个Server
实例. 使用Server
实例.-
reconstructURI(): 通过
SpringClientFactory
获取该服务ribbon客户端子上下文RibbonLoadBalancerContext
对象, 调用RibbonLoadBalancerContext
的reconstructURIWithServer
方法构建最终的请求地址 -
choose(): 通过
SpringClientFactory
获取该服务的服务均衡器, 使用负载均衡器的IRule
返回服务实例. -
execute(): 执行最终的请求, 并记录状态:
ServerStats
和Stopwatch
-