Ribbon
Ribbon是SpringCloud中提供负载均衡策略的组件。
负载均衡中的角色
LoadBalancerInterceptor
对带有@LoadBalance
的RestTemplate
的http请求进行拦截。
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
}
它的工作内容大概如下:
- 拦截请求,通过
getHost
拿到请求的服务名(还不是实际的IP地址+端口,只是服务名) - 调用
LoadBalancerClient
执行对应的请求
LoadBalancerClient
代表一个具有负载均衡功能的客户端,继承了ServiceInstanceChooser
,所以具有在多个服务实例中选择一个的能力。
上面的所有内容都是SpringCloud提供的通用API,下面的组件多是
Ribbon
中提供的实现组件
如果使用Ribbon
负载均衡器,那么Ribbon
提供了RibbonLoadBalancerClient
,并且,它的execute
方法如下:
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
- 获取一个LoadBalancer实例
- 通过LoadBalancer实例,拿到实际要调用的服务器
- 实际执行对服务器的调用
ILoadBalancer
代表软件负载均衡器的一个接口,它需要一组进行负载均衡的服务器,具有将某个服务器标记为下线的方法,具有选择一个服务器的功能。
通过打断点,发现实际上是DynamicServerListLoadBalancer
这个实现类在工作,它是一种支持服务器列表在运行时动态改变的负载均衡器,它继承自BaseLoadBalancer
,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) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
- 计数,提供服务的总数
- 调用
rule
进行实际的选择
可见,这个LoadBalancer
中将选择服务器的职能又委托给一种规则对象了,因为负载均衡算法不一样嘛,一个负载均衡器理应支持多种负载均衡算法。
IRule
IRule
就是实际的负载均衡算法的接口了,它的choose
方法用于使用它代表的负载均衡策略返回一个实际承受负载的服务器。该接口有如下实现类(不全):
可以在其中看到一些熟悉的名字,比如RoundRobin
是轮询算法,Random
是随机算法。
它的实现类AbstractLoadBalancerRule
中又有如下继承者:
其中有最佳可访问的策略,响应时间权重的策略等等,默认工作的是RoundRobinRule
,是一种轮询算法。常用的还有ZoneAvoidanceRule
,是一中考虑服务器区域和可访问性的轮询算法。
总结
切换负载均衡算法
定义IRule Bean
在配置文件中定义IRule
类型的Bean即可
@Bean
public IRule rule() {
return new RandomRule();
}
配置文件
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
饥饿加载
Ribbon的RibbonLoadBalancerClient
默认是懒加载的,即启动时不加载某个服务的具体服务器,只有在第一次访问时才加载,所以第一次访问会较慢。
如果想取消这种默认行为,可以打开饥饿加载,即启动时就加载对应服务的服务器列表:
ribbon:
eager-load:
enabled: true
clients: user-service