完整流程图
Ribbon源码分析.png调用流程图
Ribbon调用流程总图.png与feign的结合
Feign源码分析.png源码具体流程
1.负载均衡器是从EurekaClient获取服务信息
->com.netflix.loadbalancer.DynamicServerListLoadBalancer#initWithNiwsConfig
->com.netflix.loadbalancer.DynamicServerListLoadBalancer#restOfInit
->com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateListOfServers
->com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList#getUpdatedListOfServers
->com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList#obtainServersViaDiscovery
//EurekaClient eurekaClient = eurekaClientProvider.get();
//List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
2.并根据IRule去路由
public String choose(Object key) {
if (rule == null) {
return null;
} else {
try {
Server svr = rule.choose(key);
return ((svr == null) ? null : svr.getId());
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server", name, e);
return null;
}
}
}
详解
ServiceInstanceChooser
主要有一个方法,用来根据serviceId来获取ServiceInstance
ServiceInstance choose(String serviceId);
LoadBalancerClient
有如下三个方法,其中excute()为执行请求,reconstructURI()用来重构url:
public interface LoadBalancerClient extends ServiceInstanceChooser {
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}
RibbonLoadBalancerClient
最终的负载均衡的请求处理,由它来执行
public class RibbonLoadBalancerClient implements LoadBalancerClient {
...//省略代码
@Override
public ServiceInstance choose(String serviceId) {
Server server = getServer(serviceId);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
protected Server getServer(String serviceId) {
return getServer(getLoadBalancer(serviceId));
}
protected Server getServer(ILoadBalancer loadBalancer) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
...//省略代码
最终交给了ILoadBalancer类去选择服务实例。
public interface ILoadBalancer {
//添加一个Server集合
public void addServers(List<Server> newServers);
//根据key去获取Server
public Server chooseServer(Object key);
//标记某个服务下线
public void markServerDown(Server server);
//取可用的Server集合
public List<Server> getReachableServers();
//获取所有的Server集合
public List<Server> getAllServers();
}
ILoadBalancer实现类为DynamicServerListLoadBalancer
构造函数如下 主要看IRule,ServerList,ServerListFilter
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList<T> serverList, ServerListFilter<T> filter)
IRule用于复杂均衡的策略
public interface IRule{
//根据key 来获取server,
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
IRule有很多默认的实现类,这些实现类根据不同的算法和逻辑来处理负载均衡。
BestAvailableRule 选择最小请求数
ClientConfigEnabledRoundRobinRule 轮询
RandomRule 随机选择一个server
RoundRobinRule 轮询选择server
RetryRule 根据轮询的方式重试
WeightedResponseTimeRule根据响应时间去分配一个weight ,weight越低,被选择的可能性就越低
ZoneAvoidanceRule根据server的zone区域和可用性来轮询选择
ServerList是定义获取所有的server的注册列表信息的接口
public interface ServerList<T extends Server> {
public List<T> getInitialListOfServers();
public List<T> getUpdatedListOfServers();
}
ServerListFilter接口
可根据配置去过滤或者根据特性动态获取符合条件的server列表的方法
public interface ServerListFilter<T extends Server> {
public List<T> getFilteredListOfServers(List<T> servers);
}
RestTemplate是如何和Ribbon结合的
查看LoadBalancerAutoConfiguration类
@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) {
for (RestTemplateCustomizer customizer : customizers) {
//给RestTemplate增加拦截器LoadBalancerInterceptor
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 ribbonInterceptor(
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);
};
}
}
/**
* Auto configuration for retry mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {
};
}
}
/**
* Auto configuration for retry intercepting mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRetryProperties properties,
LoadBalancerRequestFactory requestFactory,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
requestFactory, loadBalancedRetryFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
LoadBalancerInterceptor
用于实时拦截,在LoadBalancerInterceptor这里实现来负载均衡
@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));
}
小结
Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPing等信息,并向EurekaClient获取注册列表的信息,并默认10秒一次向EurekaClient发送“ping”,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡。
而RestTemplate 被@LoadBalance注解后,能过用负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给列表中的RestTemplate添加拦截器,进而交给负载均衡器去处理。
网友评论