Ribbon原理你知道吗?

什么是Ribbon

Ribbon是它是Netflix发布的项目,它主要是为了给客户端提供软件负载均衡的算法跟服务的调用。Ribbon组件会提供了一系列的配置,像连接超时、重试等等。简单点来说就是在配置文件里列出Load Balancer后面的所有机子,接着Ribbon就开始自动的帮助你使用某一种规则来连接这一些机器。

架构原理

Ribbon它只是一个软负载均衡的客户端组件,它是可以和其他所需请求的客户端结合使用的,这里的Eureka只是一个示例。

Ribbon示例

@Bean
@LoadBalanced
RestTemplate restTemplate() {
    return new RestTemplate();
}

@Test
public void xdclassproductInfo() {
    String id = UUID.randomUUID().toString();
    String url = "http://xdclass-shop-product/product/info?id=" + id;
    Object result = this.restTemplate.getForObject(url, HashMap.class);
    System.out.println(JsonUtil.BeanToJson(result));
}
复制代码

如上所示,当一个请求被Ribbon代理之后,请求的执行流程是怎么样的呢?

LoadBalanced的原理

从上述的代码可以看到通过一个@LoadBalanced这个注解就可以实现RestTemplate请求出来的负载均衡,那么它的原理是怎么样的呢?

可以看到在Ribbon发送请求的时候,会被一个叫ClientHttpRequestInterceptor给拦截住,LoadBalancerInterceptor它是ClientHttpRequestInterceptor的实现类,它的作用就是负载均衡使用的,把负载均衡的逻辑交给了loadBalancer。

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, requestFactory.createRequest(request, body, execution));
}
复制代码

需要注意到的是这里的@LoadBalanced的这个注解是属于Spring,不是属于Ribbon的。在Spring进行初始化容器的时候,检测到Bean是被LoadBalanced注解的话,Spring是会设置成为LoadBalancerInterceptor拦截器的。

再看看LoadBalanced注解定义

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
复制代码

可以看到有一个类变量FIELD,跟parameter这个是在注解上就可以注释的,当然这个注解也被@Qualifier所修饰bean的对象。

动态的更新服务的实例

服务的实例上下线在我们微服务场景里其实是非常的常见,Ribbon也是实现了这个功能。在Ribbon的定时更新接口是ServerListUpdater。在Ribbon在注册中心获取到所有的服务实例列表之后,Ribbon这时候就需要动态的更新服务实例。另一种就是通过了事件通知的这种方式。

扫描二维码关注公众号,回复: 13783296 查看本文章

定时拉取

Ribbon会用定时任务线程来定时拉取更新的数据

int coresize = poolsizeProp.get();
ThreadFactory factory = (new ThreadFactoryBuilder())
.setNameFormat("PollingServerListUpdater-%d ").setDaemon(true)
.build();
serverListRefreshExecutor = new ScheduledThreadPoolExecutor(coreSize,factory);
复制代码

{service-name}.ribbon.ServerListRefreshInterval主要是更新的频率

DynamicServerListLoadBalancer.ThreadPoolSize定时更新的线程数目

@override
public synchronized void start(final UpdateAction updateAction){
    if (isActive.compareAndSet( expect: false, update: true)) {
       final Runnable wrapperRunnable = new Runnable(){
@override
public void run()i
    if (!isActive.get()){
      if (scheduledFuture != null) {
     scheduledFuture.cancel( mayInterruptlfRunning: true);
   }
    return;
  }
    try {
       updateAction.doUpdate();
       lastUpdated = System.currentTimeMillis();}catch (Exception e){
      logger.warn("Failed one update cycle",e);
 }
 };
      scheduledFuture = getRefreshExecutor().schedulewithFixedDelay(
      wrapperRunnable,
    initialDelayMs,refreshIntervalMs,TimeUnit.MILLISECONDS
  );
   }else {
     logger.info("Already active,no-op");
}
复制代码

PollingServerListUpdater它只是对线程池进行了控制,但是它的具体业务就封装在了UpdateAction了。

对服务进行心跳检测

在服务的列表当中,实例不一定一直都是可用的状态,Ribbon就会对服务来进行检测,PingerStrategy接口的检测策略,Ribbon默认就是使用串行的这种方式来进行检测的,也可以通过这个接口使用并行的检测方式。Pinger就会定时通过PingerStrategy来获取到更得多的服务实例,并且调用监听者。

//在线服务实例列表
final List<Server> newUpList = new ArrayList<Server>();
//发生状态变更的服务实例列表
final List<Server> changedServers = new ArrayList<Server>();

for (int i = 0; i < numCandidates; i++) {
    boolean isAlive = results[i];
    Server svr = allServers[i];
    boolean oldIsAlive = svr.isAlive();

    svr.setAlive(isAlive);

    if (oldIsAlive != isAlive) {
        changedServers.add(svr);
        logger.debug("LoadBalancer [{}]:  Server [{}] status changed to {}", 
            name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
    }

    if (isAlive) {
        newUpList.add(svr);
    }
}
复制代码

负载均衡调度器

从一个ServerListFilter获取到一个微服务的实例集合之后,ILoadBalancer就需要使用某一个策略从集合里选择一个服务实例。

public interface IRule{
    //省略一些方法
    public Server choose(Object key);

}
复制代码

选择服务实例之后,ILoadBalancer在调用过程中,会记录请求的执行结果,比如请求的失败成功情况,调用耗时等,IRule接口也可以根据这些信息决定是否使用某个Server

Ribbon的负载均衡策略

1、 RoundRobinRule(轮询)

2、RandomRule(随机策略)

3、BestAvailableRule(过滤出故障服务器后,选择一个并发量最小的)

4、WeightedResponseTimeRule(针对响应时间加权轮询)

5、AvailabilityFilteringRule(可用过滤策略,先过滤出故障的或并发请求大于阈值的一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个;)

6、ZoneAvoidanceRule(从最佳区域实例集合中选择一个最优性能的服务实例)

7、RetryRule(选择一个Server,如果失败,重新选择一个Server重试)

猜你喜欢

转载自juejin.im/post/7085174806479372296