Spring Cloud Ribbon源码分析
一、ILoadBalancer
Ribbon实现负载均衡的核心接口为ILoadBalancer
public interface ILoadBalancer {
//添加后端服务
public void addServers(List<Server> newServers);
//选择一个后端服务
public Server chooseServer(Object key);
//标记一个服务不可用
public void markServerDown(Server server);
//获取当前可用的服务列表
public List<Server> getReachableServers();
//获取所有后端服务列表
public List<Server> getAllServers();
}
ILoadBalancer 接口的类层结构如下所示:
核心功能实现在BaseLoadBalancer类中,核心组件有IRule、IPing和LoadBalancerStats等。
1. IRule
IRule 接口是对负载均衡策略的一种抽象,可以通过实现这个接口来提供各种适用的负载均衡算法
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
choose 方法是该接口的核心方法,根据key从指定服务列表中获取对应的服务
2. IPing
IPing 接口判断目标服务是否存活,定义如下:
public interface IPing {
public boolean isAlive(Server server);
}
可以看到 IPing 接口中只有一个 isAlive() 方法,通过对服务发出"Ping"操作来获取服务响应,从而判断该服务是否可用。
3. LoadBalancerStats
LoadBalancerStats 类记录负载均衡的实时运行信息,用来作为负载均衡策略的运行时输入。
ILoadBalancer接口中的chooseServer方法是实现负载均衡的核心方法,BaseLoadBalancer定义的chooseServer方法实现如下:
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;
}
}
}
这里使用了 IRule 接口的 choose 方法选择服务信息,负载均衡算法可以分成两大类,即**静态负载均衡算法**和**动态负载均衡算法**。静态负载均衡算法比较容易理解和实现,典型的包括随机(Random)、轮询(Round Robin)和加权轮询(Weighted Round Robin)算法等。典型动态算法包括源 IP 哈希算法、最少连接数算法、服务调用时延算法等。
二、负载均衡策略
Ribbon实现的负载均衡策略如下图所示:
![watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA5oCd57u055qE5rex5bqm_size_20_color_FFFFFF_t_70_g_se_x_16 1][]
RandomRule:随机策略
RoundRobbinRule:轮询策略
RetryRule:请求失败的重试策略,属于容错机制
WeightedResponseTimeRule:该策略与请求的响应时间有关,显然,如果响应时间越长,就代表这个服务的响应能力越有限,那么分配给该服务的权重就应该越小。
BestAvailableRule :选择一个并发请求量最小的服务器,逐个考察服务器然后选择其中活跃请求数最小的服务器。
AvailabilityFilteringRule:通过检查 LoadBalancerStats 中记录的各个服务器的运行状态,过滤掉那些处于一直连接失败或处于高并发状态的后端服务器。
三、Spring Cloud Netflix Ribbon
Spring Cloud Netflix Ribbon组件在Netflix Ribbon组件上进行封装和集成,调用关系如下:
- @LoadBalanced 注解
为什么通过 @LoadBalanced 注解创建的 RestTemplate 就能自动具备客户端负载均衡的能力?
在 Spring Cloud Netflix Ribbon 中存在一个自动配置类——LoadBalancerAutoConfiguration 类。
LoadBalancerAutoConfiguration类的核心功能如下:
- 创建一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截, 以实现客户端负载均衡
- 创建一个RestTemplateCustomizer的Bean,用于给RestTemplate增加拦截器
- 维护了一个被@LoadBalanced 注解修饰的RestTemplate对象列表, 并在这里进行初始化, 通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerinterceptor拦截器。
在LoadBalancerAutoConfiguration 类中,维护着一个被 @LoadBalanced 修饰的 RestTemplate 对象的列表。在初始化的过程中,对于所有被 @LoadBalanced 注解修饰的 RestTemplate,调用 RestTemplateCustomizer 的 customize 方法进行定制化,该定制化的过程就是对目标 RestTemplate 增加拦截器 LoadBalancerInterceptor,如下所示:
@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);
};
}
}
这个 LoadBalancerInterceptor 用于实时拦截,可以看到它的构造函数中传入了一个对象 LoadBalancerClient,而在它的拦截方法本质上就是使用 LoadBalanceClient 来执行真正的负载均衡。LoadBalancerInterceptor 类代码如下所示:
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,
this.requestFactory.createRequest(request, body, execution));
}
}
可以看到这里的拦截方法 intercept 直接调用了 LoadBalancerClient 的 execute 方法完成对请求的负载均衡执行
LoadBalanceClient 接口
public interface LoadBalancerClient extends ServiceInstanceChooser {
T execute(String serviceId, LoadBalancerRequest request) throws IOException; T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}这里有两个 execute 重载方法,用于根据负载均衡器所确定的服务实例来执行服务调用。而 reconstructURI 方法则用于构建服务 URI,使用负载均衡所选择的 ServiceInstance 信息重新构造访问 URI,也就是用服务实例的 host 和 port 再加上服务的端点路径来构造一个真正可供访问的服务。
LoadBalancerClient接口的实现类有RibbonLoadBalancerClient和BlockingLoadBalancerClient,在RibbonLoadBalancerClient类中choose方法
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));
}
choose方法最终调用getServer获取服务
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
这里的 loadBalancer 对象就是前面介绍的 Netflix Ribbon 中的 ILoadBalancer 接口的实现类。这样,我们就把 Spring Cloud Netflix Ribbon 与 Netflix Ribbon 的整体协作流程串联起来。
还没有评论,来说两句吧...