ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Dubbo集群容错

2021-02-01 18:00:26  阅读:171  来源: 互联网

标签:Dubbo 负载 调用 列表 容错 集群 Invoker 均衡 节点


本章主要内容:
• 集群容错总体实现;
• 普通容错策略的实现;
• Directory的实现原理;
• Router的实现原理;
• LoadBalance的实现原理;
• Merger的实现原理;
Cluster的总体工作流程可以分为以下几步:
(1) 生成Invoker对象。 不同的Cluster实现会生成不同类型的Clusterinvoker对象并返回。 然后调用Clusterinvoker的Invoker方法, 正式开始调用流程。
(2) 获得可调用的服务列表。 首先会做前置校验, 检查远程服务是否已被销毁。 然后通过Directory#list方法获取所有可用的服务列表。 接着使用Router接口处理该服务列表, 根据路由规则过滤一部分服务, 最终返回剩余的服务列表。
(3) 做负载均衡。 在第2步中得到的服务列表还需要通过不同的负载均衡策略选出一个服务, 用作最后的调用。 首先框架会根据用户的配置, 调用ExtensionLoader获取不同负载均衡策略的扩展点实现(具体负载均衡策略会在后面讲解) 。 然后做一些后置操作, 如果是异步调用则设置调用编号。 接着调用子类实现的dolnvoke方法(父类专门留了这个抽象方法让子类实现) ,子类会根据具体的负载均衡策略选出一个可以调用的服务。
(4) 做RPC调用。 首先保存每次调用的Invoker到RPC上下文, 并做RPC调用。 然后处理调用结果, 对于调用出现异常、 成功、 失败等情况, 每种容错策略会有不同的处理方式。

2容错机制的实现
2.1容错机制概述
Dubbo容错机制能增强整个应用的鲁棒性, 容错过程对上层用户是完全透明的, 但用户也可以通过不同的配置项来选择不同的容错机制。 每种容错机制又有自己个性化的配置项。 Dubbo中现有 Failover、Failfast、 Failsafe、 Fallback、 Forking、 Broadcast 等容错机制, 容错机制的特性如表7-1所示。

2.3 Failover 策略

2.6 Fallback 策略
Fallback如果调用失败, 则会定期重试。 FailbackClusterlnvoker里面定义了一个ConcurrentHashMap,专门用来保存失败的调用。 另外定义了一个定时线程池, 默认每5秒把所有失败的调用拿出来, 重试一次。 如果调用重试成功, 则会从ConcurrentHashMap中移除。

3 Directory 的实现
整个容错过程中首先会使用Directory#list来获取所有的Invoker列表。 Directory也有多种实现子类, 既可以提供静态的Invoker列表, 也可以提供动态的Invoker列表。 静态列表是用户自己设置的Invoker列表; 动态列表根据注册中心的数据动态变化, 动态更新Invoker列表的数据, 整个过程对上层透明

又是熟悉的“套路” , 使用了模板模式。 Directory是顶层的接口。 AbstractDirectory封装了通用的实现逻辑。 抽象类包含RegistryDirectory和StaticDirectory两个子类。 下面分别介绍每个类的职责和工作:
(1) AbstractDirectoryo封装了通用逻辑, 主要实现了四个方法: 检测Invoker是否可用,销毁所有Invoker, list方法, 还留了一个抽象的doList方法给子类自行实现。 list方法是最主要的方法, 用于返回所有可用的list,逻辑分为两步:
• 调用抽象方法doList获取所有Invoker列表, 不同子类有不同的实现 ;
• 遍历所有的router,进行Invoker的过滤, 最后返回过滤好的Invoker列表。
doList抽象方法则是返回所有的Invoker列表, 由于是抽象方法, 子类继承后必须要有自己的实现。
(2) RegistryDirectoryo属于Directory的动态列表实现, 会自动从注册中心更新Invoker列表、 配置信息、 路由列表。
(3) StaticDirectoryo Directory的静态列表实现, 即将传入的Invoker列表封装成静态的Directory对象, 里面的列表不会改变。
 

3.2 RegistryDirectory 的实现
RegistryDirectory中有两条比较重要的逻辑线, 第一条, 框架与注册中心的订阅, 并动态更新本地Invoker列表、 路由列表、 配置信息的逻辑; 第二条, 子类实现父类的doList方法
1. 订阅与动态更新
我们先看一下订阅和动态更新逻辑。 这个逻辑主要涉及subscribe、 notify> refreshinvoker三个方法, 其余是一些数据转换的辅助类方法, 如toConfigurators、toRouterSo
subscribe是订阅某个URL的更新信息。 Dubbo在引用每个需要RPC调用的Bean的时候,会调用directory. subscribe来订阅这个Bean的各种URL的变化(Bean的配置在配置中心中都是以URL的形式存放的)。
notify就是监听到配置中心对应的URL的变化, 然后更新本地的配置参数。 监听的URL分为三类: 配置configurators、路由规则router、 Invoker列表。

2. doList的实现
notify中更新的Invoker列表最终会转化为一个字典Map<Stringj List<Invoker<T>>>methodlnvokerMapo key是对应的方法名称, value是整个Invoker列表。 doList的最终目标就是在字典里匹配出可以调用的Invoker列表, 并返回给上层。 其主要步骤如下:
(1)检查服务是否被禁用。 如果配置中心禁用了某个服务, 则该服务无法被调用。 如果服务被禁用则会抛出异常。
(2)根据方法名和首参数匹配Invoker这是一个比较奇特的特性。 根据方法名和首参数查找对应的Invoker列表, 暂时没看到相关的应用场景。
 

4 路由的实现
Directory获取所有Invoker列表的时候, 就会调用到本节的路由接口。路由接口会根据用户配置的不同路由策略对Invoker列表进行过滤, 只返回符合规则的Invoker。
4.1路由的总体结构
路由分为条件路由、 文件路由、 脚本路由, 对应dubbo-admin中三种不同的规则配置方式。条件路由是用户使用Dubbo定义的语法规则去写路由规则;文件路由则需要用户提交一个文件,里面写着对应的路由规则, 框架基于文件读取对应的规则; 脚本路由则是使用JDK自身的脚本引擎解析路由规则脚本, 所有JDK脚本引擎支持的脚本都能解析, 默认是JavaScripto我们先来看一下接口之间的关系, 如图7.9所示。

5 负载均衡的实现
在整个集群容错流程中, 首先经过Directory获取所有Invoker列表, 然后经过Router根据路由规则过滤Invoker,最后幸存下来的Invoker还需要经过负载均衡这一关, 选出最终要调用的 Invoker
5.1包装后的负载均衡
整个逻辑过程大致可以分为4步:
(1) 检查URL中是否有配置粘滞连接, 如果有则使用粘滞连接的Invoker。 如果没有配置粘滞连接, 或者重复调用检测不通过、 可用检测不通过, 则进入第2步。
(2) 通过ExtensionLoader获取负载均衡的具体实现, 并通过负载均衡做节点的选择。 对选择出来的节点做重复调用、 可用性检测, 通过则直接返回, 否则进入第3步。
(3) 进行节点的重新选择。 如果需要做可用性检测, 则会遍历Directory中得到的所有节点, 过滤不可用和已经调用过的节点, 在剩余的节点中重新做负载均衡; 如果不需要做可用性检测, 那么也会遍历Directory中得到的所有节点, 但只过滤已经调用过的, 在剩余的节点中重新做负载均衡。 这里存在一种情况, 就是在过滤不可用或已经调用过的节点时, 节点全部被过滤, 没有剩下任何节点, 此时进入第4步。
(4) 遍历所有已经调用过的节点, 选出所有可用的节点, 再通过负载均衡选出一个节点并返回。 如果还找不到可调用的节点, 则返回null。从上述逻辑中, 我们可以得知, 框架会优先处理粘滞连接。 否则会根据可用性检测或重复调用检测过滤一些节点, 并在剩余的节点中做负载均衡。 如果可用性检测或重复调用检测把节点都过滤了, 则兜底的策略是: 在己经调用过的节点中通过负载均衡选择出一个可用的节点。
 

5.2负载均衡的总体结构
Dubbo现在内置了 4种负载均衡算法, 用户也可以自行扩展, 因为LoadBalance接口上有@SPI注解, 如代码清单7.12所示。

代码清单7.12负载均衡接口
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance (
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url. Invocation invocation)
throws RpcException;
}

4种负载均衡算法都继承自同一个抽象类, 使用的也是模板模式, 抽象父类中己经把通用的逻辑完成, 留了一个抽象的doSelect方法给子类实现。 负载均衡的接口关系如图7-10所示

5.3 Random负载均衡
Random负载均衡是按照权重设置随机概率做负载均衡的。 这种负载均衡算法并不能精确地平均请求, 但是随着请求数量的增加, 最终结果是大致平均的。
(1) 计算总权重并判断每个Invoker的权重是否一样。 遍历整个Invoker列表, 求和总权重。 在遍历过程中, 会对比每个Invoker的权重, 判断所有Invoker的权重是否相同。
(2) 如果权重相同, 则说明每个Invoker的概率都一样, 因此直接用nextlnt随机选一个Invoker返回即可。
(3) 如果权重不同, 则首先得到偏移值, 然后根据偏移值找到对应的Invoker,如代码清单7-14所示

代码清单7-14随机负载均衡源码

int offset = ThreadLocalRandom.current().nextlnt(totalWeight);
for (int i = 0; i < length; i++) {
offset -= weights[i];
if (offset < 0) {
return invokers.get(i);
}
)

5.4 RoundRobin 负载均衡
权重,轮询负载均衡会根据设置的权重来判断轮询的比例 。 普通轮询负载均衡的好处是每个节点获得的请求会很均匀, 如果某些节点的负载能力明显较弱, 则这个节点会堆积比较多的请求。 因此普通的轮询还不能满足需求, 还需要能根据节点权重进行干预。 权重轮询又分为普通权重轮询和平滑权重轮询。 普通权重轮询会造成某个节点会突然被频繁选中, 这样很容易突然让一个节点流量暴增。 Nginx中有一种叫平滑轮询的算法(smooth weighted round-robin balancing),这种算法在轮询时会穿插选择其他节点, 让整个服务器选择的过程比较均匀, 不会“逮住” 一个节点一直调用oDubbo框架中最新的RoundRobin代码已经改为平滑权重轮询算法。
5.5 LeastActive 负载均衡
LeastActive负载均衡称为最少活跃调用数负载均衡, 即框架会记下每个Invoker的活跃数,每次只从活跃数最少的Invoker里选一个节点。 这个负载均衡算法需要配合ActiveLimitFilter过滤器来计算每个接口方法的活跃数。 最少活跃负载均衡可以看作Random负载均衡的“加强版” , 因为最后根据权重做负载均衡的时候, 使用的算法和Random的一样。
5.6 一致性Hash负载均衡
一致性Hash负载均衡可以让参数相同的请求每次都路由到相同的机器上。 这种负载均衡的方式可以让请求相对平均, 相比直接使用Hash而言, 当某些节点下线时, 请求会平摊到其他服务提供者, 不会引起剧烈变动。
普通一致性Hash的简单示例如图7-12所示。

普通一致性Hash会把每个服务节点散列到环形上, 然后把请求的客户端散列到环上, 顺时针往前找到的第一个节点就是要调用的节点。 假设客户端落在区域2,则顺时针找到的服务C就是要调用的节点。 当服务C宕机下线, 则落在区域2部分的客户端会自动迁移到服务D上。这样就避免了全部重新散列的问题。
普通的一致性Hash也有一定的局限性, 它的散列不一定均匀, 容易造成某些节点压力大。因此Dubbo框架使用了优化过的Ketama 一致性Hash。 这种算法会为每个真实节点再创建多个虚拟节点, 让节点在环形上的分布更加均匀, 后续的调用也会随之更加均匀。
 

6 Merger的实现
当一个接口有多种实现, 消费者又需要同时引用不同的实现时, 可以用group来区分不同的实现, 如下所示。
<dubbo:service group="groupl" interface="com.xxx.testService" />
<dubbo:service group="group2" interface="com.xxx.testservice" />
如果我们需要并行调用不同group的服务, 并且要把结果集合并起来, 贝懦要用到Merger特性。 Merger实现了多个服务调用后结果合并的逻辑。 虽然业务层可以自行实现这个能力, 但Dubbo直接封装到框架中, 作为一种扩展点能力, 简化了业务开发的复杂度。 Merger的工作方式如图7.13所示

6.1总体结构
MergerCluster也是Cluster接口的一种实现, 因此也遵循Cluster的设计模式, 在invoke方法中完成具体逻辑。 整个过程会使用Merger接口的具体实现来合并结果集。 在使用的时候, 通过MergerFactory获得各种具体的Merger实现oMerger的12种默认实现的关系如图7-14所示。

如果开启了 Merger特性, 并且未指定合并器(Merger的具体实现), 则框架会根据接口的返回类型自动匹配合并器。
 

备注:文章参考《深入理解Apache Dubbo与实战》,作者:林琳,诣极

 

 

 

 

 

 

 

 

标签:Dubbo,负载,调用,列表,容错,集群,Invoker,均衡,节点
来源: https://blog.csdn.net/chenbin1991smile/article/details/113476384

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有