Netflix Ribbon、Spring Cloud Netflix Ribbon、应用服务这三者之间的关系以及核心入口如下所示:
Netflix Ribbon 基本架构
作为一款客户端负载均衡工具,要做的事情无非就是两件:第一件事情是获取注册中心中的服务器列表;第二件事情是在这个服务列表中选择一个服务进行调用。针对这两个问题,Netflix Ribbon 提供了自身的一套基本架构,并抽象了一批核心类:
Netflix Ribbon 的核心接口 ILoadBalancer 就是围绕着上述两个问题来设计的
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();
}
负载均衡的核心功能基本都由BaseLoadBalancer这个类来实现,BaseLoadBalancer 包含的作为一个负载均衡器应该具备的一些核心组件,比较重要的有以下三个:
1.IRule
IRule接口是对负载均衡的一种抽象,可以实现这个接口来实现负载均衡的各种算法
public interface IRule{
// 核心方法
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
2.IPing
IPing 接口判断目标服务是否存活,定义如下
//定义我们如何“ ping”服务器以检查其是否存在的接口
public interface IPing {
//检查给定Server是否为“活动”,即在负载平衡时应被视为候选Server
public boolean isAlive(Server server);
}
只有一个isAlice()方法,通过ping来测试目标服务器是否可以用。
3.LoadBalancerStats(stats 统计)
记录统计负载均衡实时运行信息数据,来为负载均衡策略做数据支撑。
在BaseLoadBancer内部维护这AllServerList和UpServerList两个线程安全的列表,所以对ILoadBance的addServers、getReachableServers、getAllServers\接口的方法而言主要是维护和管理者两个列表.
public void addServers(List<Server> newServers) {
if (newServers != null && newServers.size() > 0) {
try {
ArrayList<Server> newList = new ArrayList<Server>();
newList.addAll(allServerList);
newList.addAll(newServers);
setServersList(newList);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Exception while adding Servers", name, e);
}
}
}
处理过程是将原来的服务实例列表allServerList和新增加的服务实例列表newServers都合并到newList列表里面。再调用setServersList方法覆盖原有的服务实例列表。
Netflix Ribbon 中的负载均衡策略
负载均衡算法可以分成两大类,即静态负载均衡算法和动态负载均衡算法。静态负载均衡算法比较容易理解和实现,典型的包括随机(Random)、轮询(Round Robin)和加权轮询(Weighted Round Robin)算法等。所有涉及权重的静态算法都可以转变为动态算法,因为权重可以在运行过程中动态更新
动态策略主要有:
1.BestAvailableRule 策略
选择活跃请求数量最小的服务器;
2.WeightedResponseTimeRule 策略
响应时间越长,动态分配的权重越小。权重计算依赖于ILoadBalancer 接口中的LoadBalancerStats为每个服务更新权重。
3.AvailabilityFilteringRule 策略
通过LoadBalancerStats 记录服务器的运行状态,过滤掉一直处于连接失败,一直处于高并发状态的服务器。
@LoadBalanced 一个注解是怎样实现负载均衡
在 Spring Cloud Netflix Ribbon 中存在一个自动配置类——LoadBalancerAutoConfiguration 类。在该类中,维护着一个被 @LoadBalanced 修饰的 RestTemplate 对象的列表。在初始化的过程中,对于所有被 @LoadBalanced 注解修饰的 RestTemplate,调用 RestTemplateCustomizer 的 customize 方法进行定制化,该定制化的过程就是对目标 RestTemplate 增加拦截器 LoadBalancerInterceptor,如下所示:
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
拦截方法调用了LoadBalancerClient的execute方法完成对负载均衡的执行。
LoadBalancerClient接口代码如下:
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);
}
对于负载均衡重点是choose方法,如下两个就是实现了LoadBalancerClient接口的负载均衡
RibbonLoadBalancerClient类实现choose方法代码如下
public ServiceInstance choose(String serviceId) {
return this.choose(serviceId, (Object)null);
}
public ServiceInstance choose(String serviceId, Object hint) {
Server server = this.getServer(this.getLoadBalancer(serviceId), hint);
return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
}