Ribbon的核心工作原理
接口 | 描述 | 默认实现类 |
---|---|---|
IClientConfig | 定义Ribbon管理配置得接口 | DefaultClientConfigImpl |
IRule | 定义Ribbon中负载均衡策略的接口 | ZoneAvoidanceRule |
IPing | 定义顶起ping服务检查可用性的接口 | DummyPing |
ServerList | 定义获取服务列表方法的接口 | ConfigurationBasedServerList |
ServerListFilter | 定义特定期望获取服务列表方法的接口 | ZonePreferenceServerListFilter |
ILoadBalancer | 定义负载均衡选择服务的核心老方法的接口 | ZoneAwareLoadBalancer |
ServerListUpdater | 为DynamicServerListLoadBalance定义动态更新服务猎鸟的接口 | PollingServerListUpdater |
ribbon的能够起作用都是基于以上的接口的支撑,ribbon的负载均衡是如何做到的呢?在之前的例子中我们已经使用到了RestTemplate进行负载均衡的请求,加了@LoadBalanced的注解之后就可以实现负载均衡。注解的代码如下:
/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
复制代码
根据该注解的注释可以知道,标志了该注解的RestTemplate是负载均衡的客户端,从代码可以看到该注解是一个接口,该接口的具体实现类是RibbonLoadBalancerClient,初次之外还拓展了ServiceInstanceChooser接口
-
LoadBalancerClient.java
/** * Represents a client side load balancer * @author Spencer Gibb */ public interface LoadBalancerClient extends ServiceInstanceChooser {
T execute(String serviceId, LoadBalancerRequest request) throws IOException; T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original); } 复制代码 -
ServiceInstanceChooser.java
public interface ServiceInstanceChooser {
/** * Choose a ServiceInstance from the LoadBalancer for the specified service * @param serviceId the service id to look up the LoadBalancer * @return a ServiceInstance that matches the serviceId */ ServiceInstance choose(String serviceId); 复制代码
}
-
ServiceInstance choose(String serviceId):该方法主要是根据负载均衡器选择一个服务实例。
-
T execute(String serviceId, LoadBalancerRequest request) throws IOException
-
T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException:2.3这两个接口主要是为负载均衡选怎的服务执行请求,构建一个服务,3是2的具体实现。、
-
URI reconstructURI(ServiceInstance instance, URI original):构建一个url,该方法主要使用具有逻辑服务名称的作为URI座位host。使用主机ip和port构建特定的URI提供给ribbon使用。
上面的接口是很重要的,这些接口主要在以下的java类内部实现:
@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(
required = false
)
private List restTemplates = Collections.emptyList();
@Autowired(
required = false
)
private List transformers = Collections.emptyList();
public LoadBalancerAutoConfiguration() {
}
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(ObjectProvider> restTemplateCustomizers) {
return () -> {
restTemplateCustomizers.ifAvailable((customizers) -> {
Iterator var2 = this.restTemplates.iterator();
while(var2.hasNext()) {
RestTemplate restTemplate = (RestTemplate)var2.next();
Iterator var4 = customizers.iterator();
while(var4.hasNext()) {
RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
customizer.customize(restTemplate);
}
}
});
};
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
@Configuration
@ConditionalOnClass({RetryTemplate.class})
public static class RetryInterceptorAutoConfiguration {
public 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(RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
@Configuration
@ConditionalOnClass({RetryTemplate.class})
public static class RetryAutoConfiguration {
public RetryAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {
};
}
}
@Configuration
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
static class LoadBalancerInterceptorConfig {
LoadBalancerInterceptorConfig() {
}
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(LoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
复制代码
从该类的注解可以发现当前类的加载时机必须存在restTemplate类,工程环境中必须初始化了LoadBalancerClient的实现类。整个配置类只截取了重要的部分,其中,LoadBalancerRequestFactory用于创建LoadBalancerRequest供LoadBalancerInterceptor使用。LoadBalacerInterceptorConfig中维护了LoadBalacerInterceptor雨RestTemplateCustomizer的实例。
-
LoadBalacerInterceptor:拦截每一次HTTP请求,将请求绑定进Ribbon负载均衡生命周期。
-
RestTemplateCustomizer:为每个RestTemplate绑定LoadBalacerInterceptor拦截器,这个也是重要的部分,其源码如下:
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) { // for backwards compatibility 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(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); } 复制代码
}
public
T execute(String serviceId, LoadBalancerRequest request) throws IOException { ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); Server server = this.getServer(loadBalancer); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } else { RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server)); return this.execute(serviceId, ribbonServer, request); } } 复制代码
首先要得到一个ILoadBalancer,用它去得到一个Server(该server就是服务实例的封装体,也是实现负载均衡的地方)。具体的代码如下:
protected Server getServer(ILoadBalancer loadBalancer) {
return loadBalancer == null ? null : loadBalancer.chooseServer("default");
}
复制代码
查看负载均衡的方法可以看大其源码如下:
public Server chooseServer(Object key) {
if (this.counter == null) {
this.counter = this.createCounter();
}
this.counter.increment();
if (this.rule == null) {
return null;
} else {
try {
return this.rule.choose(key);
} catch (Exception var3) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});
return null;
}
}
}
复制代码
这个地方的this.rule.choose(key)其实就是我们定义的IRule,实现了HTTP请求与负载均衡的结合。
负载均衡的策略的类继承图
Ribbon是通过ILoadBalancer来关联IRule的,ILoadBalancer的chooserServer方法会转换为掉牙IRule的choose方法,抽象类AbstractLoadBalancerRule实现了这两个方法,实现了ILoadBalancer与IRule关联。