之前有读者问了 Dubbo Cluster 集群的一些问题。

那么本文聊一聊 Dubbo 的 Cluster 集群和 Failover Cluster (失败自动切换)策略。

如果没有特别说明的地方,源码均是来自 2.7.5 版本。

在阅读之前先抛出几个问题:

  • 1.Dubbo Cluster集群的作用是什么?

  • 2.Dubbo Cluster的10个实现类你能说出来几个,其中哪几个是集群容错的方法实现?

  • 3.默认的集群实现类是什么呢?

  • 4.Failover Cluster调用失败之后,会自动进行几次重试呢?

  • 5.什么是Dubbo的粘滞连接?

  • 6.粘滞连接在Cluster中是怎么应用的?

  • 7.Cluster选择出一个可用的Invoker最多要进行几次选择?

  • 8.请问几次选择分别是什么?

注意:上面的8个问题,前3个是非常常见的面试题。后面的都是你阅读完本文后就可以知道问题的答案,面试中并不常见,但是后面的问题可以综合成一个非常高频的面试题:有看过什么源码吗,能给我讲讲吗?

本文会对上面的问题进行逐一的、详细的解读。文章的最后会进行一个问题和答案的汇总。

走起。

Dubbo Cluster集群的作用是什么?

在生产环境,我们常常是多个服务器跑相同的应用,这种做的目的其一是为了避免单点故障。

为了避免单点故障,现在的应用通常至少会部署在两台服务器上。而对于一些负载比较高的服务,比如网关服务,会部署更多的服务器。

这样,在同一环境下的服务提供者数量会大于 1 。对于服务消费者来说,同一环境下出现了多个服务提供者。

这时会出现几个问题:

  • 对于一次请求,我作为消费者到底调用哪个提供者呢?

  • 服务调用失败的时候我怎么做呢?是重试?是抛出异常?或者仅仅是打印出异常?

为了处理这些问题,Dubbo 定义了集群接口 Cluster 以及 Cluster Invoker。

集群 Cluster 的用途是将多个服务提供者合并为一个 Cluster Invoker,并将这个 Invoker 暴露给服务消费者。

这样的好处就是对服务消费者来说,只需通过这个 Cluster Invoker 进行远程调用即可,至于具体调用哪个服务提供者,以及调用失败后如何处理等问题,现在都交给集群模块去处理。

集群模块是服务提供者和服务消费者的中间层,为服务消费者屏蔽了服务提供者的情况,这样服务消费者就可以专心处理远程调用相关事宜。比如发请求,接受服务提供者返回的数据等。这就是Dubbo Cluster集群的作用。

Dubbo Cluster的10个实现类是什么?

根据配置可以知道Dubbo集群接口Cluster有10种实现方法如下:

需要注意的是,十种实现方法其中只有failover、failfast、failsafe、failback、forking、broadcast这6种才属于集群容错的范畴。另外的实现均有其他的应用场景。

下面我们先说6种集群容错的实现方法:

Failover Cluster:

failover=org.apache.dubbo.rpc.cluster.support.FailoverCluster

失败自动切换,在调用失败时,失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过retries="2"来设置重试次数(不含第一次)。

Failfast Cluster:

failfast=org.apache.dubbo.rpc.cluster.support.FailfastCluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe Cluster:

failsafe=org.apache.dubbo.rpc.cluster.support.FailsafeCluster

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

Failback Cluster:

failback=org.apache.dubbo.rpc.cluster.support.FailbackCluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

Forking Cluster:

forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster

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

Broadcast Cluster:

broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster

广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

所以对于这个问题你也可以回答上来了:10个实现类中有哪几个是集群容错的方法实现?

接下来再说说另外四个实现类:

Available Cluster:

available=org.apache.dubbo.rpc.cluster.support.AvailableCluster

获取可用的服务方。遍历所有Invokers通过invoker.isAvalible判断服务端是否活着,只要一个有为true,直接调用返回,不管成不成功。

Mergeable Cluster:

mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster

分组聚合,将集群中的调用结果聚合起来,然后再返回结果。比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。

Mock Cluster:

mock=org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper

本地伪装,通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。

zone-aware Cluster:

zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster

zone-aware的应用场景是下面这样的。

业务部署假设是双注册中心:

则对应消费端,先在注册中心间选择,再到选定的注册中心选址:

所以,和之前相比,在Dubbo 2.7.5以后,对于多注册中心订阅的场景,选址时的多了一层注册中心集群间的负载均衡。

这个注册中心集群间的负载均衡的实现就是:zone-aware Cluster。

对于多注册中心间的选址策略,根据类上的注释可以看出,目前设计的有下面四种:

1.指定优先级:

来自preferred="true"注册中心的地址将被优先选择,只有该中心无可用地址时才Fallback到其他注册中心

<dubbo:registry address="zookeeper://${zookeeper.address1}" preferred="true" />

2.同 zone 优先:

选址时会和流量中的zone key做匹配,流量会优先派发到相同zone的地址

<dubbo:registry address="zookeeper://${zookeeper.address1}" zone="beijing" />

3.权重轮询:

来自北京和上海集群的地址,将以10:1的比例来分配流量

<dubbo:registry id="beijing" address="zookeeper://${zookeeper.address1}" weight="100" />

<dubbo:registry id="shanghai" address="zookeeper://${zookeeper.address2}" weight="10" />

4.默认方法:

选择第一个可用的即可。

默认的集群方法是什么呢?

源码之下无秘密。我们从源码中寻找答案:

首先我们可以看到,Cluster 是一个 SPI 接口。其默认实现是 FailoverCluster.NAME ,如下源码所示:

所以默认的实现方法就是:

org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker

由于Cluster是一个SPI接口,所以我们也可以根据实际需求去扩展自己的实现类。

FailoverCluster doInvoke源码解析

接下来我们就对 FailoverClusterInvoker 的 doInvoke 方法的源码进行解析。

这一小节主要回答这一个问题:Failover Cluster调用失败之后,会自动切换Invoker进行几次重试呢?

通过源码,我们可以知道默认的重试次数是2次

有人就问了:为什么第61行的最后还有一个"+1"呢?

你想一想。我们想要在接口调用失败后,重试 n 次,这个 n 就是 DEFAULT_RETRIES,默认为 2。

那么我们总的调用次数就是 n+1 次了。所以这个"+1"是这样来的,很小的一个点。

另外图中标记了红色五角星 ★ 的地方,第 62 到 64 行,也是很关键的地方。

对于retries参数,在官网上的描述是这样的:

不需要重试请设为0。我们前面分析了,当设置为0的时候,只会调用一次。

但是我也看见过retries配置为"-1"的。-1+1=0。调用0次明显是一个错误的含义。但是程序也正常运行,且只调用一次。

这就是标记了红色五角星★的地方的功劳了。防御性编程。哪怕你设置为-10086也只会调用一次。

接下来对 doInvoke 方法进行一个全面的解读,下面是2.7.5版本的源码,我基本上每一行主要的代码都加了注释,可以点开大图查看:

org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke

如上所示,FailoverClusterInvoker 的 doInvoke 方法主要的工作流程是:

首先是获取重试次数,然后根据重试次数进行循环调用,在循环体内,如果失败,则进行重试。

在循环体内,首先是调用父类 AbstractClusterInvoker 的 select 方法,通过负载均衡组件选择一个 Invoker ,然后再通过这个 Invoker 的 invoke 方法进行远程调用。

如果失败了,记录下异常,并进行重试。

注意一个细节:在进行重试前,重新获取最新的invoker集合,这样做的好处是,如果在重试的过程中某个服务挂了,可以通过调用list方法可以保证copyInvokers是最新的可用的invoker列表。

整个流程大致如此,不是很难理解。

什么是Dubbo的粘滞连接?

接下来我们要看的是父类 AbstractClusterInvoker 的 select 方法的逻辑。

但是在看 select 方法的逻辑之前,我必须得先铺垫一下 Dubbo 粘滞连接特性的知识。

官网上的解释是这样的:

可以看出,这是一个服务治理类型的参数。

当设置 true 时,该接口上的所有方法使用同一个 provider。

官方文档中说明可以用在接口和方法级别。

这些都是一些比较简单的服务治理的规则。如果需求更复杂,则需要使用路由功能。

官方文档已经说的很清楚了。我就只简单的解释一下第一句话:粘滞连接用于有状态服务。

那么什么是有状态服务,什么又是无状态服务呢?

根据我简单的理解。对服务提供者来说,究竟是有状态的服务提供者,还是无状态服务,其判断依据就一句话:

从客户端发起的两个或者多个请求,在服务端是否具备上下文关系。

举个例子,我们经常会用到的 session。

众所周知,HTTP 协议是无状态的。

那么当在一个电商场景下,将用户挑选的商品放到购物车,保存到 session 里,当付款的时候,再从购物车里取出商品信息。

这样通过 session 就实现了有状态的服务。

当一个服务被设计为无状态的时候,对于客户端来说,可以随意调用。所以无状态的服务可以很容易的进行水平扩容。

当一个服务被设计为有状态的时候,想要水平扩容的时候就不是那么简单了。因为客户端和服务端存在着上下文关系,所以客户端每次都需要请求那一台服务端。

把一个有状态的服务修改为无状态的服务的方案也很简单。

还是拿 session 举例,这个时候,我们的分布式 session 就呼之欲出了。

把 session 集中存储起来,比如放到 Redis 中,弄一个独立于服务的 session 共享层。

这样,一个有状态的服务就可以变为一个无状态的服务。

AbstractClusterInvoker select源码解析

看完这一小节,你也就知道了粘滞连接在 Cluster 中是怎么应用的了。

有了粘滞连接的知识储备后,再看 select 方法就比较轻松了,首先需要知道的是select 方法是一个关键的公共方法,其作用就是选择出一个可用的 invoke,有下面这几个 Cluster 的实现类都在调用:

代码片段不长,逻辑也比较清楚,具体代码解析如下:

根据代码画出select方法的流程图如下:

结合代码和流程图,再进行一个文字描述。

先介绍一下select的四个入参,分别是:

  • loanbalance:负载均衡策略。

  • invocation:它持有调用过程中的变量,比如方法名,参数等。

  • invokers:这里的invokers列表可以看做是存活着的服务提供者列表。

  • selected:已经被选择过的invoker集合。

通过源码我们可以看出,select 方法的主要逻辑集中在了对粘滞连接特性的支持上。

首先是获取 sticky 配置,然后再检测 invokers 列表中是否包含 stickyInvoker,如果不包含,则认为该 stickyInvoker 不可用,此时将其置空。

为什么可以置空?

因为这里的 invokers 列表是存活着的服务提供者列表,如果这个列表不包含 stickyInvoker,那自然而然的认为 stickyInvoker 挂了,所以置空。

接下来,如果 stickyInvoker 存在于 invokers 列表中,说明 stickyInvoker 还活着,此时要进行下一项检测。

检测 selected(选择过的服务提供者列表)中是否包含 stickyInvoker。

如果包含的话,说明 stickyInvoker 在此之前没有成功提供服务(但其仍然处于存活状态)。

此时我们认为这个服务不可靠,不应该在重试期间内再次被调用,因此这个时候不会返回该 stickyInvoker。

如果 selected 不包含 stickyInvoker,此时还需要进行可用性检测

比如检测服务提供者网络连通性等。当可用性检测通过,才可返回 stickyInvoker,否则调用 doSelect 方法选择 Invoker。

如果 sticky 为 true,此时会将 doSelect 方法选出的 Invoker 赋值给 stickyInvoker。

关于粘滞连接和可用性检测的默认配置如下:

即默认情况下粘滞连接是关闭状态。当粘滞连接开启时,默认会进行可用性检查。

关于select方法先分析这么多,继续向下分析。

AbstractClusterInvoker doSelect源码解析

读完这一小节你可以回答出这两个问题:

  • 1.Cluster选择出一个可用的Invoker最多要进行几次选择?

  • 2.请问几次选择分别是什么?

doSelect 方法的源码解析如下:

Failover Cluster 选择出一个可用的 Invoker 最多要进行三次选择,也是 doSelect 的主要逻辑,三次分别是(图中标号了 ①②③ 的地方):

  • ①:通过负载均衡组件选择 Invoker。②:如果选出来的 Invoker 不稳定,或不可用,此时需要调用reselect 方法进行重选。

  • ③:reselect选出来的Invoker为空,此时定位invoker在invokers列表中的位置index,然后获取index+1处的 invoker。

AbstractClusterInvoker reselect源码解析

下面我们来看一下 reselect 方法的逻辑。

根据源码做出流程图如下:

所以,reselect 方法总结下来其实做了四件事情:

  • 第一:查找可用的 invoker,并将其添加到 reselectInvokers 集合中。这个 reselectInvokers 集合你可以理解为里面放的是所有的可用的 invoker 的集合与 selected 集合的差集。

  • 第二:如果经过筛选后,reselectInvokers 不为空,则通过负载均衡组件再次进行选择并返回。

  • 第三:如果经过筛选后,reselectInvokers 为空,则再从 selected 集合(已经被调用过的集合)中选出所有可用的 invoker,放到 reselectInvokers 中,再次通过负载均衡组件进行选择并返回。

  • 第四:如果进过上面的步骤后,没有选择出合适的 invoker,reselectInvokers 还是为空,说明所有的 invoker 都不可用了,返回为 null。

好了,到这里就把最开始抛出的8个问题都解答完毕了,接下来对问题、答案进行一个汇总。

问题、答案汇总

1.Dubbo Cluster 集群的作用是什么?

简单来说:集群模块是服务提供者和服务消费者的中间层,为服务消费者屏蔽了服务提供者的情况,这样服务消费者就可以专心处理远程调用相关事宜。

比如发请求,接受服务提供者返回的数据等。

这就是 Dubbo Cluster 集群的作用。

2.Dubbo Cluster的10个实现类你能说出来几个,其中哪几个是集群容错的方法实现?

根据配置可以知道 Dubbo 集群接口 Cluster 有10种实现方法如下:

其中 failover、failfast、failsafe、failback、forking、broadcast 这 6 种才属于集群容错的范畴。

另外的实现均有其他的应用场景。

3.默认的集群实现类是什么呢?

失败自动切换:org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker

4.Failover Cluster 调用失败之后,会自动切换 Invoker 进行几次重试呢?

自动进行 2 次重试,共计调用 3 次。

5.什么是 Dubbo 的粘滞连接?

粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起调用,除非该提供者挂了,再连另一台。粘滞连接将自动开启延迟连接,以减少长连接数。

6.粘滞连接在 Cluster 中是怎么应用的?

参照 AbstractClusterInvoker select 源码解析。

select 方法的主要逻辑集中在了对粘滞连接特性的支持上。

7.Cluster 选择出一个可用的 Invoker 最多要进行几次选择?

最多进行三次选择。

8.请问几次选择分别是什么?

  • ①:通过负载均衡组件选择 Invoker。

  • ②:如果选出来的 Invoker 不稳定,或不可用,此时需要调用 reselect 方法进行重选。

  • ③:reselect 选出来的 Invoker 为空,此时定位 invoker 在 invokers 列表中的位置 index,然后获取 index+1 处的 invoker。

面试官:啥是集群策略啊?相关推荐

  1. 面试官:Zookeeper集群怎么搭建?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,可以微信搜索[小奇JAVA面试]第一时间阅 ...

  2. Java面试官:Kafka集群管理

    集群是一种计算机系统, 它通过一组松散集成的计算机软件和/或硬件连接起来高度紧密地协作完成计算工作.在某种意义上,他们可以被看作是一台计算机.集群系统中的单个计算机通常称为节点,通常通过局域网连接,但 ...

  3. 面试官:Redis集群有哪些方式,Leader选举又是什么原理呢?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,可以微信搜索[小奇JAVA面试]第一时间阅 ...

  4. 【观察】生态赋能与聚链共赢背后,解读 SAP 产业集群策略新价值

    申耀的科技观察 读懂科技,赢取未来! 众所周知,国家发改委.中央网信办日前印发了<关于推进"上云用数赋智"行动 培育新经济发展实施方案>,从能力扶持.金融普惠.搭建生态 ...

  5. dubbo调用失败策略_面试官:dubbo负载均衡策略,集群容错策略,动态代理策略有哪些...

    面试官心理分析 继续深问吧,这些都是用 dubbo 必须知道的一些东西,你得知道基本原理,知道序列化是什么协议,还得知道具体用 dubbo 的时候,如何负载均衡,如何高可用,如何动态代理. 说白了,就 ...

  6. Redis面试连环问:集群、复制以及与其他NOSQL数据库的区别?

    欢迎关注方志朋的博客,回复"666"获面试宝典 前言 面试官问:您熟悉redis?搭建过redis集群?rediscluster底层的主从复制rdb如何工作的?-- 接下来,就是一 ...

  7. Dubbo面试杀招--Dubbo集群容错负载均衡

    点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...

  8. 集群策略--集群(clustering)

    集群(clustering)这个词使用非常混乱.这里我们首先给出一个较为科学的阐述,并以这个阐述为主线来叙述.探讨相关问题. 集群可分为三种类型:科学集群.高可用性集群.负载均衡集群. 科学集群(Sc ...

  9. 面试官:Redis 过期删除策略和内存淘汰策略有什么区别?

    作者:小林coding 计算机八股文网站:https://xiaolincoding.com 大家好,我是小林. Redis 的「内存淘汰策略」和「过期删除策略」,很多小伙伴容易混淆,这两个机制虽然都 ...

最新文章

  1. DCGAN及其TensorFlow源码
  2. 【七招破解WinXP系统访问网络变慢的故障】
  3. hibernate annotation多对多中间表添加其他字段的第三种方法
  4. android listview 不显示_ListView详细介绍与使用
  5. 阿里巴巴为什么主推HSF?比Dubbo有哪些优势?
  6. Android注册BroadcastReceiver的两种办法及其区别
  7. 临床外显子组测序分析中的那些坑(中)
  8. java初反射_java中的反射机制
  9. linux驱动编写(pwm驱动)
  10. 持有至少百万美元钱包本周增至66,540,增长了150%
  11. Mysql你应该要懂索引知识
  12. 重学概率论的一点点理解(持续更新)
  13. iOS开发之常见的URLScheme
  14. android 多渠道覆盖,Android多渠道包
  15. SDK是什么?与API有什么关系?
  16. 【AI Studio平台基本操作】算力卡的使用获取及有关内容
  17. 计算机打印机无法共享怎么设置密码,打印机共享设置密码【调解思路】
  18. 每日工作记录——ERROR:Simulator:793 - Unable to elaborate instantiated module work
  19. WUSTOJ 1318: 区间的连通性(Java)
  20. 如何使用微软官方工具制作win10启动盘(附带微软官方工具地址)

热门文章

  1. java中unknown source_java中GUI编程中的unknown source问题
  2. python基础 while循环练习
  3. 单片机怎么学?新手怎么快速学会单片机?
  4. PHP mysql 写入多条记录_PHP mysql插入多条记录的Array数组获取问题
  5. JavaScript初学者编程题(21)
  6. springmvc开启事务_java面试题 一 :SpringMvc的流程
  7. android的UDC功能,Android实现搜索历史功能
  8. dcut matlab,cut 命令详解
  9. 【译】Web Components简介
  10. 你属于程序员中的哪种人?