Ribbon是Netflix发布的云中间层服务开源项目,其主要功能是提供客户端实现负载均衡算法。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中Load Balancer后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。
请求会先被LoadBalancerInterceptor(拦截器拦截),获取服务,将服务再委托给LoadBalancerClient,LoadBalancerClient在初始化的时候,会将服务委托给ILoadBalance(负载均衡器),ILoadBalance根据服务信息向Eureka注册中心获取该服务注册列表,并且每10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则从注册中心更新或者重新拉取,再根据具体的IRule来进行负载均衡,来获取调用的服务,再将请求进行封装,最后在将请求发送到指定服务。
1.RibbonAutoConfiguration
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
@Autowired(required = false)
private List configurations = new ArrayList<>();
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
@Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature("Ribbon", Ribbon.class);
}
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
}
通过源码分析,启动类启动时会注入LoadBalanceClient(负载均衡客户端)
2.LoadBalancerAutoConfiguration
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
通过源码分析,启动类启动时会注入LoadBalancerInterceptor (拦截器)
3.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) {
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
//拦截请求
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()
loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
RibbonLoadBalancerClient 实现LoadBalancerClient
public class RibbonLoadBalancerClient implements LoadBalancerClient
@Override
public T execute(String serviceId, LoadBalancerRequest request)
throws IOException {
return execute(serviceId, request, null);
}
RibbonLoadBalancerClient将服务信息委托给ILoadBalancer
public T execute(String serviceId, LoadBalancerRequest 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);
}
ILoadBalancer 接口
public interface ILoadBalancer {
void addServers(List var1);
//返回一个指定的服务
Server chooseServer(Object var1);
void markServerDown(Server var1);
/** @deprecated */
@Deprecated
List getServerList(boolean var1);
List getReachableServers();
List getAllServers();
}
最终获取指定的服务是通过IRule的choose方法(通过指定的负载均衡策略)
this.rule.choose(key)
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;
}
}
}
IRule 接口
public interface IRule {
Server choose(Object var1);
void setLoadBalancer(ILoadBalancer var1);
ILoadBalancer getLoadBalancer();
}
核心代码:
int index = rand.nextInt(serverCount); // 使用jdk内部的Random类随机获取索引值index
server = upList.get(index); // 得到服务器实例
表示每次都取下一个服务器,如果失败会继续向下尝试获取10次
开始的时候还没有权重列表,采用父类的轮询方式,有一个默认每30秒更新一次权重列表的定时任务,该定时任务会根据实例的响应时间来更新权重列表,choose方法做的事情就是,用一个(0,1)的随机double数乘以最大的权重得到randomWeight,然后遍历权重列表,找出第一个比randomWeight大的实例下标,然后返回该实例
上面介绍了ribbon和几种负载均衡策略底层源码实现,下面来讲解ribbon结果负载均衡的使用
添加@LoadBalanced注解
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
使用RestTemplate进行rest操作的时候,会自动使用负载均衡策略,它内部会在RestTemplate中加入LoadBalancerInterceptor这个拦截器,这个拦截器的作用就是使用负载均衡。
@Autowired
LoadBalancerClient loadBalancerClient;
//从该服务中获取url列表
ServiceInstance serviceInstance = loadBalancerClient.choose(“spring-cloud-product”);
String url=String.format(“http://%s:%s”,serviceInstance.getHost(),serviceInstance.getPort()+"/way");
return restTemplate.getForObject(url,String.class);
# HELLO-SERVICE是服务应用名
HELLO-SERVICE.ribbon.NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
2.修改默认的负载均衡策略(默认轮询)(所有服务有效)
// 定义负载均衡策略
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
第一步:继承AbstractLoadBalancerRule类
第二步:实现方法
public class GPDefined extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
public Server choose(ILoadBalancer lb,Object o) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
//获取存活的实例
List upList = lb.getReachableServers();
//获取所有实例
List 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;
}
}
@Override
public Server choose(Object o) {
return choose(this.getLoadBalancer(),o);
}
}
第三步:
注入类实例
@Bean
public IRule ribbonRule() {
return new 自定义的类();//自定义负载策略类
}