Dubbo-容错机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tank_666/article/details/78610362

容错机制


Dubbo提供了集群部署、路由、负载均衡等容错机制,在客户端引用服务时,由MockClusterInvoker封装具体的集群策略类,默认是FailoverCluster类,具体逻辑见《4.4.2 远程引用服务》部分。

由集群策略类将Directory中的多个Invoker伪装成一个Invoker,对上层调用端是透明的,不同的集群策略有不同的处理方式。主要流程基本是一致的。

1)首先调用MockClusterInvoker.invoke方法,判断是调用Mock功能还是具体的集群策略类,具体逻辑见《消费端调用》部分。后续调用具体集群策略类的invoke方法。

2)调用Directory.list获取Invoker列表;

 3)调用Router.route进行路由选择;

4)LoadBalance进行负载均衡选择;

5)最后获取Invoker对象;

1 集群

一、服务的集群


上图是集群服务的Invoker类的封装顺序。其中,FailoverClusterInovker是默认的集群策略类。

 

二、注册中心的集群

 

下图是注册中心集群的Invoker类封装顺序,其中多个注册中心集群时只能使用AvailableCluster集群策略,在StaticDirectory类中再封装具体服务的集群Cluster。因此在进行服务调用时,就要做两个集群选择,第一次是选择注册中心Cluster,然后在选择的注册中心Cluster中再选择具体服务的Cluster。

三、集群策略的种类

1)AvailableCluster: 获取可用的调用。遍历所有Invokers判断Invoker.isAvalible,只要一个有为true直接调用返回,不管成功与否;

2)BroadcastCluster: 广播调用。遍历所有Invokers, 逐个调用每个调用catch住异常不影响其他invoker调用;

3)FailbackCluster: 失败自动恢复, 对于invoker调用失败, 后台记录失败请求,任务定时重发, 通常用于通知;

4)FailfastCluster: 快速失败,只发起一次调用,失败立即保错,通常用于非幂等性操作;

5)FailoverCluster: 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟;

(1)目录服务directory.list(invocation) 列出方法的所有可调用服务,

(2)获取重试次数,参数retries,默认重试两次;

 (3)调用Inovker,若成功则返回;若失败则继续

(4)调用失败小于重试次数,继续选择Invoker发起调用;调用次数大于等于重试次数抛出调用失败异常;

6)FailsafeCluster: 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。

7)ForkingCluster: 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。

8)MergeableCluster: 分组聚合, 按组合并返回结果,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。

(1) 根据MERGE_KEY从url获取参数值;

(2) 为空不需要merge, 正常调用;

(3) 按group分组调用,将返回接口保存到集合中;

(4) 获取MERGE_KEY如果是默认的话,获取默认merge策略,主要根据返回类型判断;

(5) 如果不是,获取自定义的merge策略;

(6) Merge策略合并调用结果返回;

2 目录服务Directory

在每个集群策略Cluster类中,都是通过Directory.list来查找所有的Invoker列表,注册中心的集群策略使用的是StaticDirectory,服务的集群策略使用的是RegistryDirectory;这两个类均是AbstractDirectory的子类,在获取Invoker列表均是调用该父类的list方法,然后在该方法内部调用子类的dolist方法。

StaticDirectory:静态目录服务,该类的所有Inovker是通过构造函数传入的;对于多注册中心的服务,首先遍历所有注册中心,对每个注册中心的服务引用创建一个MockClusterInvoker对象,组成了一个多注册中心的Invoker列表;然后使用最后一个注册中心的URL以及Invoker列表来初始化StaticDirectory对象;如下代码所示。

在StaticDirectory . dolist方法中直接返回所有多注册中心的invoker列表。


RegistryDirectory:注册目录服务,它实现了NotifyListener接口中的回调接口notify方法,它的Invoker列表是从注册中心获取的,具体逻辑见《4.2 远程引用服务》部分。RegistryDirectory. dolist方法根据服务调用方法从methodInvokerMap变量中获取所有的远程服务引用的invoker执行对象。

3 路由选择

在AbstractDirectory.list方法中,通过具体的Directory.dolist方法获取到Invoker列表之后,然后根据Router列表对Invoker进行路由选择,该Router列表是在远程引用服务时初始化的,具体逻辑见《4.4.2远程引用服务》部分。

         路由规则分为条件路由规则(ConditionRouter:)和脚本路由规则(ScriptRouter:)两种:

ConditionRouter: 条件表达式以 => 分割为whenRule和thenRule

ConditionRouter创建时:

1)从url根据RULE_KEY获取路由条件路由内容

2)rule.indexOf("=>") 分割路由内容

3)分别调用parseRule(rule) 解析路由为whenRule和thenRules

ConditionRouter执行route方法时:

1)如果url不满足when条件即过来条件, 不过滤返回所有invokers;

2)遍历所有invokers判断是否满足then条件, 将满足条件的加入集合result;

3)Result不为空,有满足条件的invokers返回;

4)Result为空, 没有满足条件的invokers, 判断参数FORCE_KEY是否强制过来,如果强制过滤返回空, 不是返回所有即不过滤;

 

ScriptRouter:通过url的RULE_KEY参数获取脚本内容,然后通过java的脚本引擎执行脚本代码, dubbo的测试用例都是通过javascript作为脚本但是理论上也支持groovy, jruby脚本,大家可以参考下测试用例ScriptRouterTest。

ScriptRouter创建时:

1) 从url获取脚本类型javascript, groovy等等;

2) 从url根据RULE_KEY获取路由规则内容;

3) 根据脚本类型获取java支持的脚本执行引擎;

ScriptRouter执行route方法时:

1) 执行引擎创建参数绑定;

2) 绑定执行的参数;

3) 执行引擎编译路由规则得到执行函数CompiledScript;

4) CompiledScript.eval(binds) 根据参数执行路由规则;

4 负载均衡

LoadBalance负载均衡, 负责从多个 Invokers中选出具体的一个Invoker用于本次调用,调用过程中包含了负载均衡的算法,调用失败后需要重新选择。目前有RandomLoadBalance、RoundRobinLoadBalance、LeastActiveLoadBalance、ConsistentHashLoadBalance四种负载均衡策略可用。

RandomLoadBalance:随机访问策略,按权重设置随机概率,是默认策略,具体算法如下:

1)获取所有invokers的个数;

2)遍历所有Invokers, 获取计算每个invokers的权重,并把权重累计加起来;每相邻的两个invoker比较他们的权重是否一样,有一个不一样说明权重不均等;

3) 总权重大于零且权重不均等的情况下;按总权重获取随机数offset = random.netx(totalWeight); 遍历invokers确定随机数offset落在哪个片段(invoker上) ;

4) 权重相同或者总权重为0, 根据invokers个数均等选择:invokers.get(random.nextInt(length));

实现代码如下:

RoundRobinLoadBalance:轮询,按公约后的权重设置轮询比率

1) 获取轮询key = 服务名+方法名;

2)获取可供调用的invokers个数length;

3)设置最大权重的默认值maxWeight=0

4)设置最小权重的默认值minWeight=Integer.MAX_VALUE

5)遍历所有Inokers,比较出得出maxWeight和minWeight

6) 如果权重是不一样的,

6.1)根据key获取自增序列,自增序列加一与最大权重取模默认得到currentWeigth ;

6.2)遍历所有invokers筛选出大于currentWeight的invokers

6.3)设置可供调用的invokers的个数length;

6.4)若length=1,则直接返回该invoker;若大于1则进入第7步;

7) 自增序列加一并与length取模,从invokers获取invoker;

实现代码如下:


LeastActiveLoadBalance:最少活跃调用数,相同的活跃的随机选择。

活跃数是指调用前后的计数差, 使慢的提供者收到更少的请求,因为越慢的提供者前后的计数差越大。

活跃计数的功能消费者是在ActiveLimitFilter中设置的,代码如下,利用RpcStatus类的静态变量来统计功能方法的活跃数、调用总次数、总耗时、成功数、失败数等信息。


最少活跃的选择过程如下:

1)获取可调用invoker的总个数,初始化最小活跃数,相同最小活跃的个数,相同最小活跃数的下标数组等等;

2) 遍历所有invokers,获取每个invoker的活跃数active和权重;找出最小权重的invoker;

如果有相同最小权重的inovkers, 将下标记录到数组leastIndexs[]数组中,

累计所有的权重到totalWeight变量;

3)如果invokers的权重不相等且totalWeight大于0;

按总权重随机offsetWeight = random.nextInt(totalWeight),计算随机值在哪个片段上并返回invoker;

4)如果invokers的权重相等或者totalWeight等于0,均等随机;

实现代码如下:

ConsistentHashLoadBalance:一致性hash, 相同参数的请求总是发到同一个提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

展开阅读全文

没有更多推荐了,返回首页