Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法。
SpringCloud通过Ribbon对服务消费方获取合适的服务提供方地址。
使用@LoadBalanced注解+RestTemplate实现调用。
RestTemplate类中存在List<ClientHttpRequestInterceptor> interceptors = new ArrayList();
Ribbon作用:
在消费方调用服务方之间加了一层拦截器,通过此拦截器来决定消费哪个服务提供方服务。
Ribbon定义了LoadBalancerInterceptor拦截器来实现服务负载
通过LoadBalancerInterceptorConfig来对RestTemplate进行添加拦截器。
@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(final LoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
拦截器内部逻辑
执行LoadBalancerInterceptor的intercept方法
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));
}
执行excute方法:
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
Server server = this.getServer(loadBalancer, hint);
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, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
执行getServer获取负载之后的服务提供方地址
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}
执行chooseServer决定具体提供方
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;
}
}
}
此处根据约定的rule规则决定如何获取提供方地址(默认是轮询规则)
Rbbion提供了负载均衡算法
1.RandomRule 随机
2.AvailabilityFilteringRule 会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
3.BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
4.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。
5.RoundRobinRule 轮询
6.WeightedResponseTimeRule():根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换到WeightedResponseTimeRule
7.ZoneAvoidanceRule():默认规则,符合判断server所在区域的性能和server的可用性选择服务器
此处以随机为例
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
// 获取可用的服务
List<Server> upList = lb.getReachableServers();
// 获取全部服务
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int index = this.chooseRandomInt(serverCount);
server = (Server)upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
自定义Rule规则获取服务提供方
自定义Ribbon负载均衡规则,当服务数大于等于5时,按照随机规则,小于5时按照轮询规则
public class NewRule extends AbstractLoadBalancerRule {
private static final RandomRule randomRule = new RandomRule();
private static final RoundRobinRule roundRobinRule = new RoundRobinRule();
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
private Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
//如果服务总数大于等于5,采用随机
//如果服务总数小于5,采用轮询
if (serverCount < 5) {
server = roundRobinRule.choose(lb,key);
} else {
server = randomRule.choose(lb,key);
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
}
将规则交由Spring管理
@Configuration
public class RuleConfig {
@Bean
public NewRule newRule(){
return new NewRule();
}
}
使用@RibbonClient对客户端指定规则
@RibbonClient(name = "consumer" ,configuration = NewRule.class)