集群模块简介

集群,是指同一个服务 被部署在了多个服务器上,每个服务器的任务都相同,能够以较高的性价比,提升系统的 性能、可靠性、灵活性,但同时也要面对 集群中会出现的 负载均衡、容错等问题。dubbo 的集群模块,主要涉及以下几部分内容。

  • 负载均衡策略:dubbo 支持的所有负载均衡策略算法;
  • 集群容错:Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个;
  • 路由:dubbo 路由规则,路由规则决定了一次 dubbo 服务调用的目标服务器,路由规则分两种:条件路由规则和脚本路由规则,并且支持可拓展;
  • 配置:根据 url 上的配置规则生成配置信息;
  • 分组聚合:合并返回结果;
  • 本地伪装:mock 通常用于服务降级,mock 只在非业务异常时执行,如 超时、网络异常等。

集群工作过程可分为两个阶段,第一个阶段是在消费者初始化期间,集群 Cluster 为消费者创建 ClusterInvoker 实例。第二个阶段是在消费者进行 RPC 时,以 FailoverClusterInvoker 为例,该实例首先会调用 Directory 的 list()方法 获取 Invoker 列表,然后根据配置的 负载均衡策略,从 Invoker 列表 中选择一个 Inovker,最后将参数传给选择出的 Invoker 实例 进行真正的远程调用。

可将上文中出现的 Invoker 简单理解为服务提供者,Directory 的用途是保存 Invoker 列表,实现类 RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Inovker 列表会随着注册中心内容的变化而变化。每次变化后,RegistryDirectory 会动态增删 Inovker,并调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker。

下面我们来看一下 集群模块的项目结构图,结合上文的描述,可以对其有更加深刻的理解。

image.png

集群模块核心 API 源码解析

从上图应该也能看出其核心 API 在哪个包里。

image.png

各核心接口的源码如下。

  1. /**
  2. * 集群接口
  3. */
  4. @SPI(FailoverCluster.NAME)
  5. public interface Cluster {
  6. /**
  7. * 将目录调用程序合并到虚拟调用程序。
  8. * 基于 Directory ,创建 Invoker 对象,实现统一、透明的 Invoker 调用过程
  9. *
  10. * @param directory Directory 对象
  11. * @param <T> 泛型
  12. * @return cluster invoker
  13. * @throws RpcException
  14. */
  15. @Adaptive
  16. <T> Invoker<T> join(Directory<T> directory) throws RpcException;
  17. }
  18. /**
  19. * Configurator 接口
  20. */
  21. public interface Configurator extends Comparable<Configurator> {
  22. /**
  23. * 配置规则,生成url
  24. */
  25. URL getUrl();
  26. /**
  27. * 把规则配置到URL中
  28. */
  29. URL configure(URL url);
  30. }
  31. @SPI
  32. public interface ConfiguratorFactory {
  33. /**
  34. * 获得 Configurator实例
  35. */
  36. @Adaptive("protocol")
  37. Configurator getConfigurator(URL url);
  38. }
  39. public interface Directory<T> extends Node {
  40. /**
  41. * 获得服务类型,例如:com.alibaba.dubbo.demo.DemoService
  42. */
  43. Class<T> getInterface();
  44. /**
  45. * 获得所有服务 Invoker 集合
  46. */
  47. List<Invoker<T>> list(Invocation invocation) throws RpcException;
  48. }
  49. @SPI(RandomLoadBalance.NAME)
  50. public interface LoadBalance {
  51. /**
  52. * 从 Invoker 集合中,选择一个合适的 Invoker
  53. */
  54. @Adaptive("loadbalance")
  55. <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
  56. }
  57. @SPI
  58. public interface Merger<T> {
  59. /**
  60. * 合并 T 数组,返回合并后的 T 对象
  61. *
  62. * @param items T 数组
  63. * @return T 对象
  64. */
  65. T merge(T... items);
  66. }
  67. /**
  68. * 路由规则接口
  69. */
  70. public interface Router extends Comparable<Router> {
  71. /**
  72. * 获得路由规则的url
  73. */
  74. URL getUrl();
  75. /**
  76. * 筛选出跟规则匹配的Invoker集合
  77. *
  78. * @param invokers Invoker 集合
  79. * @param url refer url
  80. * @param invocation
  81. * @return routed invokers 路由后的 Invoker 集合
  82. * @throws RpcException
  83. */
  84. <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
  85. }
  86. @SPI
  87. public interface RouterFactory {
  88. /**
  89. * 创建 Router 对象
  90. */
  91. @Adaptive("protocol")
  92. Router getRouter(URL url);
  93. }

dubbo 常见几种面试题:

  • dubbo 工作原理:服务注册、注册中心、消费者、代理通信、负载均衡;
  • 网络通信、序列化:dubbo 协议、长连接、NIO、hessian 序列化协议;
  • 负载均衡策略、集群容错策略、动态代理策略:dubbo 跑起来的时候一些功能是如何运转的?怎么做负载均衡?怎么做集群容错?怎么生成动态代理?
  • dubbo SPI 机制:你了解不了解 dubbo 的 SPI 机制?如何基于 SPI 机制对 dubbo 进行扩展?

    dubbo 负载均衡策略

    random loadbalance

    默认情况下,dubbo 是 random load balance ,即随机调用实现负载均衡,可以对 provider 不同实例设置不同的权重,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。

    roundrobin loadbalance

    这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不一样,容易导致性能差的机器负载过高。所以此时需要调整权重,让性能差的机器承载权重小一些,流量少一些。
    举个栗子。
    跟运维同学申请机器,有的时候,我们运气好,正好公司资源比较充足,刚刚有一批热气腾腾、刚刚做好的虚拟机新鲜出炉,配置都比较高:8 核 + 16G 机器,申请到 2 台。过了一段时间,我们感觉 2 台机器有点不太够,我就去找运维同学说,“哥儿们,你能不能再给我一台机器”,但是这时只剩下一台 4 核 + 8G 的机器。我要还是得要。
    这个时候,可以给两台 8 核 16G 的机器设置权重 4,给剩余 1 台 4 核 8G 的机器设置权重 2。

    leastactive loadbalance

    这个就是自动感知一下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给不活跃的性能差的机器更少的请求

consistanthash loadbalance

一致性 Hash 算法,相同参数的请求一定分发到一个 provider 上去,provider 挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。如果你需要的不是随机负载均衡,是要一类请求都到一个节点,那就走这个一致性 Hash 策略。

集群容错

failover cluster 模式

失败自动切换,自动重试其他机器,默认就是这个,常见于读操作。(失败重试其它机器)
可以通过以下几种方式配置重试次数:

<dubbo:service retries="2" />

py to clipboardErrorCopied
或者

<dubbo:reference retries="2" />

py to clipboardErrorCopied
或者

<dubbo:reference>     <dubbo:method name="findFoo" retries="2" /> </dubbo:reference>

failfast cluster 模式

一次调用失败就立即失败,常见于非幂等性的写操作,比如新增一条记录(调用失败就立即失败)

failsafe cluster 模式

出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。

配置示例如下:

<dubbo:service cluster="failsafe" />

或者

<dubbo:reference cluster="failsafe" />

failback cluster 模式

失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。

forking cluster 模式

并行调用多个 provider,只要一个成功就立即返回。常用于实时性要求比较高的读操作,但是会浪费更多的服务资源,可通过 forks=”2” 来设置最大并行数。

broadcacst cluster

逐个调用所有的 provider。任何一个 provider 出错则报错(从2.1.0 版本开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。