Ribbon运行时Bean做了命名空间隔离,给每个服务方定义一个独立的bean上下文。这样做是为了支持对某一个服务单独配置负载。比如负载算法,是否重试等,也可以为每一个服务实例化一个服务发现逻辑,可以最大化地将自由交给实现方。
核心领域对象
-
SpringClientFactory是Ribbon全局唯一的bean工厂,只有一个实例。通过getInstances(String name, Class type)可以创建不同的命名空间(name)的不同类型对象(type)实例。
-
对@FeignClient声明的服务而言,每一个name都有一个FeignLoadBalancer实例。FeignLoadBalancer通过DynamicServerListLoadBalancer执行具体的负载均衡工作。
-
DynamicServerListLoadBalancer承载了Ribbon运行时负载均衡的全部逻辑,包括负载选择,重试等等。
-
ConsulServerList是一个逻辑含义的服务,指向一组具有相同业务功能的物理服务。是负载均衡选择的基础。
-
ConsulServer是一个物理含义的服务,指向服务的一个具体的运行实例。
bean注册
Ribbon注册的引入链为
RibbonAutoConfiguration
=> SpringClientFactory
=> @RibbonClients
=> RibbonClientConfigurationRegistrar
SpringClientFactory懒加载
Ribbon运行时依赖的类并没有在spring Application启动时初始化,而是在使用时懒加载。在需要时,SpringClientFactory基于RibbonClientConfiguration创建对应的bean实例。SpringClientFactory继承了NamedContextFactory。
Feign运行时,SynchronousMethodHandler.executeAndDecode(RequestTemplate)调用LoadBalancerFeignClient实现负载均衡。分析LoadBalancerFeignClient代码可以看出懒加载的过程:
//LoadBalancerFeignClient
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
//1. 懒加载IClientConfig
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName) //2. 懒加载FeignLoadBalancer
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
- ribbon追求为每个服务独立配置负载均衡策略,所以每个服务都有自己的IClientConfig实例。
- 从CachingSpringLoadBalancerFactory获取FeignLoadBalancer,没有会从SpringClientFactory创建。
SpringClientFactory创建ILoadBalancer时调用堆栈:
RibbonClientConfiguration.ribbonLoadBalancer(IClientConfig,ServerList,ServerListFilter,IRule,IPing,ServerListUpdater)
ZoneAwareLoadBalancer.<init>(IClientConfig,IRule,IPing,ServerList,ServerListFilter,ServerListUpdater)
DynamicServerListLoadBalancer.<init>(IClientConfig,IRule,IPing,ServerList,ServerListFilter,ServerListUpdater)
ConsulServerList.getUpdatedListOfServers()
ConsulServerList.getServers()
ConsulClient.getHealthServices(String,String,boolean,QueryParams,String)
HealthConsulClient.getHealthServices(String,String,boolean,QueryParams,String)
ConsulRawClient.makeGetRequest(String,UrlParameters[])
在创建DynamicServerListLoadBalancer时,Ribbon会调用consul server查询service的实例信息。同时创建心跳检测ServerListUpdater。
运行时调用
DynamicServerListLoadBalancer在运行时执行负载均衡策略。
负载均衡策略
负载均衡策略由IRule执行。
Ribbon 进行客户端负载均衡时,如果被调用方起了多个实例,对于本地调试很麻烦。解决方法:在 bootstrap.properties 文件中加入以下配置:
spring.cloud.consul.ribbon.enabled=false
示例:
hello-world.ribbon.listOfServers=192.168.26.183:8777
{替换成需要调用的服务名}.ribbon.listOfServers={替换成需要调用服务的IP:端口号}
健康检查
PollingServerListUpdater定期查询consul server检查service的各实例健康情况。
Ribbon和Spring Cloud Consul: http://blog.sina.com.cn/s/blog_72ef7bea0102xft7.html