荒腔走板
大家好,我是 why,欢迎来到我连续周更优质原创文章的第 64 篇。老规矩,先荒腔走板聊聊其他的。
上面这图是我之前拼的一个拼图。
我经常玩拼图,我大概拼了 50 副左右的 1000 个小块的拼图,但是玩的都是背后有字母或者数字分区提醒的那种,最快纪录是一天拼完一副 1000 块的拼图。
但是上面这幅,只有 800 个小块,却是我拼过的最难的一幅。因为这个背后没有任何提示,只能按照前面的色彩、花纹、边框进行一点点的拼凑。前后花了我两周多的时间。
这完全是一种找虐的行为。
但是你知道这个拼图拼出来的图案叫什么吗?
坛城,传说中佛祖居住的地方。
第一次知道这个名词是 2015 年,窝在寝室看纪录片《第三极》。
其中有一个片段讲的就是僧人为了某个节日用专门收集来的彩沙绘画坛城,他们的那种专注、虔诚、真挚深深的打动了我,当宏伟的坛城画完之后,它静静的等待节日的到来。
本以为节日当天众人会对坛城顶礼膜拜,而实际情况是典礼开始的时候,大家手握一炷香,然后看着众僧人快速的用扫把摧毁坛城。
还没来得及仔细欣赏那复杂的美丽的图案,却又用扫把扫的干干净净。扫把扫下去的那一瞬间,我的心受到了一种强烈的撞击:可以辛苦地拿起,也可以轻松地放下。
那个画面对我的视觉冲击太大了,质本洁来还洁去。以至于我一下就牢牢的记住了这个词:坛城。
后来去了北京,在北京的出租屋里面,看着空荡荡的墙面,我想:要不拼个坛城吧,把北漂当做一场修行,应景。
拼的时候我又看了一遍《第三极》,看到摧毁坛城的片段的时候,有一个弹幕是这样说的:
一切有为法,如梦幻泡影,如露亦如电,应作如是观。
这句话出自《金刚般若波罗蜜经》第三十二品,应化非真分。之前翻阅过几次《金刚经》读到这里的时候我就觉得这句话很有哲理,但是也似懂非懂。所以印象比较深刻。
当它再次以这样的形式展现在我的眼前的时候,我一下就懂了其中的哲理,不敢说大彻大悟,至少领悟一二。
观看摧毁坛城,这个色彩斑斓的世界变幻消失的过程,我的感受是震撼,可惜,放不下。
但是僧人却风轻云淡的说:一切有为法,如梦幻泡影,如露亦如电,应作如是观。
纪录片《第三极》,豆瓣评分 9.4 分,推荐给你。
好了,说回文章。
一道面试题
让我们开门见山,直面主题:Dubbo 服务里面有个服务端,还有个消费端你知道吧?
服务端和消费端都各有一个线程池你知道吧?
那么面试题来了:一般情况下,服务提供者比服务消费者多吧。一个服务消费方可能会并发调用多个服务提供者,每个用户线程发送请求后,会进行超时时间内的等待。多个服务提供者可能同时做完业务,然后返回,服务消费方的线程池会收到多个响应对象。这个时候要考虑一个问题,如何将线程池里面的每个响应对象传递给相应等待的用户线程,且不出错呢?
先说答案。
这个题和答案其实就写在 Dubbo 的官网上:
http://dubbo.apache.org/zh-cn/docs/source_code_guide/service-invoking-process.html
以下回答来自官网:
答案是通过调用编号进行串联。
DefaultFuture 被创建时(下面我们会讲这个 DefaultFuture 是个什么东西),会要求传入一个 Request 对象。
此时 DefaultFuture 可从 Request 对象中获取调用编号,并将 <调用编号, DefaultFuture 对象> 映射关系存入到静态 Map 中,即 FUTURES。
线程池中的线程在收到 Response 对象后,会根据 Response 对象中的调用编号到 FUTURES 集合中取出相应的 DefaultFuture 对象,然后再将 Response 对象设置到 DefaultFuture 对象中。
最后再唤醒用户线程,这样用户线程即可从 DefaultFuture 对象中获取调用结果了。整个过程大致如下图:
上面是官网上的答案,写的比较清楚了,但是官网上是在写服务调用过程的时候顺便讲解了这个考察点,源码散布在各处,看起来比较散乱,不太直观。有的读者反映看的不是特别的明白。
我知道你为什么看的不是那么明白,我在之前的文章里面说过了,你根本就只是在官网白嫖,也不自己动手,像极了看我文章时候的样子:
源码之中无秘密。带你从源码之中寻找答案,让你把官网上的回答和源码能对应起来,这样就更方便你自己动手了。
需要说明一下的是本文 Dubbo 源码版本为 2.7.5。而官网文档演示的源码版本是 2.6.4 。这两个版本上还是有一点差异的,写到的地方我会进行强调。
Demo演示
Demo 大家可以直接参照官方的快速启动:
dubbo.apache.org/zh-cn/docs/user/quick-start.html
我这里就是一个非常简单的服务端:
是的,细心的老朋友肯定看出来了,这个 Demo 我已经用过非常多次了。基本上我每篇 Dubbo 相关的文章里面都会出现这个 Demo。
我建议你自己也花了 10 分钟时间搭一个吧。对你的学习有帮助。别懒,好吗?
我给你一个地址,然后你拉下来就能跑,这种也不是不行。这种我也考虑过。主要是治一治你不想自己动手的毛病,其次那不是我也懒得弄嘛。
输出也是在我们的意料之中。当然了,大家都知道这个输出也必须是这样的。
那么你再细细的品一品。
我们扣一下题,把最开始的问题简化一下。
最开始的问题是一个服务消费端,多个服务提供者,然后服务提供者同时返回响应数据,消费端怎么处理。
其实核心问题就是服务消费端同时收到了多个响应数据,它应该怎么把响应数据对应的请求找到,只有正确找到了请求,才能正确返回数据。
所以我们把重心放到客户端。
在上面的例子中:参数 why1 和 why2 几乎是同时发到服务端的请求 ,然后服务端对于这两个请求也几乎同时响应到了客户端。
在服务端没有返回的时候客户端的两个请求是在干什么?是不是在用户线程上里面等着的接收数据?
那么问题就来了:Dubbo 是怎么把这两个响应对象和两个等待接收数据的用户线程配对成功的?
接下来,我们就带着这个问题,去源码里面寻找答案。