Spring Cloud - Ribbon


依然 是 依赖于 Spring-Ioc 和自动配置的方式,实现 客户端负载均衡器的初始化配置. 由于 代码量多,层次关联复杂.因此选择从核心类开始阅读

N : Netflix 的设计的类结构

SN : Spring 集成 Netflix 设计的类结构

S: Spring 设计的类结构

S- LoadBalancerClient :负载均衡器

public interface LoadBalancerClient {
    // 根据 serviceId 获取 ServiceInstance
    ServiceInstance choose(String serviceId);
    // 执行LoadBalancerRequest
     T execute(String serviceId, LoadBalancerRequest request) throws IOException;
    // 根据ServiceInstance,原始 URI,拼接出 目标URI
    URI reconstructURI(ServiceInstance instance, URI original);
}

SN- RibbonLoadBalancerClient : 基于 Ribbon实现的LoadBalancerClient

@Override
    public  T execute(String serviceId, LoadBalancerRequest request) throws IOException {
        // 根据 serviceId 获取 ILoadBalancer
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        // 获取 Server
        Server server = getServer(loadBalancer);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        }
        // 重新包装为RibbonServer
        RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
                serviceId), serverIntrospector(serviceId).getMetadata(server));
        // 获取RibbonLoadBalancerContext
        RibbonLoadBalancerContext context = this.clientFactory
                .getLoadBalancerContext(serviceId);
        //状态记录
        RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

        try {
            // 执行请求
            T returnVal = request.apply(ribbonServer);
            // 状态更新
            statsRecorder.recordStats(returnVal);
            return returnVal;
        }
        // catch IOException and rethrow so RestTemplate behaves correctly
        catch (IOException ex) {
            // 状态更新
            statsRecorder.recordStats(ex);
            throw ex;
        }
        catch (Exception ex) {
            // 状态更新
            statsRecorder.recordStats(ex);
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        return null;
    }

​ 可以观察到, 核心的负载均衡是由 ILoadBalancer实现的.通过SpringClientFactory 获取/创建 ILoadBalancer实现实例.

protected ILoadBalancer getLoadBalancer(String serviceId) {
   return this.clientFactory.getLoadBalancer(serviceId);
}

SN-SpringClientFactory:

用以 创建 Ribbon 的 负载均衡相关实例. 并 为每个"客户端名称" 创建一个 Spring ApplicationContext per,并从中提取需要的注入对象.(但它们公用一个父容器,可以拿到父容器中的 Bean 实例的引用).而 Spring 通过RibbonClientConfiguration创建 并使用了 ILoadBalancer的一个实现,名为ZoneAwareLoadBalancer.

到这里.差不多可以看出整体雏形了:

Spring Cloud 创建并注册 RibbonLoadBalancerClient. 在execute方法中, 委托给 Ribbon 的 ILoadBalancer获取 Server, 以实现负载均衡. 默认大家了解 Spring RestTemplate(封装了 HTTP 相关方法的模板类), 在ClientHttpRequestExecution调用的过程中, 将被ClientHttpRequestInterceptor 实现拦截功能.在LoadBalancerAutoConfiguration部分,可以看到 配置注册了一个名为LoadBalancerInterceptor的ClientHttpRequestInterceptor.

S-LoadBalancerInterceptor: 实现了客户端负载均衡的拦截器

@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, requestFactory.createRequest(request, body, execution));
}

也就是所有的@LoadBalanced注解的RestTemplate都会被拦截进入loadBalancer(RibbonLoadBalancerClient)并进行相应的执行.然后我们可以进行下一步了.

S-LoadBalancerRequest

public interface LoadBalancerRequest {

   public T apply(ServiceInstance instance) throws Exception;

}

最新版LoadBalancerRequest而它是由LoadBalancerRequestFactory生成.

public LoadBalancerRequest createRequest(final HttpRequest request,
      final byte[] body, final ClientHttpRequestExecution execution) {
   return new LoadBalancerRequest() {

      @Override
      public ClientHttpResponse apply(final ServiceInstance instance)
            throws Exception {
         // HttpRequest包转
         HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
         if (transformers != null) {
            for (LoadBalancerRequestTransformer transformer : transformers) {
              //
               serviceRequest = transformer.transformRequest(serviceRequest, instance);
            }
         }
         return execution.execute(serviceRequest, body);
      }

   };
}

就是如此 将 RestTemplate 变为支持负载均衡的RestTemplate.

Spring集成 Ribbon 的部分已经介绍好了.下面继续深入了解下 N- ILoadBalancer 负载均衡器.(好累啊!!!.)

public interface ILoadBalancer {
   // 添加服务
   public void addServers(List newServers);
   // 选择服务
   public Server chooseServer(Object key);
   // 标记为关闭的服务
   public void markServerDown(Server server);
   // 获取所有服务
   @Deprecated
   public List getServerList(boolean availableOnly);
   // 获取所有可用服务
   public List getReachableServers();
   // 获取所有服务
   public List getAllServers();
}

主要介绍下几个子类

AbstractLoadBalancer 作为ILoadBalancer的抽象.定义了 服务分类.

public abstract class AbstractLoadBalancer implements ILoadBalancer {

    public enum ServerGroup{
        ALL,
        STATUS_UP,
        STATUS_NOT_UP
    }
    public Server chooseServer() {
       return chooseServer(null);
    }
    public abstract List getServerList(ServerGroup serverGroup);

    public abstract LoadBalancerStats getLoadBalancerStats();
}

BaseLoadBalancer 作为基础类.主要功能有.

  1. 定义了 IRule 变量.实现 服务选择算法.

    1. RandomRule 实现了随机选择算法
    2. RoundRobinRule:实现了线性轮询算法
    3. RetryRule:实现了重试选择的算法
    4. WeightedResponseTimeRule:实现了更具服务状态,进行权重分析选择的算法.
    5. …..还有很多
  2. 定义了 IPingStrategy变量. 实现 Ping的策略.SerialPingStrategy为默认实现,线性顺序 Ping 操作.

  3. 定义了 IPing 变量.实现 服务是否活跃的判断.主要有:

    Spring Cloud - Ribbon_第1张图片
    F2EFE845-6BA2-4679-A6A5-5D84C84DC6FD
  4. 维护了 服务列表

DynamicServerListLoadBalancer.主要提供 服务状态动态更新的功能.

  1. ServerList 获取服务:

    DiscoveryEnabledNIWSServerList 就实现了通过 EurekaClient 对服务列表的获取. 源码不写了.有兴趣大家可以自己读

public interface ServerList {

    public List getInitialListOfServers();

    public List getUpdatedListOfServers();
}
  1. ServerListUpdater 控制当前服务列表的更新策略.

    1. PollingServerListUpdater 轮询方式 更新.
    2. DynamicServerListLoadBalancer: 客户端维护服务状态,并通过 控制状态改变事件的发布,实现 服务列表更新.

ZoneAwareLoadBalancer: 作为 DynamicServerListLoadBalancer的扩展,实现了关于 Zone 的概念,提高可用性.

参考资料
http://blog.didispace.com/springcloud-sourcecode-ribbon/ (感谢 DD)

你可能感兴趣的:(Spring Cloud - Ribbon)