blob: 08869b4354b6e28780ab27899d47fa09a26a1a55 [file] [log] [blame]
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – Apache Dubbo 中文</title><link>https://dubbo.apache.org/zh-cn/</link><description>Recent content in Apache Dubbo 中文 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><atom:link href="https://dubbo.apache.org/zh-cn/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 阿里巴巴升级 Dubbo3 全面取代 HSF2</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/16/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4%E5%8D%87%E7%BA%A7-dubbo3-%E5%85%A8%E9%9D%A2%E5%8F%96%E4%BB%A3-hsf2/</link><pubDate>Mon, 16 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/16/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4%E5%8D%87%E7%BA%A7-dubbo3-%E5%85%A8%E9%9D%A2%E5%8F%96%E4%BB%A3-hsf2/</guid><description>
&lt;p>继业务全面上云后,2022 双十一,阿里巴巴微服务技术栈全面迁移到以 Dubbo3 为代表的云上开源标准中间件体系。在业务上,基于 Dubbo3 首次实现了关键业务不停推、不降级的全面用户体验提升,从技术上,大幅提高研发与运维效率的同时地址推送等关键资源利用率提升超 40%,基于三位一体的 Dubbo3 开源中间件体系打造了阿里在云上的单元化最佳实践和统一标准,同时将规模化实践经验与技术创新贡献开源社区,成为微服务开源技术与标准发展的核心源泉与推动力。&lt;/p>
&lt;h2 id="1-阿里电商">1 阿里电商&lt;/h2>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649639544-2c49fc49-b25e-4fc9-845e-8b377ceba3bc.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=66&amp;amp;id=uf434669b&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=132&amp;amp;originWidth=204&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=13316&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u81ec2327-b1f6-43a2-b908-31b91796169&amp;amp;title=&amp;amp;width=102" alt="image.png"> &lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649088160-85a607de-4737-4e2d-90b2-49bf5b949332.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=81&amp;amp;id=l0QsD&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=162&amp;amp;originWidth=308&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=61949&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=ubb9c1593-7f65-4c84-997a-34e8e9b7f47&amp;amp;title=&amp;amp;width=154" alt="image.png"> &lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649669327-fe6b77b6-3f34-4a18-b5c6-726729b69331.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=74&amp;amp;id=ufc994d2f&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=148&amp;amp;originWidth=404&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=35362&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u2637ee3c-ea26-4a75-9a5c-2be5e913957&amp;amp;title=&amp;amp;width=202" alt="image.png"> &amp;hellip;&amp;hellip;&lt;/p>
&lt;p>整个电商体系的所有核心应用,包括交易相关、导购相关都都升级到了 Dubbo3 体系,用来替换原有的 HSF 框架,阿里电商是对 Dubbo3 实践最广泛、需求最强烈的体系,基于 Dubbo3 实现了以下关键目标。
2022 618大促、双11 大促期间 超 2000+ 应用、40w 节点均跑在 Dubbo3 之上。&lt;/p>
&lt;ul>
&lt;li>应用级服务发现,解决了大促期间地址推送降级的问题,部分关键链路提升单机资源利用率 40%,大促期间地址推送SLA保障、资源利用率。&lt;/li>
&lt;li>Triple协议,解决跨网关高效互通的问题,同时部分业务线升级了 Streaming 编程和通信模式。&lt;/li>
&lt;li>统一的流量治理规则,阿里电商场景的路由规则非常复杂,基于实现了云原生体系的结合。&lt;/li>
&lt;li>Service Mesh 解决方案,Thin SDK + Proxyless 的解决方案&lt;/li>
&lt;li>服务柔性,目前正在落地和探索自适应负载均衡、自适应限流等策略。&lt;/li>
&lt;/ul>
&lt;h2 id="2-蚂蚁金服">2 蚂蚁金服&lt;/h2>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670648656034-5b9cfafc-899b-400b-9edb-4e6775febba4.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=46&amp;amp;id=ub2336156&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=66&amp;amp;originWidth=162&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=10659&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u79c80d75-a418-4b93-b16d-6c46304d343&amp;amp;title=&amp;amp;width=113" alt="image.png"> &lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649020622-256254e8-4ef1-43f6-b1c1-a9d29b80bdd1.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=57&amp;amp;id=uf2a1d369&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=114&amp;amp;originWidth=372&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=25430&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u685e5a50-cdcc-45d4-9806-f119df0d378&amp;amp;title=&amp;amp;width=186" alt="image.png"> &lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649055218-13ba694c-68e6-4e88-b981-a8db546839b0.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=59&amp;amp;id=ue3ffce18&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=118&amp;amp;originWidth=414&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=9958&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u25313916-0a19-4b21-a6c1-5da20a95dca&amp;amp;title=&amp;amp;width=207" alt="image.png">&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649088160-85a607de-4737-4e2d-90b2-49bf5b949332.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=81&amp;amp;id=ud329ff16&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=162&amp;amp;originWidth=308&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=61949&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=ubb9c1593-7f65-4c84-997a-34e8e9b7f47&amp;amp;title=&amp;amp;width=154" alt="image.png">&lt;/p>
&lt;p>阿里集团内与蚂蚁体系的互通,目前都跑在 Dubbo3 Triple 互通链路上,与原有基于 HSF 的互通方案对比,Triple 协议链路的 RT 降低了 50%。集团与蚂蚁东西向流量的核心链路,飞猪、手淘、口碑、饿了么、1688、部分导购应用、商品库、评价等业务都采用此方案。&lt;/p>
&lt;h2 id="3-本地生活">3 本地生活&lt;/h2>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670648615017-544b873b-4bc3-4e17-8eac-87bf5d42cbf3.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=53&amp;amp;id=u397bea65&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=106&amp;amp;originWidth=308&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=15119&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u727184c0-ef5b-4156-91b9-8e927657a98&amp;amp;title=&amp;amp;width=154" alt="image.png"> &lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670650306384-a949c14f-191f-4537-b5d5-dc28844ca490.png#clientId=u8f193c04-9173-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=45&amp;amp;id=u0dfaf022&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=84&amp;amp;originWidth=272&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=20212&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u466a9278-caf7-40ad-ba03-d2fb168f83f&amp;amp;title=&amp;amp;width=147" alt="image.png">&lt;/p>
&lt;p>截止 2022 年初,Dubbo3 实现了在饿了么全量业务的生产上线,取代了之前自建的微服务体系,在过去的近一年时间内,饿了么线上有 2000 应用、15w 实例节点平稳跑在 Dubbo3 之上。&lt;/p>
&lt;p>饿了么成功升级 Dubbo3 及应用级服务发现模型,实现了和阿里电商系统互通、单元化体系互通架构的升级,实现了去 proxy 架构的目标,在饿了么关心的服务发现数据链路上:&lt;/p>
&lt;ul>
&lt;li>数据注册与订阅的传输量下降 90%&lt;/li>
&lt;li>注册中心数据存储的总体资源占用下降 90%&lt;/li>
&lt;li>消费端服务框架自身的常驻内存消耗下降达 50%&lt;/li>
&lt;/ul>
&lt;p>集群总体的稳定性、性能都得到明显提升,也为未来容量扩展做好准备。&lt;/p>
&lt;h2 id="4-钉钉">4 钉钉&lt;/h2>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649135935-0d6804cc-00ca-4acb-a7b3-842377d1a6b0.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=57&amp;amp;id=u6203f26b&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=114&amp;amp;originWidth=266&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=10659&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=ufd0b0ffa-2aff-4ba9-83c7-764ae24a31a&amp;amp;title=&amp;amp;width=133" alt="image.png">&lt;/p>
&lt;p>钉钉核心业务在 2021 年实现了 Dubbo3 升级,基于 Triple 协议解决了云上、云下混合部署环境的互通问题。&lt;/p>
&lt;h2 id="5-阿里云">5 阿里云&lt;/h2>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670649159068-ed9ba59b-9e3d-4268-be7e-c327227baa7b.png#clientId=u31fa6127-23e9-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=44&amp;amp;id=u90dcb125&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=88&amp;amp;originWidth=216&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=7597&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=ub661eabe-098b-4f30-b213-bd1b8dea581&amp;amp;title=&amp;amp;width=108" alt="image.png">&lt;/p>
&lt;p>阿里云公有云、转有云核心底座目前正全面迁移到 Dubbo3 体系,取代老版本的 Dubbo2 体系,预计本财年结束就能全部跑在 Dubbo3 之上。
除此之外,阿里云平台上的大部分对外售卖产品,目前都基于 Dubbo3 提供服务或提供 Dubbo3 支持,如微服务引擎MSE、达摩院店小蜜、教育平台、视频云业务等。&lt;/p>
&lt;h2 id="6-菜鸟">6 菜鸟&lt;/h2>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2022/png/54037/1670650418063-31eee85d-9e6a-474c-ade7-4a45fc956ae4.png#clientId=u8f193c04-9173-4&amp;amp;crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;from=paste&amp;amp;height=64&amp;amp;id=ud036f280&amp;amp;margin=%5Bobject%20Object%5D&amp;amp;name=image.png&amp;amp;originHeight=128&amp;amp;originWidth=290&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=13571&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u8d7f509c-983a-483b-9fd9-00cf05f65c8&amp;amp;title=&amp;amp;width=145" alt="image.png">&lt;/p>
&lt;p>2022 年中下旬,菜鸟网络部分核心业务开始推进 Dubbo3 的全面升级,目前生产数据正在采集中。&lt;/p></description></item><item><title>Blog: 序列化协议安全</title><link>https://dubbo.apache.org/zh-cn/blog/1/01/01/%E5%BA%8F%E5%88%97%E5%8C%96%E5%8D%8F%E8%AE%AE%E5%AE%89%E5%85%A8/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/1/01/01/%E5%BA%8F%E5%88%97%E5%8C%96%E5%8D%8F%E8%AE%AE%E5%AE%89%E5%85%A8/</guid><description>
&lt;p>Dubbo3.0在序列化协议安全方面进行了升级加固,推荐使用Tripe协议非Wrapper模式。
该协议默认安全,但需要开发人员编写IDL文件。&lt;/p>
&lt;p>Triple协议Wrapper模式下,允许兼容其它序列化数据,提供了良好的兼容性。但其它协议可能存在反序列化安全缺陷,对于Hession2协议,高安全属性用户应当按照samples代码指示,开启白名单模式,框架默认会开启黑名单模式,拦截恶意调用。&lt;/p>
&lt;p>不建议使用其它序列化协议,当攻击者可访问Provider接口时,其它序列化协议的安全缺陷,可能导致 Povider 接口命令执行。&lt;/p>
&lt;p>若必须使用其它序列化协议,同时希望具备一定安全性。应当开启Token鉴权机制,防止未鉴权的不可信请求来源威胁Provider的安全性。开启Token鉴权机制时,应当同步开启注册中心的鉴权功能。&lt;/p></description></item><item><title>Blog: 工商银行 Dubbo3 应用级服务发现实践</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%B7%A5%E5%95%86%E9%93%B6%E8%A1%8C-dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%AE%9E%E8%B7%B5/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%B7%A5%E5%95%86%E9%93%B6%E8%A1%8C-dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;h3 id="问题分析">问题分析&lt;/h3>
&lt;p>以下是经典的 Dubbo 的工作原理图,服务提供者和消费者通过注册中心协调实现地址的自动发现。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/icbc/icbc-analyze.png" alt="icbc-analyze">&lt;/p>
&lt;p>工商银行面临的主要瓶颈是在注册中心与服务消费端,接口级别地址的数量已经是亿级规模,一方面存储容量达到瓶颈、另一方面推送效率明显下降;而在消费端这一侧,Dubbo2 框架常驻内存已超 40%,每次地址推送带来的 cpu 等资源消耗率也非常高,影响正常的业务调用。&lt;/p>
&lt;p>这是 Dubbo2 接口级服务发现架构在大规模集群场景下的固有问题(具体原因请查看应用级服务发现原理解析),通过常规的性能优化无法从根本上解决问题。因此工商银行采用了 Dubbo3 中提出的应用级服务发现模型,经过实测,新的服务发现模型能实现节点到注册中心间数据传输量 90% 的下降,这就使得注册中心的压力极大降低,同时消费端的框架常驻内存也实现超 50% 下降。&lt;/p>
&lt;h3 id="压测数据">压测数据&lt;/h3>
&lt;p>下面是工商银行联合 Dubbo 社区给出的一组基于真实服务特点给出的模拟压测数据。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/icbc/icbc-data1.png" alt="icbc-data1">&lt;/p>
&lt;p>上图是对使用了应用级服务发现的消费端进程采样的内存对比数据。其中横轴是不同的 Dubbo 版本,纵轴是实际采样到的内存表现,可以看到 Dubbo 2.6、2.7 版本表现几乎一致,而升级到 3.0 版本后,即使不升级应用级服务发现,内存也降低接近 40%,而当切换到应用级服务发现之后,内存占用下降到只有原来的 30%。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/icbc/icbc-data2.png" alt="icbc-data2">&lt;/p>
&lt;p>上图是消费端的 GC 情况统计,同样的,横轴是不同的 Dubbo 版本,纵轴是实际采样到的 GC 表现。这里的压测数据,是通过模拟注册中心不停的往消费端进程推送地址列表的场景得到的。可以看到 Dubbo 2.6、2.7 版本表现几乎一致,而在 3.0 版本中切换到应用级服务发现之后,GC 已经趋近于零次。&lt;/p></description></item><item><title>Blog: 饿了么全站成功升级 Dubbo3</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E9%A5%BF%E4%BA%86%E4%B9%88%E5%85%A8%E7%AB%99%E6%88%90%E5%8A%9F%E5%8D%87%E7%BA%A7-dubbo3/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E9%A5%BF%E4%BA%86%E4%B9%88%E5%85%A8%E7%AB%99%E6%88%90%E5%8A%9F%E5%8D%87%E7%BA%A7-dubbo3/</guid><description>
&lt;h3 id="升级目标">升级目标&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/eleme/elem-arc.png" alt="elem-arc">&lt;/p>
&lt;p>这里是饿了么的的基本部署架构图。&lt;/p>
&lt;p>在升级之前,饿了么的微服务框架采用的是 HSF2,跨单元的 RPC 调用是通过 proxy 中转代理,在这个过程中 proxy 所承载的机器数和流量迅速增长,比较突出的一点是 proxy 在订阅所有的地址数据后资源消耗和稳定性都收到严峻挑战。&lt;/p>
&lt;p>通过全站升级 Dubbo3,业务线期望达到两个目标:&lt;/p>
&lt;ul>
&lt;li>将地址模型切换到应用级服务发现大幅减轻中心化节点和消费端节点的资源消耗压力。&lt;/li>
&lt;li>以应用级服务发现架构下的全局共享注册中心取代 proxy 模式,实现跨单元节点通信直连。&lt;/li>
&lt;/ul>
&lt;h3 id="升级过程">升级过程&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/eleme/elem-upgrade1.png" alt="eleme-upgrade1">&lt;/p>
&lt;p>不论是针对 Dubbo2 还是 HSF2,我们都做了全面的 API 兼容,因此 Dubbo3 基本可以做到零改造升级,并且每个应用都是独立透明升级,不需要关心它的上下游应用的升级状态,因为 Dubbo3 升级之后不论是从地址发现模型还是协议的默认行为都保持与 2.0 版本兼容,用户可以在任意时间点对任意应用按需切换 Dubbo3 行为。
如右图所示,我们模拟展示了饿了么集群 Dubbo3 升级过程的一个中间状态,其中灰色标记的是老版本 HSF2 应用,橙色和绿色标记的是已经升级 Dubbo3 的应用,橙色部分的应用及其调用链路代表不但已经升级到 Dubbo3,同时也完成了 Dubbo3 行为的切换,在这里是指已经切换到了应用级地址模型。这里的升级过程主要为了说明 Dubbo3 框架升级的兼容性和独立性。&lt;/p>
&lt;p>接下来,我们详细分析一下橙色部分节点往 Dubbo3 应用级发现迁移的具体过程。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/eleme/elem-upgrade-provider.png" alt="elem-upgrade-provider">&lt;/p>
&lt;p>首先看 Provider 侧,服务提供者在升级 Dubbo3 后会默认保持双注册行为,即同时注册接口级地址和应用级地址到注册中心,一方面保持兼容,另一方面为未来消费端迁移做好准备。双注册的开关可通过 -Ddubbo.application.register-mode=al/interface/interface控制,我们推荐保持双注册的默认行为以减少后续迁移成本。&lt;/p>
&lt;p>大家可能担心双注册给注册中心带来的存储压力,实际上在应用级服务发现模型下这并不是一个问题,因为大家如果回想前面我们对应用级服务发现工作原理的分析,注册地址已经被大幅精简,根据我们实际推算,每多注册一条应用级服务发现 URL 地址,只会增加 0.1% ~ 1% 的额外开销。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/eleme/elem-upgrade-consumer.png" alt="elem-upgrade-consumer">&lt;/p>
&lt;p>与提供端类似,要实现平滑迁移消费端也要经历双订阅的过程,流程上就不再赘述。消费端的双订阅行为也可通过规则或开关进行动态调整,控制消费端的消费的某个服务、应用迁移到应用级地址模型;除此之外,Dubbo3 还内置了自动决策机制,在发现应用级地址可用的情况下即会自动完成切换,并且这个行为是默认的。&lt;/p>
&lt;p>以下是消费端双订阅时的选址流程:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/eleme/elem-upgrade-consumer1.png" alt="elem-upgrade-consumer1">&lt;/p>
&lt;h3 id="升级效果">升级效果&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/user/eleme/elem-result.png" alt="elem-result">&lt;/p>
&lt;p>饿了么成功升级 Dubbo3 及应用级服务发现模型,实现了去 proxy 架构的目标,在我们关心的服务发现数据链路上:&lt;/p>
&lt;ul>
&lt;li>数据注册与订阅的传输量下降 90%&lt;/li>
&lt;li>注册中心数据存储的总体资源占用下降 90%&lt;/li>
&lt;li>消费端服务框架自身的常驻内存消耗下降达 50%
集群总体的稳定性、性能都得到明显提升,也为未来容量扩展做好准备。&lt;/li>
&lt;/ul></description></item><item><title>Blog: 店小蜜升级 Triple 协议</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%BA%97%E5%B0%8F%E8%9C%9C%E5%8D%87%E7%BA%A7-triple-%E5%8D%8F%E8%AE%AE/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%BA%97%E5%B0%8F%E8%9C%9C%E5%8D%87%E7%BA%A7-triple-%E5%8D%8F%E8%AE%AE/</guid><description>
&lt;h1 id="前言">前言&lt;/h1>
&lt;p>阿里云-达摩院-云小蜜对话机器人产品基于深度机器学习技术、自然语言理解技术和对话管理技术,为企业提供多引擎、多渠道、多模态的对话机器人服务。17年云小蜜对话机器人在公共云开始公测,同期在混合云场景也不断拓展。为了同时保证公共云、混合云发版效率和稳定性,权衡再三我们采用了1-2个月一个大版本迭代。
经过几年发展,为了更好支撑业务发展,架构升级、重构总是一个绕不过去的坎,为了保证稳定性每次公共云发版研发同学都要做两件事:&lt;/p>
&lt;pre>&lt;code>1. 梳理各个模块相较线上版本接口依赖变化情况,决定十几个应用的上线顺序、每批次发布比例;
2. 模拟演练上述1产出的发布顺序,保证后端服务平滑升级,客户无感知;
&lt;/code>&lt;/pre>
&lt;p>上述 1、2 动作每次都需要 2-3 周左右的时间梳理、集中演练,但是也只能保证开放的PaaS API平滑更新;&lt;/p>
&lt;p>控制台服务因为需要前端、API、后端保持版本一致才能做到体验无损(如果每次迭代统一升级API版本开发、协同成本又会非常大),权衡之下之前都是流量低谷期上线,尽量缩短发布时间,避免部分控制台模块偶发报错带来业务问题。针对上面问题,很早之前就考虑过用蓝绿发布、灰度等手段解决,但是无奈之前对话机器人在阿里云内部业务区域,该不再允许普通云产品扩容,没有冗余的机器,流量治理完全没法做。&lt;/p>
&lt;h1 id="迁移阿里云云上">迁移阿里云云上&lt;/h1>
&lt;p>带着上面的问题,终于迎来的 2021 年 9 月份,云小蜜将业务迁移至阿里云云上。&lt;/p>
&lt;h2 id="dubbo3-的实践">Dubbo3 的实践&lt;/h2>
&lt;p>“当时印象最深的就是这张图,虽然当时不知道中间件团队具体要做什么事情,但是记住了两个关键词:三位一体、红利。没想到在2021年底,真真切切享受到了这个红利。”&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/yunxiaomi-1.png" alt="image1">&lt;/p>
&lt;p>云小蜜使用的是集团内部的HSF服务框架,需要迁移至阿里云云上,并且存在阿里云云上与阿里内部业务域的互通、互相治理的诉求。云小蜜的公共服务部署在公有云VPC,部分依赖的数据服务部署在内部,内部与云上服务存在RPC互调的诉求,其实属于混合云的典型场景。
简单整理了下他们的核心诉求,概括起来有以下三点吧:希望尽可能采用开源方案,方便后续业务推广;在网络通信层面需要保障安全性;对于业务升级改造来说需要做到低成本。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/yunxiaomi-2.png" alt="image2">&lt;/p>
&lt;p>在此场景下,经过许多讨论与探索,方案也敲定了下来&lt;/p>
&lt;ul>
&lt;li>全链路升级至开源 Dubbo3.0,云原生网关默认支持Dubbo3.0,实现透明转发,网关转发RT小于1ms&lt;/li>
&lt;li>利用 Dubbo3.0 支持HTTP2特性,云原生网关之间采用 mTLS 保障安全&lt;/li>
&lt;li>利用云原生网关默认支持多种注册中心的能力,实现跨域服务发现对用户透明,业务侧无需做任何额外改动&lt;/li>
&lt;li>业务侧升级SDK到支持 Dubbo3.0,配置发布 Triple 服务即可,无需额外改动&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>解决了互通、服务注册发现的问题之后,就是开始看如何进行服务治理方案了&lt;/strong>&lt;/p>
&lt;h1 id="阿里云云上流量治理">阿里云云上流量治理&lt;/h1>
&lt;p>迁移至阿里云云上之后,流量控制方案有非常多,比如集团内部的全链路方案、集团内单元化方案等等。&lt;/p>
&lt;h2 id="设计目标和原则">设计目标和原则&lt;/h2>
&lt;ol>
&lt;li>要引入一套流量隔离方案,上线过程中,新、旧两个版本服务同时存在时,流量能保证在同一个版本的“集群”里流转,这样就能解决重构带来的内部接口不兼容问题。&lt;/li>
&lt;li>要解决上线过程中控制台的平滑性问题,避免前端、后端、API更新不一致带来的问题。&lt;/li>
&lt;li>无上线需求的应用,可以不参与上线。&lt;/li>
&lt;li>资源消耗要尽量少,毕竟做产品最终还是要考虑成本和利润。&lt;/li>
&lt;/ol>
&lt;h2 id="方案选型">方案选型&lt;/h2>
&lt;ol>
&lt;li>集团内部的全链路方案:目前不支持阿里云云上&lt;/li>
&lt;li>集团内单元化方案:主要解决业务规模、容灾等问题,和我们碰到的问题不一样&lt;/li>
&lt;li>搭建独立集群,版本迭代时切集群:成本太大&lt;/li>
&lt;li>自建:在同一个集群隔离新、老服务,保证同一个用户的流量只在同版本服务内流转&lt;/li>
&lt;/ol>
&lt;p>以RPC为例:&lt;/p>
&lt;ul>
&lt;li>方案一:通过开发保证,当接口不兼容升级时,强制要求升级HSF version,并行提供两个版本的服务; 缺点是一个服务变更,关联使用方都要变更,协同成本特别大,并且为了保持平滑,新老接口要同时提供服务,维护成本也比较高&lt;/li>
&lt;li>方案二:给服务(机器)按版本打标,通过RPC框架的路由规则,保证流量优先在同版本内流转&lt;/li>
&lt;/ul>
&lt;h2 id="全链路灰度方案">全链路灰度方案&lt;/h2>
&lt;p>就当1、2、3、4都觉得不完美,一边调研一边准备自建方案5的时候,兜兜绕绕拿到了阿里云 MSE 微服务治理团队&lt;a href="https://yuque.antfin.com/docs/share/a8df43ac-3a3b-4af4-a443-472828884a5d?#">《20分钟获得同款企业级全链路灰度能力》&lt;/a>,方案中思路和准备自建的思路完全一致,也是利用了RPC框架的路由策略实现的流量治理,并且实现了产品化(微服务引擎-微服务治理中心),同时,聊了两次后得到几个“支持”,以及几个“后续可以支持”后,好像很多事情变得简单了&amp;hellip;&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/yunxiaomi-3.png" alt="image3">&lt;/p>
&lt;p>从上图可以看到,各个应用均需要搭建基线(base)环境和灰度(gray)环境,除了流量入口-业务网关以外,下游各个业务模块按需部署灰度(gray)环境,如果某次上线某模块没有变更则无需部署。&lt;/p>
&lt;h3 id="各个中间件的治理方案">各个中间件的治理方案&lt;/h3>
&lt;ol>
&lt;li>Mysql、ElasticSearch:持久化或半持久化数据,由业务模块自身保证数据结构兼容升级;&lt;/li>
&lt;li>Redis:由于对话产品会有多轮问答场景,问答上下文是在Redis里,如果不兼容则上线会导致会话过程中的C端用户受影响,因此目前Redis由业务模块自身保证数据结构兼容升级;&lt;/li>
&lt;li>配置中心:基线(base)环境、灰度(gray)环境维护两套全量配置会带来较大工作量,为了避免人工保证数据一致性成本,基线(base)环境监听dataId,灰度(gray)环境监听gray.dataId如果未配置gray.dataId则自动监听dataId;(云小蜜因为在18年做混合云项目为了保证混合云、公共云使用一套业务代码,建立了中间件适配层,本能力是在适配层实现)&lt;/li>
&lt;li>RPC服务:使用阿里云 one agent 基于Java Agent技术利用Dubbo框架的路由规则实现,无需修改业务代码;&lt;/li>
&lt;/ol>
&lt;p>应用只需要加一点配置:&lt;/p>
&lt;ul>
&lt;li>1)linux环境变量
alicloud.service.tag=gray标识灰度,基线无需打标
profiler.micro.service.tag.trace.enable=true标识经过该机器的流量,如果没有标签则自动染上和机器相同的标签,并向后透传&lt;/li>
&lt;li>2)JVM参数,标识开启MSE微服务流量治理能力** SERVICE_OPTS=&lt;strong>&amp;quot;$&lt;/strong>{SERVICE_OPTS}** -Dmse.enable=true&amp;quot;**&lt;/li>
&lt;/ul>
&lt;h3 id="流量管理方案">流量管理方案&lt;/h3>
&lt;p>流量的分发模块决定流量治理的粒度和管理的灵活程度。&lt;/p>
&lt;p>对话机器人产品需要灰度发布、蓝绿发布目前分别用下面两种方案实现:&lt;/p>
&lt;ol>
&lt;li>灰度发布:
部分应用单独更新,使用POP的灰度引流机制,该机制支持按百分比、UID的策略引流到灰度环境&lt;/li>
&lt;li>蓝绿发布:
&lt;ul>
&lt;li>1)部署灰度(gray)集群并测试:测试账号流量在灰度(gray)集群,其他账号流量在基线(base)集群&lt;/li>
&lt;li>2)线上版本更新:所有账号流量在灰度(gray)集群&lt;/li>
&lt;li>3)部署基线(base)集群并测试:测试账号流量在基线(base)集群,其他账号流量在灰度(gray)集群&lt;/li>
&lt;li>4)流量回切到基线(base)集群并缩容灰度(gray)环境:所有账号流量在基线(base)集群&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="全链路落地效果">全链路落地效果&lt;/h2>
&lt;p>上线后第一次发布的效果:“目前各个模块新版本的代码已经完成上线,含发布、功能回归一共大约花费2.5小时,相较之前每次上线到凌晨有很大提升。”
MSE 微服务治理全链路灰度方案满足了云小蜜业务在高速发展情况下快速迭代和小心验证的诉求,通过JavaAgent技术帮助云小蜜快速落地了企业级全链路灰度能力。
流量治理随着业务发展会有更多的需求,下一步,我们也会不断和微服务治理产品团队,扩充此解决方案的能力和使用场景,比如:rocketmq、schedulerx的灰度治理能力。&lt;/p>
&lt;h2 id="更多的微服务治理能力">更多的微服务治理能力&lt;/h2>
&lt;p>使用 MSE 服务治理后,发现还有更多开箱即用的治理能力,能够大大提升研发的效率。包含服务查询、服务契约、服务测试等等。这里要特别提一下就是云上的服务测试,服务测试即为用户提供一个云上私网 Postman ,让我们这边能够轻松调用自己的服务。我们可以忽略感知云上复杂的网络拓扑结构,无需关系服务的协议,无需自建测试工具,只需要通过控制台即可实现服务调用。支持 Dubbo 3.0 框架,以及 Dubbo 3.0 主流的 Triple 协议。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/yunxiaomi-4.png" alt="image4">&lt;/p>
&lt;h1 id="结束语">结束语&lt;/h1>
&lt;p>最终云小蜜对话机器人团队成功落地了全链路灰度功能,解决了困扰团队许久的发布效率问题。在这个过程中我们做了将部分业务迁移至阿里云云上、服务框架升级至Dubbo3.0、选择MSE微服务治理能力等等一次次新的选择与尝试。“世上本没有路,走的人多了便成了路”。经过我们工程师一次又一次的探索与实践,能够为更多的同学沉淀出一个个最佳实践。我相信这些最佳实践将会如大海中璀璨的明珠般,经过生产实践与时间的打磨将会变得更加熠熠生辉。&lt;/p></description></item><item><title>Blog: 瓜子二手车 Dubbo 实践</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E7%93%9C%E5%AD%90%E4%BA%8C%E6%89%8B%E8%BD%A6-dubbo-%E5%AE%9E%E8%B7%B5/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E7%93%9C%E5%AD%90%E4%BA%8C%E6%89%8B%E8%BD%A6-dubbo-%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>  随着瓜子业务的不断发展,系统规模在逐渐扩大,目前在瓜子的私有云上已经运行着数百个dubbo应用,上千个dubbo实例。瓜子各部门业务迅速发展,版本没有来得及统一,各个部门都有自己的用法。随着第二机房的建设,dubbo版本统一的需求变得越发迫切。几个月前,公司发生了一次与dubbo相关的生产事故,成为了公司dubbo版本升级的诱因。&lt;/p>
&lt;p>  接下来,我会从这次事故开始,讲讲我们这段时间所做的dubbo版本升级的历程以及dubbo后续多机房的方案。&lt;/p>
&lt;h2 id="一ephermal节点未及时删除导致provider不能恢复注册的问题修复">一、Ephermal节点未及时删除导致provider不能恢复注册的问题修复&lt;/h2>
&lt;h3 id="事故背景">事故背景&lt;/h3>
&lt;p>  在生产环境,瓜子内部各业务线共用一套zookeeper集群作为dubbo的注册中心。2019年9月份,机房的一台交换机发生故障,导致zookeeper集群出现了几分钟的网络波动。在zookeeper集群恢复后,正常情况下dubbo的provider应该会很快重新注册到zookeeper上,但有一小部分的provider很长一段时间没有重新注册到zookeeper上,直到手动重启应用后才恢复注册。&lt;/p>
&lt;h3 id="排查过程">排查过程&lt;/h3>
&lt;p>  首先,我们统计了出现这种现象的dubbo服务的版本分布情况,发现在大多数的dubbo版本中都存在这种问题,且发生问题的服务比例相对较低,在github中我们也未找到相关问题的issues。因此,推断这是一个尚未修复的且在网络波动情况的场景下偶现的问题。&lt;/p>
&lt;p>  接着,我们便将出现问题的应用日志、zookeeper日志与dubbo代码逻辑进行相互印证。在应用日志中,应用重连zookeeper成功后provider立刻进行了重新注册,之后便没有任何日志打印。而在zookeeper日志中,注册节点被删除后,并没有重新创建注册节点。对应到dubbo的代码中,只有在&lt;code>FailbackRegistry.register(url)&lt;/code>的&lt;code>doRegister(url)&lt;/code>执行成功或线程被挂起的情况下,才能与日志中的情况相吻合。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">register&lt;/span>&lt;span style="color:#719e07">(&lt;/span>URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failedRegistered&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failedUnregistered&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Sending a registration request to the server side
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> doRegister&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Throwable t &lt;span style="color:#719e07">=&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// If the startup detection is opened, the Exception is thrown directly.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> check &lt;span style="color:#719e07">=&lt;/span> getUrl&lt;span style="color:#719e07">().&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CHECK_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CHECK_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#719e07">!&lt;/span>Constants&lt;span style="color:#719e07">.&lt;/span>CONSUMER_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> skipFailback &lt;span style="color:#719e07">=&lt;/span> t &lt;span style="color:#719e07">instanceof&lt;/span> SkipFailbackWrapperException&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>check &lt;span style="color:#719e07">||&lt;/span> skipFailback&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>skipFailback&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t &lt;span style="color:#719e07">=&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getCause&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to register &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; to registry &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> getUrl&lt;span style="color:#719e07">().&lt;/span>getAddress&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to register &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, waiting for retry, cause: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Record a failed registration request to a failed list, retry regularly
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> failedRegistered&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>  在继续排查问题前,我们先普及下这些概念:dubbo默认使用curator作为zookeeper的客户端,curator与zookeeper是通过session维持连接的。当curator重连zookeeper时,若session未过期,则继续使用原session进行连接;若session已过期,则创建新session重新连接。而ephemeral节点与session是绑定的关系,在session过期后,会删除此session下的ephemeral节点。&lt;/p>
&lt;p>  继续对&lt;code>doRegister(url)&lt;/code>的代码进行进一步排查,我们发现在&lt;code>CuratorZookeeperClient.createEphemeral(path)&lt;/code>方法中有这么一段逻辑:在&lt;code>createEphemeral(path)&lt;/code>捕获了&lt;code>NodeExistsException&lt;/code>,创建ephemeral节点时,若此节点已存在,则认为ephemeral节点创建成功。这段逻辑初看起来并没有什么问题,且在以下两种常见的场景下表现正常:&lt;/p>
&lt;ol>
&lt;li>Session未过期,创建Ephemeral节点时原节点仍存在,不需要重新创建&lt;/li>
&lt;li>Session已过期,创建Ephemeral节点时原节点已被zookeeper删除,创建成功&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">createEphemeral&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String path&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">().&lt;/span>withMode&lt;span style="color:#719e07">(&lt;/span>CreateMode&lt;span style="color:#719e07">.&lt;/span>EPHEMERAL&lt;span style="color:#719e07">).&lt;/span>forPath&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>NodeExistsException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>  但是实际上还有一种极端场景,&lt;strong>zookeeper的Session过期与删除Ephemeral节点不是原子性的&lt;/strong>,也就是说客户端在得到Session过期的消息时,Session对应的Ephemeral节点可能还未被zookeeper删除。此时dubbo去创建Ephemeral节点,发现原节点仍存在,故不重新创建。待Ephemeral节点被zookeeper删除后,便会出现dubbo认为重新注册成功,但实际未成功的情况,也就是我们在生产环境遇到的问题。&lt;/p>
&lt;p>  此时,问题的根源已被定位。定位问题之后,我们与dubbo社区交流,发现考拉的同学也遇到过同样的问题,更确定了这个原因。&lt;/p>
&lt;h3 id="问题的复现与修复">问题的复现与修复&lt;/h3>
&lt;p>  定位到问题之后,我们便开始尝试本地复现。由于zookeeper的Session过期但Ephemeral节点未被删除的场景直接模拟比较困难,我们通过修改zookeeper源码,在Session过期与删除Ephemeral节点的逻辑中增加了一段休眠时间,间接模拟出这种极端场景,并在本地复现了此问题。&lt;/p>
&lt;p>  在排查问题的过程中,我们发现kafka的旧版本在使用zookeeper时也遇到过类似的问题,并参考kafka关于此问题的修复方案,确定了dubbo的修复方案。在创建Ephemeral节点捕获到&lt;code>NodeExistsException&lt;/code>时进行判断,若Ephemeral节点的SessionId与当前客户端的SessionId不同,则删除并重建Ephemeral节点。在内部修复并验证通过后,我们向社区提交了issues及pr。&lt;/p>
&lt;p>  kafka类似问题issues:https://issues.apache.org/jira/browse/KAFKA-1387&lt;/p>
&lt;p>  dubbo注册恢复问题issues:https://github.com/apache/dubbo/issues/5125&lt;/p>
&lt;h2 id="二瓜子的dubbo升级历程">二、瓜子的dubbo升级历程&lt;/h2>
&lt;p>  上文中的问题修复方案已经确定,但我们显然不可能在每一个dubbo版本上都进行修复。在咨询了社区dubbo的推荐版本后,我们决定在dubbo2.7.3版本的基础上,开发内部版本修复来这个问题。并借这个机会,开始推动公司dubbo版本的统一升级工作。&lt;/p>
&lt;h3 id="为什么要统一dubbo版本">为什么要统一dubbo版本&lt;/h3>
&lt;ol>
&lt;li>统一dubbo版本后,我们可以在此版本上内部紧急修复一些dubbo问题(如上文的dubbo注册故障恢复失效问题)。&lt;/li>
&lt;li>瓜子目前正在进行第二机房的建设,部分dubbo服务也在逐渐往第二机房迁移。统一dubbo版本,也是为dubbo的多机房做铺垫。&lt;/li>
&lt;li>有利于我们后续对dubbo服务的统一管控。&lt;/li>
&lt;li>dubbo社区目前的发展方向与我们公司现阶段对dubbo的一些诉求相吻合,如支持gRPC、云原生等。&lt;/li>
&lt;/ol>
&lt;h3 id="为什么选择dubbo273">为什么选择dubbo2.7.3&lt;/h3>
&lt;ol>
&lt;li>在我们之前,携程与dubbo社区合作进行了携程dubbo内部版本的升级,并在社区2.7.3版本上修复了很多兼容性问题。感谢携程的同学帮我们踩坑~&lt;/li>
&lt;li>dubbo2.7.3版本在当时虽然是最新的版本,但已经发布了2个月的时间,从社区issues反馈来看,dubbo2.7.3相对dubbo2.7之前的几个版本,在兼容性方面要好很多。&lt;/li>
&lt;li>我们也咨询了dubbo社区的同学,推荐升级版本为2.7.3。&lt;/li>
&lt;/ol>
&lt;h3 id="内部版本定位">内部版本定位&lt;/h3>
&lt;p>  基于社区dubbo2.7.3版本开发的dubbo内部版本属于过渡性质的版本,目的是为了修复线上provider不能恢复注册的问题,以及一些社区dubbo2.7.3的兼容性问题。瓜子的dubbo最终还是要跟随社区的版本,而不是开发自已的内部功能。因此我们在dubbo内部版本中修复的所有问题均与社区保持了同步,以保证后续可以兼容升级到社区dubbo的更高版本。&lt;/p>
&lt;h3 id="兼容性验证与升级过程">兼容性验证与升级过程&lt;/h3>
&lt;p>  我们在向dubbo社区的同学咨询了版本升级方面的相关经验后,于9月下旬开始了dubbo版本的升级工作。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>初步兼容性验证&lt;/strong>
首先,我们梳理了一些需要验证的兼容性case,针对公司内部使用较多的dubbo版本,与dubbo2.7.3一一进行了兼容性验证。经验证,除dubboX外,dubbo2.7.3与其他dubbo版本均兼容。dubboX由于对dubbo协议进行了更改,与dubbo2.7.3不兼容。&lt;/li>
&lt;li>&lt;strong>生产环境兼容性验证&lt;/strong>
在初步验证兼容性通过后,我们与业务线合作,挑选了一些重要程度较低的项目,在生产环境对dubbo2.7.3与其他版本的兼容性进行了进一步验证。并在内部版本修复了一些兼容性问题。&lt;/li>
&lt;li>&lt;strong>推动公司dubbo版本升级&lt;/strong>
在10月初,完成了dubbo兼容性验证后,我们开始在各个业务线推动dubbo的升级工作。截止到12月初,已经有30%的dubbo服务的完成了版本升级。按照排期,预计于2020年3月底前完成公司dubbo版本的统一升级。&lt;/li>
&lt;/ol>
&lt;h3 id="兼容性问题汇总">兼容性问题汇总&lt;/h3>
&lt;p>  在推动升级dubbo2.7.3版本的过程整体上比较顺利,当然也遇到了一些兼容性问题:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>创建zookeeper节点时提示没有权限&lt;/strong>
dubbo配置文件中已经配置了zookeeper的用户名密码,但在创建zookeeper节点时却抛出&lt;code>KeeperErrorCode = NoAuth&lt;/code>的异常,这种情况分别对应两个兼容性问题:&lt;/p>
&lt;ul>
&lt;li>issues:https://github.com/apache/dubbo/issues/5076
dubbo在未配置配置中心时,默认使用注册中心作为配置中心。通过注册中心的配置信息初始化配置中心配置时,由于遗漏了用户名密码,导致此问题。&lt;/li>
&lt;li>issues:https://github.com/apache/dubbo/issues/4991
dubbo在建立与zookeeper的连接时会根据zookeeper的address复用之前已建立的连接。当多个注册中心使用同一个address,但权限不同时,就会出现&lt;code>NoAuth&lt;/code>的问题。
参考社区的pr,我们在内部版本进行了修复。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>curator版本兼容性问题&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>dubbo2.7.3与低版本的curator不兼容,因此我们默认将curator版本升级至4.2.0&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.curator&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>curator-framework&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>4.2.0&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.curator&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>curator-recipes&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>4.2.0&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>分布式调度框架elastic-job-lite强依赖低版本的curator,与dubbo2.7.3使用的curator版本不兼容,这给dubbo版本升级工作带来了一定阻塞。考虑到elastic-job-lite已经很久没有人进行维护,目前一些业务线计划将elastic-job-lite替换为其他的调度框架。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>openFeign与dubbo兼容性问题&lt;/strong>
issues: &lt;a href="https://github.com/apache/dubbo/issues/3990">https://github.com/apache/dubbo/issues/3990&lt;/a>
dubbo的ServiceBean监听spring的ContextRefreshedEvent,进行服务暴露。openFeign提前触发了ContextRefreshedEvent,此时ServiceBean还未完成初始化,于是就导致了应用启动异常。
参考社区的pr,我们在内部版本修复了此问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>RpcException兼容性问题&lt;/strong>
dubbo低版本consumer不能识别dubbo2.7版本provider抛出的&lt;code>org.apache.dubbo.rpc.RpcException&lt;/code>。因此,在consumer全部升级到2.7之前,不建议将provider的&lt;code>com.alibaba.dubbo.rpc.RpcException&lt;/code>改为&lt;code>org.apache.dubbo.rpc.RpcException&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>qos端口占用&lt;/strong>
dubbo2.7.3默认开启qos功能,导致一些混部在物理机的dubbo服务升级时出现qos端口占用问题。关闭qos功能后恢复。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>自定义扩展兼容性问题&lt;/strong>
业务线对于dubbo的自定义扩展比较少,因此在自定义扩展的兼容性方面暂时还没有遇到比较难处理的问题,基本上都是变更package导致的问题,由业务线自行修复。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>skywalking agent兼容性问题&lt;/strong>
我们项目中一般使用skywalking进行链路追踪,由于skywalking agent6.0的plugin不支持dubbo2.7,因此统一升级skywalking agent到6.1。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="三dubbo多机房方案">三、dubbo多机房方案&lt;/h2>
&lt;p>  瓜子目前正在进行第二机房的建设工作,dubbo多机房是第二机房建设中比较重要的一个话题。在dubbo版本统一的前提下,我们就能够更顺利的开展dubbo多机房相关的调研与开发工作。&lt;/p>
&lt;h3 id="初步方案">初步方案&lt;/h3>
&lt;p>  我们咨询了dubbo社区的建议,并结合瓜子云平台的现状,初步确定了dubbo多机房的方案。&lt;/p>
&lt;ol>
&lt;li>在每个机房内,部署一套独立的zookeeper集群。集群间信息不同步。这样就没有了zookeeper集群跨机房延迟与数据不同步的问题。&lt;/li>
&lt;li>dubbo服务注册时,仅注册到本机房的zookeeper集群;订阅时,同时订阅两个机房的zookeeper集群。&lt;/li>
&lt;li>实现同机房优先调用的路由逻辑。以减少跨机房调用导致的不必要网络延迟。&lt;/li>
&lt;/ol>
&lt;h3 id="同机房优先调用">同机房优先调用&lt;/h3>
&lt;p>  dubbo同机房优先调用的实现比较简单,相关逻辑如下:&lt;/p>
&lt;ol>
&lt;li>瓜子云平台默认将机房的标志信息注入容器的环境变量中。&lt;/li>
&lt;li>provider暴露服务时,读取环境变量中的机房标志信息,追加到待暴露服务的url中。&lt;/li>
&lt;li>consumer调用provider时,读取环境变量中的机房标志信息,根据路由策略优先调用具有相同标志信息的provider。&lt;/li>
&lt;/ol>
&lt;p>  针对以上逻辑,我们简单实现了dubbo通过环境变量进行路由的功能,并向社区提交了pr。
  dubbo通过环境变量路由pr: &lt;a href="https://github.com/apache/dubbo/pull/5348">https://github.com/apache/dubbo/pull/5348&lt;/a>&lt;/p></description></item><item><title>Blog: 小米与 Dubbo 社区的合作</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%B0%8F%E7%B1%B3%E4%B8%8E-dubbo-%E7%A4%BE%E5%8C%BA%E7%9A%84%E5%90%88%E4%BD%9C/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%B0%8F%E7%B1%B3%E4%B8%8E-dubbo-%E7%A4%BE%E5%8C%BA%E7%9A%84%E5%90%88%E4%BD%9C/</guid><description>
&lt;p>小米一直在积极拥抱开源社区并提交贡献,参与Dubbo3建设发布以来,在内部业务也积极推进升级工作,目前实例数已经升级到了一定的比例 。升级过程总体平稳,稳定性指标正常,性能提升明显,率先升级完成的应用更早拥有了mesh化的条件。从升级后的数据表现来看,Dubbo3改变以往接口粒度的注册发现方式为应用粒度的注册发现方式,这样带来了注册中心存储和运行的更稳定,降低运维成本;使用protobuf协议进行序列化与反序列化,性能和字节大小均提升数量级;完全兼容gprc,给小米这样多言语并存的服务环境带来了极大便利。&lt;/p>
&lt;p>Xiaomi is devoted to making continuous contribution to the open source community. Since the introduction of Dubbo3, internal projects are rapidly upgrading to the latest version of Dubbo. Currently, At present the numbers of instances has been upgrade to a certain proportion. Not only performance improvements have been seen, but services are also running smoothly with improvements in availability. Statistics provide proof that Dubbo’s switch from api-level discovery to application-level discovery has improved the availability and reliability of service discovery, which leads to lower operations cost. In addition, using ProtoBuf for serialization and deserialization has reduced data exchange size. Lastly, full compatibility with grpc provides convenience to Xiaomi’s multi-language development environment.&lt;/p>
&lt;p>Dubbo3 基于 Dubbo2 演进而来,Dubbo3 在云原生基础设施适配、服务注册发现、面向下一代协议设计等几大方向上进行了全面升级。并且往 3.0 版本的升级过程将会是完全透明的,用户无需做任何业务改造,就可以直接升级到 Dubbo 3.0。
下面从云原生适配、全新服务发现模型、通信协议、升级兼容四个方面详细介绍。&lt;/p>
&lt;p>Having evolved from Dubbo2, Dubbo3 is a major upgrade in several areas including, Cloud Native adaptation, service discovery, communication protocols. Furthermore, upgrading to version 3.0 requires no changes in application code. The following is a detailed introduction to Dubbo3’s features.&lt;/p>
&lt;h3 id="云原生适配--cloud-native-adaptation">云原生适配 Cloud Native Adaptation&lt;/h3>
&lt;p>Dubbo3从理念到设计再到实现最大的变革之一在于全面遵循云原生环境,做到了面向未来,为了达到这个目标dubbo本身做了相当重要的取舍。
在“取”这个层面,Dubbo3众多核心组件已经面向云原生升级,支持 Kubernetes 平台调度,实现了服务生命周期与容器生命周期的对齐,Serverless、Native Image等机制都在计划之中。&lt;/p>
&lt;p>在“舍”这个层面,Dubbo3割舍了以往要求开发人员遵守并熟知的Dubbo启动、销毁、注册等生命周期事件,Dubbo3自身设配了Kubernetes基础设施定义的生命周期事件(probe),并且将服务的定义与注册下沉到了Kubernetes Service,重新划定了dubbo和k8s基础设施的边界。&lt;/p>
&lt;p>To be future proof, the design and development of Dubbo3 has fully adapted Cloud Native. To meet this goal, Dubbo has made some trade-offs.Several core components of Dubbo3 support Kubernetes. In particular, Dubbo3 implements Kubernetes service and container life-cycle. Serverless and native image support will be release in the future.&lt;/p>
&lt;p>To support Kubernetes, Dubbo3 places service registration and discovery down to the Kubernetes Service layer, thus reestablishing the boundary between Dubbo and Kubernetes. However, this requires deprecating Dubbo’s own service discovery events.&lt;/p>
&lt;h3 id="全新服务发现模型--new-service-discovery-model">全新服务发现模型 New Service Discovery Model&lt;/h3>
&lt;p>Dubbo以往版本与Spring Cloud、gRPC等同属主流的服务框架进行服务发现的粒度不一致,Dubbo选择了基于更细粒度的接口来进行服务发现,Dubbo3.x进行了服务发现机制的对齐,即以应用粒度来进行服务发现,应用粒度的机制也带来了几点好处:&lt;/p>
&lt;ol>
&lt;li>打通与其他异构微服务体系的地址互发现障碍,这一点对于小米这样的多语种多框架并存的技术组织非常重要。&lt;/li>
&lt;li>提升了 Dubbo3 在大规模集群实践中的性能与稳定性。新模型可大幅提高系统资源利用率,降低 Dubbo 地址的单机内存消耗近一半,降低注册中心集群的存储与推送压力一半以上, Dubbo 可支持集群规模步入百万实例层次。&lt;/li>
&lt;/ol>
&lt;p>Previous versions of Dubbo differs from other mainstream service discovery middle-ware such as Spring Cloud and gRPC in its service discovery granularity. In the past Dubbo discovers services at the API level. Dubbo3, however, utilizes application level service discovery. This provides the following benefits.&lt;/p>
&lt;ol>
&lt;li>Eliminates incompatibility issues with other service discovery platforms, which is crucial since Xiaomi has diverse languages and development frameworks.&lt;/li>
&lt;li>The new service discovery model improves resource utilization, lowers Dubbo address’s single machine memory usage, lowers service discover cluster’s load.&lt;/li>
&lt;/ol>
&lt;h3 id="全新rpc-通信协议--new-rpc-communication-protocal">全新RPC 通信协议 New RPC Communication Protocal&lt;/h3>
&lt;p>定义了全新的 RPC 通信协议 – Triple,它是基于 HTTP/2 上构建的 RPC 协议。 使用 Triple 协议,用户将获得以下能力:&lt;/p>
&lt;ol>
&lt;li>完全兼容gRPC,能够更友好的支持跨语言跨框架的微服务进行互通。&lt;/li>
&lt;li>支持Protobuf进行序列化与反序列化,性能和字节大小均提升数量级。&lt;/li>
&lt;/ol>
&lt;p>Introduction of a new RPC communication protocol based on HTTP/2 called Triple. Using Triple has the following benefits.&lt;/p>
&lt;ol>
&lt;li>Complete compatibility with gRPC.&lt;/li>
&lt;li>Protobuf support for serialization and deserialization.&lt;/li>
&lt;/ol>
&lt;h3 id="升级与兼容性--upgrade-and-backwards-compatibility">升级与兼容性 Upgrade and Backwards Compatibility&lt;/h3>
&lt;p>对业务而言,在保证兼容以往版本的前提下进行升级是最核心的问题,在 3.0 版本的设计与开发之初,就定下了兼容老版本 Dubbo 用户(2.5、2.6、2.7)的目标。因此,往 3.0 版本的升级过程将会是完全透明的,用户无需做任何业务改造,升级 3.x 后的框架行为将保持与 2.x 版本完全一致。&lt;/p>
&lt;p>但也要注意,透明升级仅仅是通往 3.0 的第一步,因为 “框架行为保持一致” 也就意味着用户将无法体验到 3.0 的新特性。如果要启用 3.0 带来的新特性,用户则需要进行一定的改造,这个过程称为迁移,这是一个按需开启的过程。
因此,对老用户而言,有两条不同的迁移路径:&lt;/p>
&lt;ol>
&lt;li>分两步走,先以兼容模式推动业务升级到 3.0 版本(无需改造),之后在某些时机按需启用新特性(按需改造);&lt;/li>
&lt;li>升级与迁移同步完成,在业务升级到 3.0 版本的同时,完成改造并启用新特性;&lt;/li>
&lt;/ol>
&lt;p>第一种方式更安全,第二种方式更彻底,业务可根据自身的情况来进行评判选择。&lt;/p>
&lt;p>For production systems, upgrading a dependency while maintaining backward compatibility is challenging. Supporting users of older versions of Dubbo (2.5, 2.6, 2,7) is a goal of Dubbo3’s design and development. Thus, upgrading to Dubbo3 is painless. No changes are required to be made to existing production systems. However to use Dubbo3’s new features, additional changes are needed to existing code. Thus, there are two suggested upgrade paths.&lt;/p>
&lt;ol>
&lt;li>Upgrade to Dubbo3 but do not use Dubbo3’s new features. This path requires no code changes.&lt;/li>
&lt;li>Upgrade to Dubbo3 while making additional changes to support the new features.&lt;/li>
&lt;/ol>
&lt;h3 id="建议">建议&lt;/h3>
&lt;ol>
&lt;li>dubbo3已经和云原生做了深度适配,建议后续能够和istio等service mesh框架进行更多的衔接打通,方便dubbo用户更简单的走向mesh化之路;&lt;/li>
&lt;li>建议在调用方和服务方调用链路上增加更多的可选可观测点;&lt;/li>
&lt;/ol>
&lt;p>Suggestions&lt;/p>
&lt;ol>
&lt;li>Since Dubbo3 is closely compatible with Cloud Native, further work can be done to enhance Dubbo’s support of istio and other service mesh frameworks to speed up Dubbo users’ adaption of service mesh.&lt;/li>
&lt;li>Add more performance metrics to monitor Dubbo consumers and providers.&lt;/li>
&lt;/ol>
&lt;h3 id="作者">作者&lt;/h3>
&lt;ul>
&lt;li>张志勇&lt;/li>
&lt;li>张平&lt;/li>
&lt;li>许铮&lt;/li>
&lt;/ul></description></item><item><title>Blog: 中伦网络 Dubbo3 升级实践</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E4%B8%AD%E4%BC%A6%E7%BD%91%E7%BB%9C-dubbo3-%E5%8D%87%E7%BA%A7%E5%AE%9E%E8%B7%B5/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E4%B8%AD%E4%BC%A6%E7%BD%91%E7%BB%9C-dubbo3-%E5%8D%87%E7%BA%A7%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;p>中伦网络在 2022 年完成了服务框架从 Dubbo2 到 Dubbo3 的全站升级,深度使用了应用级服务发现、Kubernetes 原生服务部署、服务治理等核心能力。来自中仑网络的技术负责人来彬彬对整个 Dubbo3 的选型、升级过程及收益等做了深入总结。&lt;/p>
&lt;p>值得一提的是近期 &lt;a href="https://dubbo.apache.org/zh-cn/">Dubbo3 官网文档&lt;/a> 整体有了本质的提升,并且社区承诺短期内文档还会投入大量精力完善文档,这点对于 Dubbo3 的使用和用户信心提升非常重要。&lt;/p>
&lt;h3 id="一公司业务与技术架构简介">一、公司业务与技术架构简介&lt;/h3>
&lt;p>&lt;a href="https://www.zhonglunnet.com/guanyu.html">苏州中仑网络科技有限公司&lt;/a>是一家“专注零售门店增收服务”的公司,一直以“解决中小零售门店经营难的问题”为初心,致力于为零售商户提供门店运营一体化解决方案,帮助零售门店实现增收。中仑网络以零售技术为核心,为零售商户打造出集收银系统、中仑掌柜、微商城、汇邻生活平台、大数据平台、移动支付、智慧农贸、汇邻门店运营服务等为一体的新零售生态体系,实现线上线下全方位融合,为零售商家赋能增收。技术团队在构建之初选取Dubbo 2.5.3+Zookeeper版本构建公司微服务基座支撑公司业务发展,后期同阿里云深度合作整体迁移使用阿里云,使用云原生基础设施ACK(Kubernetes)+MSE(Zookeeper)+Dubbo+PolarDB等构建,实现可动态缩扩容的服务能力。
伴随合作商扩展3000+,市场遍及300+城市,零售商户30万+,服务覆盖餐饮、茶饮、服装、母婴、烘焙、生鲜、商超、美业、美妆、宠物等多个行业。伴随着领域拓宽、商户量快速增长上升,系统数量和部署节点也迎来了暴增,随之在系统可用性上受到较大挑战:微服务治理能力、微服务地址注册发现,Kubernetes平台服务的无损上下线顺滑度上问题与挑战越来越多。架构图见图一。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zhonglunwangluo-1.png" alt="image1">&lt;/p>
&lt;p>图一&lt;/p>
&lt;h3 id="二dubbo3-升级总结">二、Dubbo3 升级总结&lt;/h3>
&lt;p>在升级微服务组件技术选型上主要考虑解决以前的痛点:服务治理能力、云原生友好性、服务注册发现,这几个制约业务发展的紧要问题。比较下来Dubbo3架构设计理念与我们较为契合,能较好的满足我们业务发展要求。&lt;/p>
&lt;h3 id="1服务治理能力">1、服务治理能力&lt;/h3>
&lt;p>Dubbo 3提供丰富的服务治理能力,可实现诸如服务发现、负载均衡、流量调度等服务治理诉求。在使用上我们有两种选择:一、使用Dubbo管理控制台管理配置、二、集成相关API能力到系统。同时Dubbo 扩展性较好,可以在很多功能点(见图二)去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。Dubbo SPI ( Service Provider Interface)将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。基于此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能,如我们在此基础上实现了基于生产和消费者过滤器Filter实现全链路自定义的链路监控;基于路由扩展标签路由方式进行测试环境的隔离方便快速多版本服务测试验证。实操上我们基于生产者注册服务时打标,如原系统A V1版本部署在fat环境上,现在为了测试V2版本,我们将V2版本打标tag=fat-v2;使用端在消费时指定Invocation Attachment 参数,inv.setAttachment(TAG_KEY, routeTag);基于此我们可以方便自测试,同时生产上我们也可以做简单的生产灰度运用。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zhonglunwangluo-2.png" alt="image2">
图二&lt;/p>
&lt;h3 id="2云原生友好性">2、云原生友好性&lt;/h3>
&lt;p>Dubbo 在设计上遵循云原生微服务开发理念,微服务支持 Kubernetes平台调度,实现服务生命周期与容器生命周期的对齐,包括 Dubbo 的启动、销毁、服务注册等生命周期事件。中仑网络微服务管理使用的是MSE(Zookeeper),因而我们服务暴露使用需与之对齐。具体操作上我们自定义Startup 启动探针、 Liveness 存活探针、Readiness 就绪探针。项目的正常切换需要保障无损的上下线,在实施中无损上线相对于下线来说会更麻烦点,项目的发布上线过程大体会遵从如下流程:大致分成三个阶段,第一阶段升级少量(如 20% )的实例,并切换少量流量到新版本,完成这个阶段后先暂停升级。经过人工确认之后继续第二个阶段,升级更大比例(如 90% )的实例和流量,再次暂停等待人工确认。最后阶段将全量升级到新版本并验证完毕,从而完成整个发布过程。如果升级期间发现包括业务指标在内的任何异常,例如 CPU 或 memory 异常使用率升高或请求 500 日志过多等情况,可以快速回滚。因为我们使用的是MSE(Zookeeper)服务,dubbo服务自注册在应用启动过程暴露不受Kubernetes 生命周期的控制,出现项目未完全就绪部分服务可被提前可被访问问题。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zhonglunwangluo-3.png" alt="image3">
图三&lt;/p>
&lt;p>实施处理上我们主要利用Dubbo Qos指令,初始使用服务不暴露,在应用就绪后调用Qos online指令进行服务上线替换老节点,每次替换的节点数量基于发布策略来制定;下线过程针对需下线节点我们会先使用Qos指令进行下线offline操作等待应用执行完服务,从而进行优雅停机,从实践的效果来看能满足我们的生产需求。&lt;/p>
&lt;h3 id="3实例级别升级切换">3、实例级别升级切换&lt;/h3>
&lt;p>相比于 2.x 版本中的基于接口粒度的服务发现机制,3.x 引入了全新的基于应用粒度的服务发现机制,进一步提升了 Dubbo3 在大规模集群实践中的性能与稳定性。此次升级过程中我们也同步引入了配置中心与原数据中心,即将图四置灰部分启用&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zhonglunwangluo-4.png" alt="image4">
图四&lt;/p>
&lt;p>采用实例级别注册管理,一个应用N个服务,接口级时N服务需监听推送,应用级只关注单实例相关信息。同时引入元数据中心后极大降低接口配置数据信息,减少接口数据传输大小,相关职责配置也更加清晰。根据测试新模型大幅提高系统资源利用率,降低 Dubbo 地址的单机内存消耗,大幅降低注册中心集群的存储与推送压力,上线后稳定性有较大的提升。&lt;/p>
&lt;h3 id="三总结与展望">三、总结与展望&lt;/h3>
&lt;p>在中仑网络Dubbo 2升级Dubbo 3的过程中我们也有过一些迟疑,如把接口级换成应用级还是混合注册;Dubbo 3.0新特性新技术在项目中引入的时机与范围。对公司来说大的升级意味风险和不可预知的问题,但同时也能为之带来资源利用率提升、基础功能的扩展与增强,作为技术人员我们需要反复谨慎评估与论证。现在我们已经完成切换所有的业务领域。&lt;/p>
&lt;h3 id="四dubbo-社区合作">四、Dubbo 社区合作&lt;/h3>
&lt;p>在这里再次感谢Dubbo社区人员的专业、高效,以及对中仑网络架构升级的大力支持,同时很荣幸能够成为一名社区的贡献者,感兴趣的同学可以&lt;strong>加入贡献者钉钉群:31982034&lt;/strong>。&lt;/p>
&lt;p>Dubbo 社区近期筹办了每周一次的微服务纯技术分享,讲解 Dubbo 使用、源码,同时涵盖众多云原生微服务知识,感兴趣的同学可扫码关注。&lt;/p></description></item><item><title>Blog: 平安健康的 Dubbo3 迁移历程</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%B9%B3%E5%AE%89%E5%81%A5%E5%BA%B7%E7%9A%84-dubbo3-%E8%BF%81%E7%A7%BB%E5%8E%86%E7%A8%8B/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/15/%E5%B9%B3%E5%AE%89%E5%81%A5%E5%BA%B7%E7%9A%84-dubbo3-%E8%BF%81%E7%A7%BB%E5%8E%86%E7%A8%8B/</guid><description>
&lt;h1 id="1-背景">1 背景&lt;/h1>
&lt;p>我们公司从15年开始就使⽤dubbo作为微服务框架,当社区推出dubbo3时,我们也⽴刻跟进并做了深⼊调研,发现dubbo3 的应⽤/实例级服务注册和发现模式能够在一定程度上解决我们当前注册中⼼⾯临的压⼒,解决稳定性和安全性问题。同时dubbo3在服务治理上也做了升级,契合云原⽣架构,⽽且dubbo3能够向下兼容dubbo2,这也将降低升级的成本和⻛险。&lt;/p>
&lt;p>升级项目有了阶段性的进展,目前仍然在进行中。通过本⽂,我们对公司内部的Dubbo3 升级过程及收益等做了深⼊总结。&lt;/p>
&lt;h1 id="2-dubbo3-核功能介绍">2 Dubbo3 核⼼功能介绍&lt;/h1>
&lt;p>dubbo社区关于dubbo3的文档和资料越来越完善,以下是我们从社区引用的一些内容。&lt;/p>
&lt;h2 id="21-下一代云原生服务框架">2.1 下一代云原生服务框架&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-2.png" alt="pingan">&lt;/p>
&lt;p>Dubbo3被社区寄予厚望,将其视为下一代云原生服务框架打造,Dubbo3 提供的核心特性列表,主要包括四部分。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>全新服务发现模型&lt;/strong> 。应用粒度服务发现,面向云原生设计,适配基础设施与异构系统;性能与集群伸缩性大幅提升。&lt;/li>
&lt;li>**下一代 **** RPC **&lt;strong>协议&lt;/strong> &lt;strong>Triple&lt;/strong> 。基于 HTTP/2 的 Triple 协议,兼容 gRPC;网关穿透性强、多语言友好、支持 Reactive Stream。&lt;/li>
&lt;li>&lt;strong>统一流量治理模型&lt;/strong> 。面向云原生流量治理,SDK、Mesh、VM、Container 等统一治理规则;能够支持更丰富的流量治理场景。&lt;/li>
&lt;li>&lt;strong>Service Mesh&lt;/strong> 。在最新的3.1.0的版本中支持Sidecar Mesh 与 Proxyless Mesh,提供更多架构选择,降低迁移、落地成本。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-3.png" alt="pingan">&lt;/p>
&lt;p>首先是性能、资源利用率的提升。社区资料显示,升级 Dubbo3 的应用预期能实现单机内存 50% 的下降,对于越大规模的集群效果将越明显,Dubbo3 从架构上支持百万实例级别的集群横向扩展,同时依赖应用级服务发现、Triple协议等可以大大提供应用的服务治理效率和吞吐量。&lt;/p>
&lt;p>其次, Dubbo3 让业务架构升级变得更容易、更合理,尤其是RPC协议,在 2.x 版本中,web、移动端与后端的通信都要经过网关代理,完成协议转换、类型映射等工作,dubbo3的Triple 协议让这些变得更容易与自然;并通过流式通信模型满足更多的使用场景。&lt;/p>
&lt;p>最后,得益于 Dubbo3 的完善云原生解决方案,Dubbo3的mesh架构可以帮助业务屏蔽底层云原生基础设施细节,让业务更专注于业务,这也是mesh的最根本的优势。&lt;/p>
&lt;h2 id="22-应用级服务发现核心原理">2.2 应用级服务发现核心原理&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-4.png" alt="pingan">&lt;/p>
&lt;p>我们从 Dubbo 最经典的工作原理图说起,Dubbo 从设计之初就内置了服务地址发现的能力,Provider 注册地址到注册中心,Consumer 通过订阅实时获取注册中心的地址更新,在收到地址列表后,consumer 基于特定的负载均衡策略发起对 provider 的 RPC 调用。 在这个过程中&lt;/p>
&lt;ul>
&lt;li>每个 Provider 通过特定的 key 向注册中心注册本机可访问地址;&lt;/li>
&lt;li>注册中心通过这个 key 对 provider 实例地址进行聚合;&lt;/li>
&lt;li>Consumer 通过同样的 key 从注册中心订阅,以便及时收到聚合后的地址列表;&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-5.png" alt="pingan">&lt;/p>
&lt;p>再来看一下Provider向注册中心注册的 URL 地址的详细格式,这里把 URL 地址数据划分成了几份:&lt;/p>
&lt;ul>
&lt;li>首先是实例可访问地址,主要信息包含 ip port,是消费端将基于这条数据生成 tcp 网络链接,作为后续 RPC 数据的传输载体。&lt;/li>
&lt;li>其次是 RPC 元数据,元数据用于定义和描述一次 RPC 请求,表明这条地址数据是与某条具体的 RPC 服务有关的,它的版本号、分组以及方法相关信息。&lt;/li>
&lt;li>下一部分是 RPC 配置数据,部分配置用于控制 RPC 调用的行为,还有一部分配置用于同步 Provider 进程实例的状态,典型的如超时时间、数据编码的序列化方式等。&lt;/li>
&lt;li>最后一部分是自定义的元数据,这部分内容区别于以上框架预定义的各项配置,给了用户更大的灵活性,用户可任意扩展并添加自定义元数据,以进一步丰富实例状态。&lt;/li>
&lt;/ul>
&lt;p>结合以上对于 Dubbo2 接口级地址模型的分析,以及最开始的 Dubbo 基本原理图,可以得出这么几条结论:&lt;/p>
&lt;ul>
&lt;li>第一,地址发现聚合的 key 就是 RPC 粒度的服务。&lt;/li>
&lt;li>第二,注册中心同步的数据不止包含地址,还包含了各种元数据以及配置。&lt;/li>
&lt;li>得益于 1 与 2,Dubbo 实现了支持应用、RPC 服务、方法粒度的服务治理能力。&lt;/li>
&lt;/ul>
&lt;p>这就是一直以来 Dubbo2 在易用性、服务治理功能性、可扩展性上强于很多服务框架的真正原因。&lt;/p>
&lt;p>面对这样的地址数量级放大的问题,在 Dubbo3 架构下,社区认真思考了两个问题:&lt;/p>
&lt;ul>
&lt;li>如何在保留易用性、功能性的同时,重新组织 URL 地址数据,避免冗余数据的出现,让 Dubbo3 能支撑更大规模集群水平扩容?&lt;/li>
&lt;li>如何在地址发现层面与其他的微服务体系如 Kubernetes、Spring Cloud 打通?&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-6.png" alt="pingan">&lt;/p>
&lt;p>最终,社区给出的方案也是非常巧妙和经典。Dubbo3 的应用级服务发现方案设计的基本思路是:地址发现链路上的聚合元素也就是之前提到的 Key 由服务调整为应用,这也是其名称叫做应用级服务发现的由来,与kubernetes和spring cloud的服务注册发现处于同一粒度,能够平滑打通;另外,通过注册中心同步的数据内容上做了大幅精简,只保留最核心的 ip、port 地址数据。 经过上述调整,应用级别服务发现在保持接口级地址模型易用性的同时,实现了地址单条数据大小和总数量的下降。&lt;/p>
&lt;p>元数据、配置数据以及自定义数据等通过元数据中心或者MetadataService进行同步,且将所有的数据生成一个metadata revision, metadata revision相同则认为元数据等信息相同,通过这种方式来降低元数据中心或MetadataService的访问频次。&lt;/p>
&lt;h1 id="3-前期调研">3 前期调研&lt;/h1>
&lt;p>了解了 Dubbo3 的核心功能以及应用级服务发现的工作原理后,我们开始进入前期工作阶段。&lt;/p>
&lt;h2 id="31-性能压测">3.1 性能压测&lt;/h2>
&lt;p>从社区的资料来看,dubbo3各方面都非常不错,但是我们还得自己检验一次,所以我们使用当前在用的dubbo2内部定制版和dubbo3的性能压测,压测的主要场景在于同步调用,异步场景只做了dubbo3的压测, &lt;strong>以下压测数据和结论仅供参考&lt;/strong> 。结果表明dubbo3在性能上面确实做了很多的优化,在相同cpu使用率的情况下,dubbo3的tps是要高于dubbo2的;tps相当的情况下,dubbo3的cpu使用率要低于dubbo2。尤其是dubbo2的接口级与dubbo3的实例级,在tps相当的情况下,dubbo3的cpu使用率要较定制版的dubbo2低20%左右。&lt;/p>
&lt;p>压测环境:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类别&lt;/th>
&lt;th>版本&lt;/th>
&lt;th>机器配置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Provider&lt;/td>
&lt;td>dubbo 3.0.4&lt;/td>
&lt;td>4C8G&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Consumer&lt;/td>
&lt;td>dubbo 3.0.4&lt;/td>
&lt;td>4C8G&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Provider&lt;/td>
&lt;td>dubbo 2.5.3.22(基于2.5.3版本定制)&lt;/td>
&lt;td>4C8G&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Consumer&lt;/td>
&lt;td>dubbo 2.5.3.222(基于2.5.3版本定制)&lt;/td>
&lt;td>4C8G&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>测试场景:&lt;/p>
&lt;p>使用的是Dubbo协议,接口没有其它逻辑,直接将输入返回给消费者, 接口数据包大小500B,每个场景压30分钟。&lt;/p>
&lt;p>测试数据 (仅供参考):&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-7.png" alt="pingan">&lt;/p>
&lt;h2 id="32-升级前调研">3.2 升级前调研&lt;/h2>
&lt;p>做了压测得到dubbo2和dubbo3的压测数据后,我们开始计划将 Dubbo3 引入公司进行试点,此时,我们需要考虑dubbo3与dubbo2的兼容和迁移重构问题,升级目标,以及dubbo3提供有哪些能力支持升级和迁移。&lt;/p>
&lt;h3 id="321-升级的兼容和迁移重构问题">3.2.1 升级的兼容和迁移重构问题&lt;/h3>
&lt;p>考虑到公司的系统规模,要将dubbo2升级到dubbo3却不是一个简单的过程,尤其是公司的dubbo2版本在原开源版本基础之上做了不少优化和扩展,涵盖了ops服务治理、monitor数据指标监控、服务注册和发现、RPC灰度路由、链路分析、序列化编解码、作为其他基础框架的底层支持等多个方面,同时dubbo3社区也处于活跃的状态,我们也希望能够持续享受dubbo社区的技术红利,在这样的背景下不得不考虑三个问题:&lt;/p>
&lt;ol>
&lt;li>需要解决公司版本的dubbo2与dubbo3的兼容问题;&lt;/li>
&lt;li>原有功能的迁移重构问题;&lt;/li>
&lt;li>不在dubbo3的源码上做改动,保持和社区版本一致。&lt;/li>
&lt;/ol>
&lt;p>得益于dubbo 良好的扩展能力,我们可以通过dubbo 的SPI和IoC模块在dubbo3的基础之上优雅的兼容公司版本的dubbo2,不用改动dubbo3源码,只要研发dubbo3扩展包跟随dubbo3版本的API升级即可,这个升级改动的成本和从社区获取红利相比是比较小的。&lt;/p>
&lt;h3 id="322-升级目标">3.2.2 升级目标&lt;/h3>
&lt;p>既然历史包袱的解决方案已经有了,那么就要思考升级的目标了。首先确定的是,最终我们将会采用实例级的服务注册和服务发现,其次我们目前使用的注册中心是zookeeper,而dubbo社区更推荐使用的注册中心是nacos,而且我们在验证阶段时也暴露过几个在zookeeper上出现而在nacos上没有出现的问题,这也使得我们开始考虑将来是否将注册中心最终也迁移到nacos上。&lt;/p>
&lt;p>同时,我们也希望整个迁移过程是平滑、可控的,我们整体方案也要将风险把控作为核心要点考虑,尽可能的做到失败降级、实时可控。&lt;/p>
&lt;p>综上,我们将升级的目标归纳为下面几点:&lt;/p>
&lt;p>1)平滑的从dubbo2升级到dubbo3。&lt;/p>
&lt;p>2)将接口级服务注册和发现模式平滑的迁移到应用级服务注册和发现模式。&lt;/p>
&lt;p>3)为后面平滑迁移注册中心做好准备。&lt;/p>
&lt;p>4)迁移过程可监控、可观测。&lt;/p>
&lt;p>5)迁移过程要可灰度、可实时管控。&lt;/p>
&lt;p>6)统一dubbo3的通用配置规范,尽量适配原dubbo2的export和refer方式。&lt;/p>
&lt;h3 id="323-dubbo3对于迁移的支撑能力">3.2.3 dubbo3对于迁移的支撑能力&lt;/h3>
&lt;p>前面介绍的是我们的目标,但是如何把dubbo3的原生设计理念融入到现实情况中呢?以下是我们的相关思考,并在验证过程中。&lt;/p>
&lt;p>首先dubbo3能够支持在registryUrl上通过参数管理provider和consumer以不同的模式进行服务注册和服务发现,其中核心参数名有: registry-type, registry-protocol-type, register-mode。&lt;/p>
&lt;p>其次,dubbo3可以支持使用多个注册中心,不同的注册中心通过上面的registryUrl参数控制注册中心的服务注册模式和服务发现模式。而且还可以通过ProviderConfig和ConsumerConfig这两个这两个Config类分别管理provider侧和consumer侧使用的注册中心。在consumer侧,如果有使用多个注册中心,默认会使用ZoneAwareCluster创建的ZoneAwareClusterInvoker来进行负载均衡,从类名上可以看出,该ClusterInvoker是有提供区域感知的能力,查看源码时发现它还提供了preferred的功能,只在相应的registryUrl中添加了preferred=true,这个registryUrl创建的ClusterInvoker就会被优先调用。&lt;/p>
&lt;p>在同一注册中心进行接口级迁移到实例级的场景中,dubbo3的MigrationInvoker也提供了相应的支持,MigrationInvoker可以根据MigrationRule来控制实例级的RPC流量,并且根据MigrationRuleListener能够实时监听到指定应用的MigrationRule的变更。&lt;/p>
&lt;p>关于RPC 的监控在dubbo中一直由MonitorFilter和DubboMonitor提供RPC监控数据的收集和上报,收集的数据有消费者的ip端口、提供者的ip端口、应用名称、DubboService、Method、以及成功数、失败数、输入字节数、输出字节数、耗时、并发数等,&lt;/p>
&lt;p>这些能力能够满足基本的迁移工作,结合我们的现状来看,相对升级目标要求的平滑迁移,迁移流量可观测、可灰度、可管控还有一些距离,不过在梳理dubbo3这块能力的时候,也找到了相对简单的扩展方案。到此,对于整体的迁移方案也有了一个大致的雏形。&lt;/p>
&lt;h1 id="4-升级迁移方案设计">4 升级&amp;amp;迁移方案设计&lt;/h1>
&lt;p>方案的设计重点放在&amp;quot;平滑、可控&amp;quot;两点,根据dubbo3的新架构,目前正在验证中的迁移方案示意图如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/pingan-8.png" alt="pingan">&lt;/p>
&lt;p>从上图可以看出,在整个升级迁移的过程中,应用域中会存在多个dubbo版本,dubbo3是能够兼容社区的dubbo2版本,而我们公司内部的dubbo2版本是基于dubbo2.5.3开源版本深度定制过的,在ops服务治理、monitor数据指标监控、服务注册和发现、RPC灰度路由、序列化编解码等方面都做了扩展定制,我们的思路是由dubbo3基于dubbo的SPI采用扩展的方式或者ExtensionLoader的Wrapper模式去兼容定制版的dubbo2。&lt;/p>
&lt;p>除了应用外,在dubbo3的架构基础上划分了3个逻辑域,分别是注册域、配置管控域和监控域。注册域主要服务于服务的注册和发现,例如provider在DubboService暴露时,将服务信息和元数据信息上报到注册域的注册中心和元数据中心,consumer通过注册中心和元数据中心来发现服务。配置管控域主要是管理应用配置和迁移流量管控,dubbo3提供的配置中心支持自身能力的配置,所以流量规则的配置由dubbo3的配置中心进行维护,dubbo3的DynamicConfigration提供了很多关于动态配置的方法可以直接使用。监控域除了原RPC流量的监控外,还细分了迁移流量的监控,在迁移过程中,可以通过监控直观的看到迁移流量的现状,出现问题时,也可以及时报警通知相关人员介入。&lt;/p>
&lt;p>整个升级过程分为3个阶段:&lt;/p>
&lt;p>第一阶段:将dubbo2升级到dubbo3的接口级,验证功能、兼容性、性能和稳定性&lt;/p>
&lt;p>第二阶段:接口级和应用级双注册,通过MigrationRule和RegistryMigrationRule管理RPC流量&lt;/p>
&lt;p>第三阶段:全面切换到应用级,撤掉MigrationRule和RegistryMigrationRule&lt;/p>
&lt;h2 id="41-dubbo3扩展兼容dubbo2定制版本">4.1 dubbo3扩展兼容dubbo2定制版本&lt;/h2>
&lt;p>考虑到dubbo3社区版本的迭代情况,最终决定dubbo3兼容dubbo2定制版本的插件以SDK的方式单独维护,实现兼容的扩展功能主要有如下内容:&lt;/p>
&lt;ol>
&lt;li>RPC 正向透递与反向透传&lt;/li>
&lt;/ol>
&lt;p>在consumer侧和provider侧扩展Filter接口实现类,为正向与反向透传数据实现其发送和接收的功能。Dubbo2定制版是在序列化编解码层面对RpcResult进行了修改,要兼容这一逻辑也只能在序列化编解码层面去支持,采用Wrapper模式对Codec2这个SPI进行增强,与其它扩展类一并实现该兼容功能。&lt;/p>
&lt;ol start="2">
&lt;li>RPC 灰度路由&lt;/li>
&lt;/ol>
&lt;p>扩展Dubbo 的Router、 Cluster和ConfiguratorFactory等SPI,与内部的eunomia灰度管控平台集成实现RPC灰度路由,替换并兼容掉原dubbo2定制版在其源码层面修改实现的灰度路由功能。&lt;/p>
&lt;ol start="3">
&lt;li>monitor数据指标监控&lt;/li>
&lt;/ol>
&lt;p>扩展Protocol、MonitorFilter、Monitor等SPI,与内部的监控中心完成对接,与dubbo2定制版的监控逻辑保持一致。&lt;/p>
&lt;ol start="4">
&lt;li>ops 支持实例级&lt;/li>
&lt;/ol>
&lt;p>在ops层面,除了要兼容原接口级的服务管控外,还要添加实例级的服务管控。&lt;/p>
&lt;ol start="5">
&lt;li>其它中间件兼容&lt;/li>
&lt;/ol>
&lt;p>除了dubbo本身的兼容外,内部还有部分自研的中间件也是有依赖dubbo或者跟dubbo有关联的,还要对这些中间件进行改造升级。&lt;/p>
&lt;h2 id="42-迁移扩展">4.2 迁移扩展&lt;/h2>
&lt;p>在dubbo3原有能力基础上,也要另外进行扩展以提供平滑迁移的能力,我们构想的设计方案如下:&lt;/p>
&lt;ol>
&lt;li>扩展支持注册中心的迁移可灰度、可管控&lt;/li>
&lt;/ol>
&lt;p>在dubbo3中,当consumer侧应用了多个注册中心时,默认会通过ZoneAwareCluster创建ZoneAwareClusterInvoker来进行负载均衡,参考其实现可以扩展一个Cluster实现类和一个ClusterInvoker实现类将ZoneAwareCluster替换掉,注册中心迁移的流量管理、灰度、降级等能力都在扩展的ClusterInvoker中去实现&lt;/p>
&lt;ol start="2">
&lt;li>扩展支持接口级到实例级迁移规则的全局配置管理&lt;/li>
&lt;/ol>
&lt;p>MigrationRule由MigrationRuleListener通过DynamicConfiguration监听指定的应用的迁移规则,如果一个系统拥有成百上千个微服务应用时,由这种方式去管理,维护成本将会变高,我们借鉴这个方法实现了全局级别的MigrationRule管理能力。&lt;/p>
&lt;ol start="3">
&lt;li>扩展迁移流量可观测、可报警&lt;/li>
&lt;/ol>
&lt;p>dubbo RPC的流量监控已经有MonitorFilter和DubboMonitor实现了,只是MonitorFilter不能识别本次RPC请求的Invoker对象是通过哪个注册中心进行服务发现的,以及该Invoke对象的服务发现模式是接口级还是实例级的;,我们在consumer侧新增一个ClusterFilter接口和Filter接口去实现这个识别逻辑。&lt;/p>
&lt;ol start="4">
&lt;li>迁移组件开关&lt;/li>
&lt;/ol>
&lt;p>在扩展的最后,要考虑迁移组件下线的问题,即使要下线也不可能再次调整业务工程将迁移组件全部从依赖中删除掉,所以需要通过开关控制迁移组件的功能下线。&lt;/p>
&lt;h2 id="43-配置统一管理">4.3 配置统一管理&lt;/h2>
&lt;p>在升级和迁移过程中,我们可能会随时调整注册中心和迁移规则的配置参数,为减少出错的风险以及业务工程升级改动的工作量,这些公共的配置需要统一管理维护起来,dubbo3的配置中心正常能够把这个任务较好的承接下来。&lt;/p>
&lt;p>最终我们又扩展了一个配置加载的组件,通过我们公司内部的配置中心管理维护迁移组件的开关和配置中心的连接地址与超时等配置参数。有了这个组件后,新的应用可以不用担心dubbo3和迁移的公共配置,老的应用也减少了这部分公共配置的调整工作量。&lt;/p>
&lt;h2 id="44-风险预案">4.4 风险预案&lt;/h2>
&lt;p>dubbo作为一个提供底层支撑能力的微服务框架,我们始终把稳定性的要求放在首位,升级过程中任何一点意外,都可能导致线上问题,所以我们整个方案都是在面向失败、面向风险设计的。除了在扩展的迁移组件中实现了主动降级外,也着重考虑了一些极端情况,同时为这些极端情况提供风险预案。线上环境可能会出现各种意想不到的情况,在迁移过程中需要预先思考各种风险,准备完善的应对处理预案,保证系统快速恢复。&lt;/p>
&lt;h2 id="45-小结">4.5 小结&lt;/h2>
&lt;p>dubbo2是一个优秀的微服务框架,提供的SPI以及Extension机制能够非常方便的让用户去扩展实现想要功能。dubbo3在其基础之上,丰富了不少新的SPI,我们花了很长的时间去设计迁移方案,真正花在迁移组件上的开发时间较短。&lt;/p>
&lt;p>dubbo3整体架构升级调整对于过去的服务注册压力也得到了解决。性能上也做了优化,架构升级后的dubbo3也更适应目前云原生的架构,dubbo 3.1.x 版本支持sidecar和proxyless的mesh方案,而且社区也在准备开源java agent 方式的proxyless,这样就能较好的将微服务架框的framework与数据面解耦,降低微服务框架的维护成本和升级成本。&lt;/p>
&lt;h1 id="5-社区协作">5 社区协作&lt;/h1>
&lt;p>目前该项目仍然在持续升级中,我们跟社区保持着紧密的联系,期间碰到不少问题,都得到了社区开发同学耐⼼解答并最终给予解决。对于要升级 Dubbo3 的⽤户,可以在社区的Github User Issue(https://github.com/apache/dubbo/issues/9436) 进⾏登记,想参与社区的同学们也可以关注 Dubbo 官⽅公众号(搜索 Apache Dubbo)了解更多关于dubbo社区的进展。&lt;/p></description></item><item><title>Blog: 全国首个政企采购云平台:政采云的混合云跨网方案实践</title><link>https://dubbo.apache.org/zh-cn/blog/2023/03/22/%E5%85%A8%E5%9B%BD%E9%A6%96%E4%B8%AA%E6%94%BF%E4%BC%81%E9%87%87%E8%B4%AD%E4%BA%91%E5%B9%B3%E5%8F%B0%E6%94%BF%E9%87%87%E4%BA%91%E7%9A%84%E6%B7%B7%E5%90%88%E4%BA%91%E8%B7%A8%E7%BD%91%E6%96%B9%E6%A1%88%E5%AE%9E%E8%B7%B5/</link><pubDate>Wed, 22 Mar 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/03/22/%E5%85%A8%E5%9B%BD%E9%A6%96%E4%B8%AA%E6%94%BF%E4%BC%81%E9%87%87%E8%B4%AD%E4%BA%91%E5%B9%B3%E5%8F%B0%E6%94%BF%E9%87%87%E4%BA%91%E7%9A%84%E6%B7%B7%E5%90%88%E4%BA%91%E8%B7%A8%E7%BD%91%E6%96%B9%E6%A1%88%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;p>对云岛业务结构的公司来说,云平台属于公司内部、完全可控的局域网,而岛端则是有自己安全网络策略的独立内部网络。需要云岛通信时,会基于需求,按客户要求走流程开通一些端口,这个过程需要一定的成本且不完全可控。业务上,如果这种跨网需求增多,则会逐渐变成痛点。如果可以搭建一个透明的跨网传输网络,配合良好的顶层设计,就可以在业务支撑、安全管控和运维成本中寻求较好的平衡。&lt;/p>
&lt;p>本文将介绍政采云基于 Dubbo 的跨网方案落地过程中面临的技术挑战、社区合作以及更深层次抽象的一些思考。在政采云这种政企业务场景中的数据跨网,与业界公有云、自建私有云的公司相比,既有共性又有自己的特点,希望能为大家提供新的思路或者启发。&lt;/p>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>稳定、高效、可靠的基础设施是互联网企业应对业务高峰流量的底层基石。作为政采云的基础技术平台,基础平台部一直致力于通过业内前沿技术的落地,保障公司内部所有业务在线生产系统所依赖的基础技术平台能稳定、安全、低成本、可持续地运行与发展。&lt;/p>
&lt;p>由于公司对 Dubbo 框架的重度使用,&lt;strong>跨网数据传输系统&lt;/strong>一般基于 Dubbo 特性开发,在政采云内部就有多个版本的实现。&lt;/p>
&lt;p>早在几年前,政采云就上线了基于 Dubbo Filter 转发的方案,它解决了岛到云的单向数据传输,安全认证等问题。另外,业务部门也有按照自己的需求,推出网状点对点的方案,实现了一定程度的透明传输。&lt;/p>
&lt;p>结合前两年的探索实践以及业界相关领域技术的成熟度,2022年下半年,我们对各跨岛方案,进行了整合升级,也就是现在的&lt;strong>高速公路&lt;/strong>方案,保障跨岛标准化同时,解决了之前方案实践过程中面临的很多业务痛点,包括:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>单向传输&lt;/strong>:因为架构原因,如需双向需要对等重新部署一套,成本较大。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>白名单开通成本高&lt;/strong>:点对点的网状架构,需要两两开通白名单,因为政企网络特殊性,开通流程复杂且慢。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>平台维护成本高&lt;/strong>:业务各自一套数据传输平台,重复建设且运维成本高。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>公共功能的缺失&lt;/strong>:核心功能,业务可以按需开发,但是数据审计、链路追踪、可观测性等公共特性,往往没有足够投入。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="1-跨网数据传输系统演进">1. 跨网数据传输系统演进&lt;/h2>
&lt;h3 id="11-历史架构">1.1 历史架构&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-1.png" alt="img">&lt;/p>
&lt;p>​&lt;/p>
&lt;p>自左向右、自下而上进行模块介绍:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>业务Web&lt;/strong>:业务 Web 作为数据发送方,调本地 集群 Provider 时,携带跨岛信息过去(Dubbo 上下文)。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>岛业务Center&lt;/strong>:本地虚拟Provider,通过Filter拦截跨岛请求,通过http传送到云平台 Dubbo 网关,返回数据后反序列化返回岛业务 web。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Dubbo网关&lt;/strong>:接收 Http 请求,通过泛化调用云端 Provider,处理数据后返回业务 Center。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>云业务Center&lt;/strong>:普通 Dubbo Provider。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="12-高速公路架构">1.2 高速公路架构&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-2.png" alt="img">&lt;/p>
&lt;p>​&lt;/p>
&lt;p>&lt;strong>1.2.1 隧道机制&lt;/strong>&lt;/p>
&lt;p>隧道技术是一种通过使用&lt;strong>互联网络&lt;/strong>的&lt;strong>基础设施&lt;/strong>在网络之间传递数据的方式。使用隧道传递的&lt;strong>数据&lt;/strong>(或负载)可以是不同协议的数据帧或包。&lt;/p>
&lt;p>高速公路架构中,使用了隧道这个概念。两端(业务层)是 Dubbo 私有协议,跨网传输过程中,则使用了 http 协议,http 协议可以更好的被中间设备、网关识别转发。这个机制的最大便利在于对业务的低侵入性。对于业务集群的应用完全不需要修改。
&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-3.png" alt="img">&lt;/p>
&lt;p>除了路由标记,出口/入口 Dubbo 协议字节流没有任何业务外信息,所以可以路由任何 Dubbo 请求。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-4.png" alt="img">&lt;/p>
&lt;p>&lt;strong>1.2.2 主要节点&lt;/strong>&lt;/p>
&lt;p>&lt;strong>客户端 Sdk&lt;/strong>:不改变用户使用 Dubbo 的方式,多种形式提供Dubbo的路由。&lt;/p>
&lt;p>**Dubbo 出口网关:**代理 Dubbo 流量出口。&lt;/p>
&lt;p>**Dubbo 入口网关:**代理 Dubbo 流量入口。&lt;/p>
&lt;p>**统一网关:**基于 Apisix,代理跨网间所有流量,可以扩展鉴权、审计、限流等特性&lt;/p>
&lt;h2 id="2-挑战与应对之策">2. 挑战与应对之策&lt;/h2>
&lt;p>如前言中所述,已有的几个方案设计上存在了一些问题,落地后也限制了使用了场景。在架构上,我们提出了高速公路方案,选择了全双工的对等网络传输框架。角色上,云平台定位一个特殊的岛端应用,遵循P2P实施原则。而对用户而言,高速公路是一个通往岛端的隧道,遵循对用户透明原则。我们可以先来看下在搭建平台的过程中面临的一些挑战以及解法。&lt;/p>
&lt;h3 id="21-技术挑战">2.1 技术挑战&lt;/h3>
&lt;p>结合当下跨网数据传输系统面临的处境,并对业界 Dubbo 跨网方案做过一番调研后,在平台搭建上确定了如下三期目标:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>一期目标&lt;/strong>:网络能力建设,简单来说是搭建基于 Dubbo 的传输通道,上层功能先维持不变。&lt;/li>
&lt;li>&lt;strong>二期目标&lt;/strong>:业务上,找业务先行试点,基于反馈,小步快跑,快速迭代;技术上,寻求 Dubbo 社区协作,增强对Dubbo相关技术风险的把控,同时抽离通用特性,反馈社区。&lt;/li>
&lt;li>&lt;strong>三期目标&lt;/strong>:抽象出更通用的网络框架,从而使语言层,传输协议层、及中间件层独立扩展,一键切换。&lt;/li>
&lt;/ul>
&lt;p>在上述三期目标基本落地后,高速公路系统不仅可以跑起来,同时拥有非常强大的扩展性,更好的承接业务需求及共建。在这过程中,我们需要解决不少技术问题。&lt;/p>
&lt;p>&lt;strong>2.1.1 客户端路由&lt;/strong>&lt;/p>
&lt;p>如前面历史方案所述,其场景被限制为岛到云的单向数据传输,特点如下:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>客户端无路由能力&lt;/strong>:Consumer 端只能指定是否路由到云平台,而不能指定其他岛端。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>基于filter的扩展&lt;/strong>:Dubbo的 Filter 并不是为路由设计的,在此基础上较难扩展。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>需要本地Provider角色&lt;/strong>:Consumer 端发出的请求,必须由一个注册在 Zookeeper 下的 Provider 兜住,然后 Filter 根据上下文决定是否转发,这就限制了业务方必须部署一个本地 Provider 应用(哪怕是空应用),才能做到跨网访问。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>我们要解决的问题之一,就是打破单向传输瓶颈,客户端可以更自由的路由到目标云/岛。我们设计了以下几种路由方式:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>注解方式&lt;/strong>:使用@DubboReference提供的通用 parameters 参数,设置路由目标,可以达到方法粒度的路由。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboReference&lt;/span>&lt;span style="color:#719e07">(&lt;/span>check &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">,&lt;/span> parameters &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ENV_SHANGHAI&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;ALL&amp;#34;&lt;/span>&lt;span style="color:#719e07">})&lt;/span> &lt;span style="color:#586e75">//all表示所有方法,可以单独指定
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> DemoService demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>配置中心指定&lt;/strong>:把以上parameters = {&amp;ldquo;ENV_SHANGHAI&amp;rdquo;, &amp;ldquo;ALL&amp;rdquo;} 信息,在配置中心配置,达到同样的效果,这种方式对代码完全无侵入。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>线程指定&lt;/strong>:这种方式最灵活。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>AddressZoneSpecify&lt;span style="color:#719e07">.&lt;/span>setAddress&lt;span style="color:#719e07">(&lt;/span>Enviroment&lt;span style="color:#719e07">.&lt;/span>SHANGHAI&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>demoService&lt;span style="color:#719e07">.&lt;/span>play&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>无论哪种路由方式,基于“用户透明“的原则,都不改变用户使用 dubbo 的方式。&lt;/p>
&lt;p>&lt;strong>2.1.2 Dubbo请求地址切换&lt;/strong>&lt;/p>
&lt;p>客户端路由最小限度地侵入业务代码,达到了透明调用远程服务的目标。但是,用户仍旧需要部署一套虚拟 Provider 应用,接收请求后按规则进行路由。&lt;/p>
&lt;p>为了避免部署多余的应用,我们需要有一定的机制,直接把dubbo流量切换到远程。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-5.png" alt="img">&lt;/p>
&lt;p>​&lt;/p>
&lt;p>解决了切换问题后,本地的 APP2 不再需要,甚至zk也可以移除。当然,如果业务同时有本地和远程的调用需要,也可以继续存在。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-6.png" alt="img">&lt;/p>
&lt;p>​&lt;/p>
&lt;p>原先,我们准备通过Dubbo的Route自定义扩展,去实现动态切换地址的能力。查阅资料后,发现Dubbo已经提供了类似能力。&lt;/p>
&lt;p>&lt;a href="https://cn.dubbo.apache.org/zh-cn/docs3-v2/java-sdk/advanced-features-and-usage/service/specify-ip/">https://cn.dubbo.apache.org/zh-cn/docs3-v2/java-sdk/advanced-features-and-usage/service/specify-ip/&lt;/a>&lt;/p>
&lt;p>该特性放在Dubbo的子工程dubbo-spi-extensions中,同样以Route扩展形式实现。&lt;/p>
&lt;p>但在实际使用过程中,我们遇到如下问题:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>不支持 Dubbo2&lt;/strong>:使用 Dubbo2 时,直接以异常的形式提醒暂不支持。&lt;/li>
&lt;li>&lt;strong>NPE异常:&lt;/strong> 某些场景下调用出现了NPE异常。&lt;/li>
&lt;li>&lt;strong>丢失部分信息:&lt;/strong> Router下构建新 Invocation 时,丢失了 version、group等信息。&lt;/li>
&lt;li>&lt;strong>重试异常:&lt;/strong> 远程 Provider 如果发生了异常,客户端在重试的时候,选择了本地集群 Provider 调用,造成错误.&lt;/li>
&lt;/ul>
&lt;p>作为一个尝鲜新特性,我们理解功能存在不稳定的情况。但这个功能作为我们跨网方案的技术要点,又必须解决。所以,我们通过PR的形式,把相应补丁提交到Dubbo社区。这个过程中,我们联系到了Dubbo PMC 远云大佬,一起讨论和完善PR,直到解决所有已知问题。&lt;/p>
&lt;p>&lt;strong>2.1.3 出口网关的实现&lt;/strong>&lt;/p>
&lt;p>在上图中,通过切换地址,我们似乎可以直接访问远程应用,并且架构非常简单。但是遗憾的是,存在几个难以解决的问题:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>网关组件的限制&lt;/strong>:在云岛/岛岛间,存在一系列网关组件,来提供转发、负载均衡的功能,比如SLB、NGINX、WAF。这些组件并不能识别私有的 Dubbo 流量并转发&lt;/li>
&lt;li>&lt;strong>ip白名单开通成本高:&lt;/strong> 类似 P2P 方案,需要点对点开通 IP 白名单,成本巨大。&lt;/li>
&lt;li>&lt;strong>升级维护复杂:&lt;/strong> 客户端通过集成 SDK 的形式转发,后续如需要劫持流量进行扩展,需要同时对每个接入应用进行升级。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-7.png" alt="img">&lt;/p>
&lt;p>​&lt;/p>
&lt;p>针对以上问题,我们的设计中,需要加入 Dubbo 网关的角色,来实现以下目标。&lt;/p>
&lt;p>① &lt;strong>两端ip收敛&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>显著减少网关长连接数量&lt;/li>
&lt;li>弱化服务注册发现流程(每个环境只有一个 Dubbo 网关,直接配置即可互相发现)&lt;/li>
&lt;li>简化鉴权、认证流程。一条链路可以使用白名单,一群则只能配置较复杂的鉴权&lt;/li>
&lt;/ul>
&lt;p>② &lt;strong>两端功能收敛&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>客户端的 SDK 专注路由功能,基本不用升级&lt;/li>
&lt;li>扩展功能放在 Dubbo-Proxy,统一升级,业务端无感知&lt;/li>
&lt;/ul>
&lt;p>Dubbo-Proxy 作为业务网关,可以减轻对业务端的侵入,起到类似分布式运行时(Dapr)作用。但是,在引入之前,需要解决一些现实的技术问题。其中,最重要的问题是如何接收陌生的 Dubbo 流量,然后进行转发。做了一些相关调研后,有两个方案可用:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>通用Provider&lt;/strong>:直接在 Dubbo-Proxy 注册一个普通的通用 Service,客户端的 SDK 利用 Filter,劫持流量,直接调用通用 Service 后处理数据返回。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>**注册虚拟节点:**该方案来源于远云。客户端在本地zk订阅远程节点时,通知 Proxy,Proxy 获取订阅的信息后(预先订阅所有 zk 变更),主动注册相应虚拟 Service(对 zk 来说,注册一个节点的参数只是字符串)到zk上。这样,可以把客户端的远程流量“骗”到 Proxy ,Proxy 再使用服务端泛化,接收请求并转发。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>以上两种方案,都可以实现出口网关。但是,在设计上,角色间需要多次交互,才能达到目的。那么,是否有更简洁的方式,直接支持这种接收和转发呢?&lt;/p>
&lt;p>首先,我们对 Dubbo 源码进行了调研,看 Provider 接收到陌生流量(无相应Service)后会如何处理,是否有扩展点可以拦截。发现在 Byte 流解析阶段,Dubbo 即对 Service 进行了检查,不存在直接抛异常返回。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-8.png" alt="img">
​&lt;/p>
&lt;p>在 Provider 处理的生命周期中,Decode 出于非常早期的阶段,几乎没有什么扩展点可以拦截处理。因为快速失败的理念,早期的检测确实可以避免后面无谓的代码执行消耗。但是,对比 Spring ,Dubbo 在扩展性上是有不足的,即对于一个通用的异常,却没有相应的扩展机制。&lt;/p>
&lt;p>我们决定在 decode 的基础上,加上对这个异常的扩展。主要思路是,在 decode 被调用处,catch 住这块异常,通过 SPI 的形式,获取扩展实现,可以定制异常信息,也可以控制 decode 流程重试。这块修改难度并不大,私有版本上顺利通过测试,同时提交 PR 到社区。这个过程中,远云大佬帮忙发现了一个并发安全的 bug,并给了不少减少风险的建议。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//解码结束后,无论是否异常,都将进入这个方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">handleRequest&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#268bd2">final&lt;/span> ExchangeChannel channel&lt;span style="color:#719e07">,&lt;/span> Request req&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RemotingException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>req&lt;span style="color:#719e07">.&lt;/span>error &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Give ExceptionProcessors a chance to retry request handle or custom exception information.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String exPs &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>getProperty&lt;span style="color:#719e07">(&lt;/span>EXCEPTION_PROCESSOR_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotBlank&lt;span style="color:#719e07">(&lt;/span>exPs&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>ExceptionProcessor&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> channel&lt;span style="color:#719e07">.&lt;/span>getUrl&lt;span style="color:#719e07">().&lt;/span>getOrDefaultFrameworkModel&lt;span style="color:#719e07">().&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>ExceptionProcessor&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExceptionProcessor expProcessor &lt;span style="color:#719e07">=&lt;/span> extensionLoader&lt;span style="color:#719e07">.&lt;/span>getOrDefaultExtension&lt;span style="color:#719e07">(&lt;/span>exPs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> handleError &lt;span style="color:#719e07">=&lt;/span> expProcessor&lt;span style="color:#719e07">.&lt;/span>shouldHandleError&lt;span style="color:#719e07">(&lt;/span>error&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>handleError&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取异常扩展,执行wrapAndHandleException操作,需要重试的场景可以抛出retry异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> msg &lt;span style="color:#719e07">=&lt;/span> Optional&lt;span style="color:#719e07">.&lt;/span>ofNullable&lt;span style="color:#719e07">(&lt;/span>expProcessor&lt;span style="color:#719e07">.&lt;/span>wrapAndHandleException&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">,&lt;/span> req&lt;span style="color:#719e07">)).&lt;/span>orElse&lt;span style="color:#719e07">(&lt;/span>msg&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res&lt;span style="color:#719e07">.&lt;/span>setErrorMessage&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Fail to decode request due to: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> msg&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res&lt;span style="color:#719e07">.&lt;/span>setStatus&lt;span style="color:#719e07">(&lt;/span>Response&lt;span style="color:#719e07">.&lt;/span>BAD_REQUEST&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> channel&lt;span style="color:#719e07">.&lt;/span>send&lt;span style="color:#719e07">(&lt;/span>res&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//handleRequest过程中的retry控制
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">received&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Channel channel&lt;span style="color:#719e07">,&lt;/span> Object message&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RemotingException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//解码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> decode&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> handler&lt;span style="color:#719e07">.&lt;/span>handleRequest&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">,&lt;/span> message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RetryHandleException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>message &lt;span style="color:#719e07">instanceof&lt;/span> Request&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ErrorData errorData &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ErrorData&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">((&lt;/span>Request&lt;span style="color:#719e07">)&lt;/span> message&lt;span style="color:#719e07">).&lt;/span>getData&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//有定制,进行重试
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> retry&lt;span style="color:#719e07">(&lt;/span>errorData&lt;span style="color:#719e07">.&lt;/span>getData&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Retry only once, and only Request will throw an RetryHandleException
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RemotingException&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Unknown error encountered when retry handle: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> e&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> handler&lt;span style="color:#719e07">.&lt;/span>received&lt;span style="color:#719e07">(&lt;/span>channel&lt;span style="color:#719e07">,&lt;/span> message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于ExceptionProcessor扩展,我们在官方扩展包Dubbo-Spi-Extensions中,提供了一个默认实现,允许控制重试解码,并自定义异常处理。&lt;/p>
&lt;p>&lt;strong>2.1.4 中心网关&lt;/strong>&lt;/p>
&lt;p>最新架构,已经非常接近最终实现了,但是缺了一个中心网关角色。引入这个网关(基于 Apisix )的原因:&lt;/p>
&lt;ul>
&lt;li>白名单问题:虽然 Dubbo 网关收敛了终端 IP,但是要实现岛岛互通,还是得两两互开白名单。引入中心网关(云平台)后,每个岛单独和云平台互开即可。白名单开通复杂度从O(n*n) 变为O(n)。&lt;/li>
&lt;li>统一网关的好处:作为公司级网关,可以统一对所有应用进行限流、鉴权、审计、可观测性等功能拓展。&lt;/li>
&lt;/ul>
&lt;h2 id="3-更多思考">3. 更多思考&lt;/h2>
&lt;p>无论公司内外,能选择的跨网方案非常多,我们会去选择一个能解决痛点的,而不是完美的方案。落地方案一般比较保守,但是对于架构的思考,一定是需要更超前的。&lt;/p>
&lt;p>&lt;strong>http协议导致的性能损失&lt;/strong>&lt;/p>
&lt;p>前面说到,在 Dubbo 网关和中心网关间,我们使用了 Http 协议。对比 Dubbo 等精简协议,Http 协议显然更臃肿。但是,也许这是现阶段最合适的方案。除了避免私有协议在网络设备中的“艰难前行”,Http 协议开发成本更低,相应落地风险也更小。一些新技术,也许是我们后续发展的方向。比如 Higress,支持 Triple 协议(基于 Http2)交换信息,在获得更高性能的同时,也解决了设备识别问题。但是选择 Higress,需要面对学习认知成本、新开源 BUG 多等问题,同时它可能更适合内部网络(即使跨公网也能搭建 VPN),而不是我们各私有岛端(客户自定义安全策略)的网络互通。&lt;/p>
&lt;p>&lt;strong>扩展性不足&lt;/strong>&lt;/p>
&lt;p>高速公路是一个基于 Dubbo 的跨网方案,在协议与框架层,与 Dubbo 的绑定比较深,但是它应该能做的更多。也许很快,会接入 Http、Mq 等应用协议的流量,或者 Python、Go 等语言的客户端,甚至是 Mysql 的数据互通。这个时候,要么对架构大改,要么各种兼容,这都不是我们想看到的。参考网络分层协议,我们也粗略地做了一个分层抽象规划。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/users/zcy-9.png" alt="img">
​&lt;/p>
&lt;ul>
&lt;li>物理层打通:主要解决网络异构问题,即约定不同安全策略的子域如何通信。&lt;/li>
&lt;li>通讯协议层加速:前面讲到的应用层协议,需要做到允许独立扩展及切换。&lt;/li>
&lt;li>语言层编译加速:业务网关可能更适合使用 Golang,然后 Java 节点是否可以用 Native 优化性能?&lt;/li>
&lt;li>框架层功能升级:比如当前对 Dubbo 的定制开发,使用的 Apisix 中心网关是否可以扩展 dubbo 转 dubbo?&lt;/li>
&lt;li>任务编排:业务的跨网调度,不一定是A-&amp;gt;B-&amp;gt;C-&amp;gt;D,会不会是A、B同时完成后才能-&amp;gt;C-&amp;gt;D?&lt;/li>
&lt;li>更上层的控制面/治理面/运维面&lt;/li>
&lt;/ul>
&lt;h2 id="4-未来规划">4. 未来规划&lt;/h2>
&lt;p>随着高速公路方案在政采云的逐渐落地,我们未来会从稳定性、功能增强、新技术探索三个方面去做深、做广:&lt;/p>
&lt;p>(1)&lt;strong>稳定性&lt;/strong>:基础服务的稳定性是一切的基石,而这往往是不少研发同学容易忽视的一点,研发同学需“在晴天时修屋顶”。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>系统自身的健壮性&lt;/strong>:资源池化隔离、QoS 保障能力建设。&lt;/li>
&lt;li>&lt;strong>节点实例的稳定性&lt;/strong>:加固发现能力,持续完善异常检测工具(除了常规的健康检测,会从观测指标的不同纬度综合决策),自动进行异常实例的替换;加强数据运营,提升反馈能力。&lt;/li>
&lt;/ul>
&lt;p>(2)&lt;strong>功能增强&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>协议增强&lt;/strong>:当前只能对 Dubbo 流量转发,计划增加对 Http/Grpc等协议等支持,从而支持更多的场景(已有业务提此类需求)。&lt;/li>
&lt;li>&lt;strong>安全性增强&lt;/strong>:在中心网关 Apisix 开发鉴权、审计等插件,更好的控制跨网的调用与被调。&lt;/li>
&lt;li>&lt;strong>易用性增强&lt;/strong>:开发自动工单系统,对需要配置的事项,由业务测提工单,相应人员审核后自动配置,解放劳动力同时减少出错概率。&lt;/li>
&lt;/ul>
&lt;p>(3)&lt;strong>新技术探索&lt;/strong>&lt;/p>
&lt;p>​ 网关场景,通常有个两个比较明显的特点:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>并发量高: 多个应用复用同一个网关&lt;/p>
&lt;/li>
&lt;li>
&lt;p>行为轻量: 一般只有转发、权限校验等轻量操作&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>基于这两个特点,语言层性能开销在总性能开销中的占比,往往会业务应用更大,这个时候, Golang 等语言会比 Java更有优势。当前也在对 Dubbo-Go 调研,未来替换基于 Java 版 Dubbo 的网关应用。&lt;/p>
&lt;p>另外,Higress 方案看起来不错,必定会有许多值得我们学习的东西。&lt;/p></description></item><item><title>Blog: Dubbo Java 3.1.4 正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/01/dubbo-java-3.1.4-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Thu, 01 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/01/dubbo-java-3.1.4-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo 3.1.4 版本是目前 Dubbo 3 的最新稳定版本,我们建议所有的用户都升级到最新的稳定版本。&lt;/p>
&lt;h1 id="dubbo-314">Dubbo 3.1.4&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/3-1-4.png" alt="image.png">&lt;/p>
&lt;h3 id="新特性">新特性&lt;/h3>
&lt;ul>
&lt;li>Dubbo QoS 支持指定白名单 IP 列表访问所有命令&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复在泛化调用时 Dubbo Metrics 采集方法名错误&lt;/li>
&lt;li>修复使用 Zookeeper 作为元数据中心时,上报接口映射可能存在相互覆盖的问题&lt;/li>
&lt;li>修复 timeout countdown 在 Triple 协议的支持&lt;/li>
&lt;li>修复 timeout countdown 存在透传的问题&lt;/li>
&lt;li>修复多注册中心请求时,由于没有可用的地址导致的 NPE 问题&lt;/li>
&lt;li>修复 Mesh 模式下 Triple 获取 remoteApplicationName 为空的问题&lt;/li>
&lt;li>修复 GraalVM Native Image 的支持&lt;/li>
&lt;li>修复端口复用时无 SSL 连接导致的 NPE 异常&lt;/li>
&lt;li>完善 JDK 编译器报错日志的输出格式&lt;/li>
&lt;li>修复 MetadataReportConfig 部分配置时应用无法启动的问题&lt;/li>
&lt;li>修复 dubbo.reference 作为默认参数在 3.x 版本中不生效的问题&lt;/li>
&lt;li>完善 Zookeeper 连接失败的日志&lt;/li>
&lt;li>修复 ReferenceConfig 中配置的 ClassLoader 可能被覆盖的问题&lt;/li>
&lt;li>修复部分属性在应用级服务发现时被缓存在实例级的地址中导致方法级配置失效的问题&lt;/li>
&lt;li>修复 Triple 协议 onError 异常的问题&lt;/li>
&lt;/ul>
&lt;h3 id="faq">FAQ&lt;/h3>
&lt;p>本次发布中有 3 个提交涉及异常日志 FAQ 的完善。关于错误码机制请参考官网错误码机制介绍一文。(https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/faq/intro/)&lt;/p>
&lt;h3 id="代码优化">代码优化&lt;/h3>
&lt;p>本次发布中有 11 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="依赖升级">依赖升级&lt;/h3>
&lt;ul>
&lt;li>升级 fastjson2: 2.0.14 -&amp;gt; 2.0.21&lt;/li>
&lt;li>升级 resteasy-jaxrs: 3.0.19.Final -&amp;gt; 3.0.20.Final&lt;/li>
&lt;/ul>
&lt;h3 id="贡献者">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献:@cnjxzhao, @CrazyHZM, @EarthChen, @gold-fisher, @IncrementalRefinement, @Koooooo-7, @ShenFeng312, @tonycody, @twz007, @win120a, @wlazjr&lt;/p>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@twz007 在 PR #11012 提交了第一个贡献&lt;/li>
&lt;li>@IncrementalRefinement 在 PR #11046 提交了第一个贡献&lt;/li>
&lt;li>@gold-fisher 在 PR #11058 提交了第一个贡献&lt;/li>
&lt;li>@wlazjr 在 PR #11084 提交了第一个贡献&lt;/li>
&lt;li>@ShenFeng312 在 PR #11102 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="未来版本规划">未来版本规划&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>Dubbo 版本的发布规划以及在《&lt;a href="https://mp.weixin.qq.com/s?__biz=MzIwODYwNTA4MA==&amp;amp;mid=2247484424&amp;amp;idx=1&amp;amp;sn=2f5ff4846f7dafad325f78fd8cf4d1fc&amp;amp;chksm=9701deffa07657e9a46eb97bb859770b4856599566b992724013a848a730f394702938e72404&amp;amp;token=1547029975&amp;amp;lang=zh_CN#rd">聚焦稳定性,Dubbo 发版规划公布&lt;/a>》一文中正式发布,欢迎查看。&lt;/p></description></item><item><title>Blog: 聚焦稳定性,Dubbo Java 发版规划公布</title><link>https://dubbo.apache.org/zh-cn/blog/2022/10/22/%E8%81%9A%E7%84%A6%E7%A8%B3%E5%AE%9A%E6%80%A7dubbo-java-%E5%8F%91%E7%89%88%E8%A7%84%E5%88%92%E5%85%AC%E5%B8%83/</link><pubDate>Sat, 22 Oct 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/10/22/%E8%81%9A%E7%84%A6%E7%A8%B3%E5%AE%9A%E6%80%A7dubbo-java-%E5%8F%91%E7%89%88%E8%A7%84%E5%88%92%E5%85%AC%E5%B8%83/</guid><description>
&lt;h2 id="dubbo-简介">Dubbo 简介&lt;/h2>
&lt;p>Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。&lt;/p>
&lt;h2 id="我应该如何选择版本">我应该如何选择版本?&lt;/h2>
&lt;p>对于这个问题,一直以来 Dubbo 都没有很好地去回答。究其原因保证所有版本的稳定性是一件非常有挑战的事情。比如在 Dubbo 2.7 的一些版本中,出现了一些前后不兼容的问题,给一些用户升级带来了困扰。为此,很多用户从保障稳定性的视角出发,选择了更保守的策略,即生产环境采用被采用最多的版本,此后就不再更新,避免升级过程中出现意外。&lt;/p>
&lt;p>但是这种方式无疑是有问题的,一方面这将导致永远无法用上社区新的功能,得到技术演进的红利;另一方面,对于安全漏洞和带来不稳定因素的 bug 等已知问题如果不升级永远会存在,久而久之将给应用带来巨大的风险,很有可能导致严重的安全生产事故。&lt;/p>
&lt;p>因此,为了更好地回答这个问题,Dubbo 制定了未来的版本迭代规划,明确版本的稳定性维护机制,提升开发者使用 Dubbo 的信心。&lt;/p>
&lt;h2 id="现状">现状&lt;/h2>
&lt;p>在此前,Dubbo 总共维护了 2.6.x、2.7.x 以及 3.x 三个大版本,其中 2.7.x 为捐献进入 Apache 基金会之后的版本。&lt;/p>
&lt;p>2.6.x 在去年已经宣布生命周期结束(EOL)了,而 2.7.x 累计维护了四年的时间了。这四年的时间里,2.7.x 版本经历了多次大的修改,如 2.7.3 版本中对 Nacos 的注册方式进行了修改、2.7.6 版本中添加了应用级服务发现、2.7.9 版本中对应用级服务发现进行大的重构等。&lt;/p>
&lt;p>这些修改都对 2.7.x 版本的稳定性带来了巨大的风险隐患,导致 2.7.x 中很多版本或多或少存在不同的问题,用户无法选择一个最稳定的版本进行使用。&lt;/p>
&lt;p>因此,我们希望在 3.x 版本中将这种不稳定的情况通过发版机制给收敛掉,提高 Dubbo 版本的稳定性。&lt;/p>
&lt;h2 id="未来总体规划">未来总体规划&lt;/h2>
&lt;p>在接下来,Dubbo 将以每 6 个月为一个周期,对每个版本包括了新功能合入、稳定性维护以及安全漏洞修复三个大方向的内容。&lt;/p>
&lt;p>新功能合入是指将 Dubbo 开发过程中所有新的特性、性能优化、破坏性修改都会被合入的行为。由于新功能是需要一段时间迭代稳定的,所以新功能的合入会给对应版本带来不稳定因素,因此只能在每个版本最开始的时候进行。&lt;/p>
&lt;p>稳定性维护是指修复&lt;strong>已有&lt;/strong>功能的非预期行为,进一步提升版本的稳定性。特别的,对于一些大的问题修复,如功能需要整体重构的,或者回带来破坏性影响的提交将被视为新功能提交,执行新功能合入的流程。避免因为修复一个问题而来带更多问题的严重后果。&lt;/p>
&lt;p>安全维护是指修复使用中的安全风险问题,这一块主要是解决来自白帽子提交的漏洞问题。&lt;/p>
&lt;p>新功能合入在每个版本中会持续 &lt;strong>6&lt;/strong> 个月,稳定性维护在每个版本中会持续 &lt;strong>12&lt;/strong> 个月,安全维护在每个版本中会持续 &lt;strong>18&lt;/strong> 个月。&lt;/p>
&lt;p>因此,从整体迭代规划来看,每 6 个月 Dubbo 会开始一个新的版本迭代,与之同时的,也会有一个版本宣布生命周期结束(EOL)。&lt;/p>
&lt;h2 id="逐版本维护规划">逐版本维护规划&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>上图为具体到每个版本在每个时间点的维护状态图。&lt;/p>
&lt;p>对于 Dubbo 2.7 以及 3.0 版本,目前已经进入了安全维护阶段,将在 2023 年 3 月宣布版本维护周期结束。&lt;/p>
&lt;p>对于 Dubbo 3.1 版本,目前已经是稳定性维护阶段,如前一小节所述,目前只会继续合入和稳定性修改的修改。处于稳定性维护阶段的版本也是社区推荐生产使用的最新版本。稳定性维护工作将持续到 2023 年 3 月,在此之后会有持续 6 个月的安全维护阶段,最终在 2023 年 9 月结束其版本的生命周期。&lt;/p>
&lt;p>对于 Dubbo 3.2 版本,目前仍处在新功能合入的阶段,发布的是 beta 版本。此阶段适合一些尝鲜使用,版本稳定性相较于已经进入仅稳定性维护阶段的版本稍欠。在 2023 年 3 月,Dubbo 3.2 将结束新功能合入周期,进行充分的稳定性验证,并正式发布生产可用的 GA 版本。此后在 2023 年 9 月之前会进行稳定性的维护,在 2024 年 3 月结束版本的生命周期。&lt;/p>
&lt;p>对于 Dubbo 3.3 版本,将在 2023 年 3 月紧随着 3.2 版本结束新功能合入阶段后正式开始开发。因此,Dubbo 3.3 版本的第一个 beta release 也将会在 2023 年 3 月发布。同样的,经过 6 个月的新功能合入阶段之后,Dubbo 3.3 版本将会在 2023 年 9 月正式发布生产可用 GA 版本,同时进入稳定性维护阶段。之后在 2024 年 3 月进入安全维护阶段,在 2024 年 9 月结束版本的生命周期。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>对于绝大多数的生产用户,我们建议使用&lt;strong>当前处于仅稳定性维护阶段的版本的最新小版本&lt;/strong>。如在 2022 年 9 月至 2023 年 3 月这段时间,Dubbo 3.1 是处于仅稳定性维护阶段的,因此首选 Dubbo 3.1 版本。而对于如 3.1.0、3.1.1、3.1.2 等的 Dubbo 3.1.x 的多个小版本,我们建议直接使用最新的小版本。由于各个小版本之间仅包含稳定性修复,所以越往后的版本是越稳定的。&lt;/p>
&lt;p>对于愿意尝鲜、体验新功能的用户,可以直接基于最新的开发版本进行测试。即使是开发版本,Dubbo 也有一整套的质量保障机制,如果使用过程中遇到问题,欢迎向社区提交反馈。&lt;/p>
&lt;p>目前,阿里巴巴集团已经基于 Dubbo 3.0 的稳定版本完美支撑了整个核心电商今年双十一的所有调用,现在也正在升级到 Dubbo 3.1 这个最新的稳定版本上来。我们欢迎更多的用户一起升级到 Dubbo 3.1 版本上,在升级过程中可以参考官网给出的升级指南,遇到任何问题也可以通过 issue、微信群、钉钉群及时反馈社区,我们将尽全力协助。&lt;/p>
&lt;h2 id="更多">更多&lt;/h2>
&lt;p>在版本规划的方向上,除了本次发布的迭代线路图,Dubbo 还将通过一系列的机制保障用户的使用体验是连续的、稳定的。在大版本升级方面,Dubbo 将建设&lt;strong>完善的版本升级指南&lt;/strong>,如 Dubbo 3.1 版本升级到 Dubbo 3.2 版本中的重大修改点,让用户在升级的时候能够清晰的了解其中的风险点,更好的规划升级节奏。&lt;/p>
&lt;p>此外,对于目前人在大范围使用的 Dubbo 2.7 版本,在近期 Dubbo 社区也将推出&lt;strong>针对 Dubbo 2.7 版本的升级的指南与工具&lt;/strong>,通过工程化的方式让所有的 Dubbo 2.7 用户都可以提前查出所有的版本差异点,平滑升级到 Dubbo 3 上来。&lt;/p></description></item><item><title>Blog: Dubbo Java 3.0.2 发版公告</title><link>https://dubbo.apache.org/zh-cn/blog/2021/07/18/dubbo-java-3.0.2-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</link><pubDate>Sun, 18 Jul 2021 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2021/07/18/dubbo-java-3.0.2-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</guid><description>
&lt;h2 id="新特性">新特性&lt;/h2>
&lt;ul>
&lt;li>支持通过 @DubboService 注解暴露泛化服务&lt;/li>
&lt;li>元数据中心xml格式的配置支持 protocol 和 port 属性&lt;/li>
&lt;li>兼容 curator5 以上的版本&lt;/li>
&lt;li>点对点调用支持*通配符进行匹配, 一个提供者地址可对应多个接口&lt;/li>
&lt;li>为应用级别的服务发现增加动态配置去进行规则覆盖&lt;/li>
&lt;li>对提供者测的动态配置覆盖规则提供开关,可以使提供者无视动态配置,不重新暴露&lt;/li>
&lt;li>支持 native image&lt;/li>
&lt;li>提供取消执行 shutdown hook 的开关&lt;/li>
&lt;li>支持 Kubernetes Mesh 的服务治理规则&lt;/li>
&lt;li>Netty 连接支持 SSL 配置&lt;/li>
&lt;/ul>
&lt;h2 id="bug-修复">Bug 修复&lt;/h2>
&lt;ul>
&lt;li>DubboBootStrap start 重复调用后,动态配置被覆盖&lt;/li>
&lt;li>动态配置规则被删除后,依然生效&lt;/li>
&lt;li>triple 协议在暴露时会抛出空指针异常&lt;/li>
&lt;li>ConfigCenterConfig.setAddress 方法会覆盖掉 username 属性&lt;/li>
&lt;li>DefaultFuture.closeChannel 会销毁掉消费端测的线程池&lt;/li>
&lt;li>TripleClientHandler.writeRequest 抛出空指针异常&lt;/li>
&lt;li>解析3.0迁移规则异常时会抛出空指针异常&lt;/li>
&lt;li>Activated Extensions 的顺序性问题&lt;/li>
&lt;li>URLAddress.parse 方法在解析 ipv6 地址时存在问题&lt;/li>
&lt;li>用户自定义的参数在 properties 配置中不生效&lt;/li>
&lt;li>同时使用 API 模式和 Spring 模式配置时属性,Config Id 存在覆盖问题&lt;/li>
&lt;li>应用级别服务发现在启动时不生效&lt;/li>
&lt;li>Nacos 注册中心无法动态感知提供者的数量变化&lt;/li>
&lt;li>${dubbo.application} 在 xml 文件中无法被 spring 的 placeholder 规则给解析替换&lt;/li>
&lt;li>获取实例参数的顺序问题,先去获取了实例级别参数,再去获取接口级别参数&lt;/li>
&lt;li>当 DubboConfigBeanInitializer 不存在时,注册应用启动监听器会抛出异常&lt;/li>
&lt;li>Mock 时 参数中包含 &amp;lsquo;:&amp;rsquo; 或者 &amp;lsquo;=&amp;rsquo; 字符时,不生效&lt;/li>
&lt;li>删除 Mesh 规则时空指针异常&lt;/li>
&lt;/ul>
&lt;h2 id="优化">优化&lt;/h2>
&lt;ul>
&lt;li>抓住 RemovalTask 的异常,保证信号量能够释放&lt;/li>
&lt;li>通过 唯一 service name 检查 ReferenceConfig/ServiceConfig 是否重复&lt;/li>
&lt;li>优化生成随机数的性能&lt;/li>
&lt;li>如果用户使用接口级别去做服务发现,不发布应用与接口的映射数据到元数据中心&lt;/li>
&lt;li>使用 StringBuilder#append(Char) 提升性能&lt;/li>
&lt;li>保证 GRPC 编译的类文件中接口的顺序&lt;/li>
&lt;li>优化 reference bean 的占位符解析&lt;/li>
&lt;li>MergeableClusterInvoker 中使用 CompletableFuture#get(long, TimeUnit) 去提升性能&lt;/li>
&lt;li>内置服务 MetadataService 不延迟暴露&lt;/li>
&lt;li>优化 ConfigBean 和 Bootstrap 的启动逻辑&lt;/li>
&lt;li>优化 Config 检查是否重复的逻辑&lt;/li>
&lt;li>使用 Ring 数据结构去进行注册通知&lt;/li>
&lt;li>优化动态配置的初始化逻辑&lt;/li>
&lt;li>ConfigManager 使用 ConcurrentHashMap 去移除锁逻辑,提升 equals 和 toString 性能&lt;/li>
&lt;li>优化 MetadataInfo equals 方法 以及 Instance Listener&lt;/li>
&lt;li>优化异步 export/refer 逻辑&lt;/li>
&lt;li>使用 TreeSet 数据结构保证应用级别服务发现时应用名称的顺序一致&lt;/li>
&lt;li>RegistryNotifier 的第一个十次通知不延迟&lt;/li>
&lt;li>dubbo-compile 编译使用新的格式生成 stub&lt;/li>
&lt;li>Mesh 服务治理规则在动态配置中心的分组和其他规则分组统一,由 DEFAULT_GROUP 改成 dubbo&lt;/li>
&lt;li>使用 nacos 用作注册中心时,可以在注册中心地址中使用参数来改变在 nacos 中的分组&lt;/li>
&lt;li>计算 ServiceInfo 的 reversion 时,移除运行时参数,避免生成多个 reversion&lt;/li>
&lt;li>Nacos 注册中心抛出异常时,将异常封装称 RpcException 抛出&lt;/li>
&lt;li>禁止动态配置中心对一些权限参数进行动态修改&lt;/li>
&lt;li>优化 Config Bean 的初始化流程,并兼容 spring 3.x/4.1.x&lt;/li>
&lt;li>Bootstrap.start 方法可重入,暴露或引用新的服务&lt;/li>
&lt;li>将 org.apache.dubbo 包中的类默认添加到白名单中&lt;/li>
&lt;li>保证生成的 Config 的 Id 唯一,并检查 Config 是否之前存在&lt;/li>
&lt;li>Javaassist 兼容改变 override 声明字段&lt;/li>
&lt;li>重构解码时的检查逻辑,当找不到 path, version 对应的提供者是,抛出异常&lt;/li>
&lt;li>当 ApplicationModel 为 null 时,兼容 adaptive extensions&lt;/li>
&lt;/ul>
&lt;h2 id="代码质量提升">代码质量提升&lt;/h2>
&lt;p>感谢以下提高 Apache Dubbo 的稳定性的贡献。&lt;/p>
&lt;p>&lt;a href="https://github.com/apache/dubbo/pull/8111">#8111&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8147">#8147&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8164">#8164&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8177">#8177&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8180">#8180&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8161">#8161&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8183">#8183&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8205">#8205&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8173">#8173&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8219">#8219&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8228">#8228&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8232">#8232&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8230">#8230&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8236">#8236&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8260">#8260&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8262">#8262&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8252">#8252&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8246">#8246&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8208">#8208&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8278">#8278&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8267">#8267&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8277">#8277&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8291">#8291&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8296">#8296&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8302">#8302&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8175">#8175&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8319">#8319&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8309">#8309&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8336">#8336&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8332">#8332&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8328">#8328&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8355">#8355&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8396">#8396&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8401">#8401&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8395">#8395&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8415">#8415&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8406">#8406&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8411">#8411&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8418">#8418&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8439">#8439&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8404">#8404&lt;/a>,
&lt;a href="https://github.com/apache/dubbo/pull/8443">#8443&lt;/a>&lt;/p>
&lt;h2 id="maven依赖变化">Maven依赖变化&lt;/h2>
&lt;ul>
&lt;li>移除依赖: org.eclipse.collections:eclipse-collections&lt;/li>
&lt;li>移除依赖: com.google.guava:guava&lt;/li>
&lt;li>jetty: 9.4.11.v20180605 -&amp;gt; 9.4.43.v20210629&lt;/li>
&lt;li>apollo client: 1.1.1 -&amp;gt; 1.8.0&lt;/li>
&lt;li>snakeyaml: 1.20 -&amp;gt; 1.29&lt;/li>
&lt;li>tomcat embed: 8.5.31 -&amp;gt; 8.5.69&lt;/li>
&lt;li>nacos client: 2.0.0 -&amp;gt; 2.0.2&lt;/li>
&lt;li>swagger: 1.5.19 -&amp;gt; 1.5.24&lt;/li>
&lt;li>hessian_lite: 3.2.8 -&amp;gt; 3.2.11&lt;/li>
&lt;/ul></description></item><item><title>Blog: Dubbo Java 3.1.3 正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2022/07/18/dubbo-java-3.1.3-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Mon, 18 Jul 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/07/18/dubbo-java-3.1.3-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;h2 id="dubbo-313">Dubbo 3.1.3&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/3-1-3.png" alt="image.png">&lt;/p>
&lt;h3 id="修改内容">修改内容&lt;/h3>
&lt;ul>
&lt;li>修复本地调用的过程中 Filter 顺序异常的问题&lt;/li>
&lt;li>支持导入协议配置项到 MetadataService&lt;/li>
&lt;li>支持在发布 MetadataService 时自动选择可用端口&lt;/li>
&lt;li>完善错误码内容&lt;/li>
&lt;li>泛化调用下支持一致性哈希负载均衡&lt;/li>
&lt;li>修复泛化调用时反序列化异常的问题&lt;/li>
&lt;li>修复由于 JVM Methods 顺序不一致导致的应用级元数据无法复用的问题&lt;/li>
&lt;li>默认关闭端口复用能力,修复 remoting 组建找不到 SPI 的问题&lt;/li>
&lt;li>修复由于引用计数异常导致的 safe gard 问题&lt;/li>
&lt;li>删除 Nacos 注册中心组建中使用的 guava 依赖&lt;/li>
&lt;li>修复接口级使用 Nacos 注册中心时由于地址聚合导致的无法下线的问题&lt;/li>
&lt;li>多个代码质量优化提交&lt;/li>
&lt;/ul>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/zhangzq7">@zhangzq7&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10847">#10847&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;li>&lt;a href="https://github.com/akaakking">@akaakking&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10799">#10799&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;li>&lt;a href="https://github.com/wxbty">@wxbty&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10921">#10921&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;li>&lt;a href="https://github.com/haoxz11">@haoxz11&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10937">#10937&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h3 id="新贡献者-1">新贡献者&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/weixsun">@weixsun&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10941">#10941&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h2 id="未来的版本规划">未来的版本规划&lt;/h2>
&lt;p>3.0、3.1、3.2 有什么区别?未来会怎么发展?发版周期是怎么样的?更多的版本迭代规划也将在近期推出,欢迎关注 Apache Dubbo 官方公众号获取最新的信息。&lt;/p></description></item><item><title>Blog: Dubbo Java 3.2.0-beta.3 正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/18/dubbo-java-3.2.0-beta.3-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Sun, 18 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/18/dubbo-java-3.2.0-beta.3-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo 3.2.0-beta.3 版本是目前 Dubbo 3 的最新特性版本,包括了如 Spring Boot 3、JDK 17、服务粒度的线程池隔离等新特性的支持,欢迎大家尝鲜使用。&lt;/p>
&lt;h1 id="dubbo-320-beta3">Dubbo 3.2.0-beta.3&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/3-2-0-beta-3.png" alt="image.png">&lt;/p>
&lt;p>&lt;strong>注:Dubbo 3.2.0-beta.3 的代码基础和 Dubbo 3.1.4 完全一致,因此在 Dubbo 3.1.4 中包括的所有修改内容,在 Dubbo 3.2.0-beta.3 中也同样存在,后续说明中对于重复的内容讲不再赘述。&lt;/strong>&lt;/p>
&lt;h3 id="新特性">新特性&lt;/h3>
&lt;ul>
&lt;li>支持使用 jackson 作为Dubbo 内部的 JSON 序列化方式&lt;/li>
&lt;li>优化 Dubbo Logger 的选择逻辑,在存在多种日志框架的情况下会尝试读取其配置,选择一个有效的日志框架,解决在 SpringBoot 等场景下 Dubbo 日志不输出的问题。&lt;/li>
&lt;li>Triple 协议支持客户端流控&lt;/li>
&lt;li>支持发布 Dubbo Metrics 数据到 Spring Boot 的 Endpoints 上&lt;/li>
&lt;li>支持可选关闭线程池满时的线程 Dump 以及支持配置 Dump 的结果输出路径&lt;/li>
&lt;li>xDS 实现支持路由规则的解析&lt;/li>
&lt;li>支持 Dubbo QoS 命令安全性分级,默认对外暴露存活检测的端口,支持 Kubernetes 的原生接入&lt;/li>
&lt;li>支持基于 P2C 的自适应负载均衡&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复默认 Metadata 缓存未开启的问题&lt;/li>
&lt;li>修复 Metrics 指标资源路径错误的问题&lt;/li>
&lt;li>完善线程池隔离的配置,默认采用共享线程池,避免创建过多的线程&lt;/li>
&lt;li>完善 prefer-serialization 的选择逻辑,提供向前兼容的能力&lt;/li>
&lt;li>修复 Triple 协议传输时未携带版本号导致版本调用错误的问题&lt;/li>
&lt;li>完善 GraalVM Native Image 的支持&lt;/li>
&lt;/ul>
&lt;h3 id="性能优化">性能优化&lt;/h3>
&lt;ul>
&lt;li>优化在连接数高时由于获取 channels 数量导致的资源占用问题&lt;/li>
&lt;/ul>
&lt;h3 id="代码优化">代码优化&lt;/h3>
&lt;p>本次发布中有 5 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="贡献者">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献:@AlbumenJ, @asa3311, @conghuhu, @CrazyHZM, @gitchenjh, @haoyann, @JavaHello, @Koooooo-7, @nannanfighting, @ningboliu, @shanuo0312, @songxiaosheng, @tonycody, @XDanwar&lt;/p>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@JavaHello 在 PR #10970 提交了第一个贡献&lt;/li>
&lt;li>@songxiaosheng 在 PR #10997提交了第一个贡献&lt;/li>
&lt;li>@Koooooo-7 在 PR #11051 提交了第一个贡献&lt;/li>
&lt;li>@ningboliu 在 PR #10745 提交了第一个贡献&lt;/li>
&lt;li>@XDanwar 在 PR #11063 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="未来版本规划">未来版本规划&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>Dubbo 版本的发布规划以及在《&lt;a href="https://mp.weixin.qq.com/s?__biz=MzIwODYwNTA4MA==&amp;amp;mid=2247484424&amp;amp;idx=1&amp;amp;sn=2f5ff4846f7dafad325f78fd8cf4d1fc&amp;amp;chksm=9701deffa07657e9a46eb97bb859770b4856599566b992724013a848a730f394702938e72404&amp;amp;token=1547029975&amp;amp;lang=zh_CN#rd">聚焦稳定性,Dubbo 发版规划公布&lt;/a>》一文中正式发布,欢迎查看。&lt;/p></description></item><item><title>Blog: Dubbo 3.2.0-beta.2 正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2022/11/18/dubbo-3.2.0-beta.2-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Fri, 18 Nov 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/11/18/dubbo-3.2.0-beta.2-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;h2 id="dubbo-320-beta2">Dubbo 3.2.0-beta.2&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/3-2-0-beta-2.png" alt="image.png">&lt;/p>
&lt;h3 id="修改内容">修改内容&lt;/h3>
&lt;ul>
&lt;li>切换到在用户线程序列化,提升协议性能&lt;/li>
&lt;li>支持 Netty3 的端口复用&lt;/li>
&lt;li>修复存在多个用户配置的 providedBy 时不生效的问题&lt;/li>
&lt;li>支持 istio 的 first-party-jwt 能力&lt;/li>
&lt;li>删除 fastjson 和 gson 的传递依赖&lt;/li>
&lt;li>支持可选 appResponse 不透传的能力&lt;/li>
&lt;li>切换到 Fastjson2 为默认序列化依赖&lt;/li>
&lt;li>完善注册中心推送的日志&lt;/li>
&lt;li>修复路由刷新时机早于服务目录刷新时机的问题&lt;/li>
&lt;li>关闭地址推空保护&lt;/li>
&lt;li>更新支持 GraalVM Native 的 SPI 生成代码&lt;/li>
&lt;li>支持使用 plain text 模式传输 xds 通道&lt;/li>
&lt;li>支持 Nacos 批量注册,修复多注册覆盖的问题&lt;/li>
&lt;li>支持 Spring Framework 6 and Spring Boot 3&lt;/li>
&lt;li>多个代码质量优化提交&lt;/li>
&lt;li>前述 Dubbo 3.1.3 的所有修改内容&lt;/li>
&lt;/ul>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/weixsun">@weixsun&lt;/a> 在 &lt;a href="https://github.com/apache/dubbo/pull/10941">#10941&lt;/a> 中提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h2 id="未来的版本规划">未来的版本规划&lt;/h2>
&lt;p>3.0、3.1、3.2 有什么区别?未来会怎么发展?发版周期是怎么样的?更多的版本迭代规划也将在近期推出,欢迎关注 Apache Dubbo 官方公众号获取最新的信息。&lt;/p></description></item><item><title>Blog: Dubbo Java 2.7.14 发版公告</title><link>https://dubbo.apache.org/zh-cn/blog/2020/05/18/dubbo-java-2.7.14-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</link><pubDate>Mon, 18 May 2020 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2020/05/18/dubbo-java-2.7.14-%E5%8F%91%E7%89%88%E5%85%AC%E5%91%8A/</guid><description>
&lt;h2 id="变动项">变动项&lt;/h2>
&lt;ul>
&lt;li>为 ServiceDiscovery 增加动态配置中心的覆盖规则。(#8389)&lt;/li>
&lt;li>修复当 mock 参数中包含 &amp;lsquo;:&amp;rsquo; 或者 &amp;lsquo;=&amp;rsquo; 符号时无法正常使用的问题。(#8379)&lt;/li>
&lt;li>修复 zone 参数对 ZoneAwareClusterInvoker 配置无法生效的问题。(#8521)&lt;/li>
&lt;li>为序列化白名单检查增加开关,默认为 true。(#8537)&lt;/li>
&lt;li>修复当请求超时时序列化检查的空指针异常。(#8587)&lt;/li>
&lt;li>修复 NetUtils.ignoreNetworkInterface 无法处理网卡中包含 &amp;lsquo;(&amp;rsquo; 符号的问题。(#8629)&lt;/li>
&lt;li>统一获取本地地址的方式。(#8679)&lt;/li>
&lt;li>修复当重试参数为0,依旧会重试1次的问题。(#8743)&lt;/li>
&lt;li>当清除未使用的 invoker 时,立即关闭客户端。(#8756)&lt;/li>
&lt;li>修复 destroy 方法以及 doOverrideIfNecessary 中的异常。(#8683)&lt;/li>
&lt;li>DefaultFuture.closeChannel 根据日志级别选择是否打印请求的详细数据。(#8778)&lt;/li>
&lt;li>使用 MapUtils 替换 AttachmentsAdapter(#8772)&lt;/li>
&lt;/ul>
&lt;h2 id="maven依赖变化">Maven依赖变化&lt;/h2>
&lt;ul>
&lt;li>netty4: 4.1.51.Final -&amp;gt; 4.1.66.Final&lt;/li>
&lt;li>netty4_ssl: 2.0.39.Final -&amp;gt; 2.0.40.Final&lt;/li>
&lt;li>http_client: 4.5.3 -&amp;gt; 4.5.13&lt;/li>
&lt;li>jetty: 9.4.11.v20180605 -&amp;gt; 9.4.43.v20210629&lt;/li>
&lt;li>apollo_client: 1.1.1 -&amp;gt; 1.8.0&lt;/li>
&lt;li>tomcat_embed: 8.5.31-&amp;gt; 9.0.48&lt;/li>
&lt;li>commons_io: 2.6 -&amp;gt; 2.7&lt;/li>
&lt;li>curator: 5.0.0 -&amp;gt; 5.1.0&lt;/li>
&lt;li>hessian_lite: 3.2.8 -&amp;gt; 3.2.11&lt;/li>
&lt;/ul></description></item><item><title>Blog: Dubbo Go 1.5.1</title><link>https://dubbo.apache.org/zh-cn/blog/1/01/01/dubbo-go-1.5.1/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/1/01/01/dubbo-go-1.5.1/</guid><description>
&lt;p>近期我们发布了 dubbo-go v1.5.1,虽然是 v1.5 的一个子版本,但相比于 v1.5.0, 社区还是投入了很大人力添加了如下重大改进。&lt;/p>
&lt;h2 id="1-应用维度注册模型">1 应用维度注册模型&lt;/h2>
&lt;p>在新模型 release 后,我们发现 Provider 每个 URL 发布元数据都会注册 ServiceInstance,影响性能需要优化。&lt;/p>
&lt;p>我们的优化方案是:&lt;/p>
&lt;p>去除 ServiceDiscoveryRegistry 中注册 ServiceInstance 的代码,在 config_loader 中的loadProviderConfig 方法的最后注册 ServiceInstance&lt;/p>
&lt;p>具体步骤:&lt;/p>
&lt;ol>
&lt;li>获取所有注册的 Registry,过滤出 ServiceDiscoveryRegistry,拿取所有 ServiceDiscovery。&lt;/li>
&lt;li>创建 ServiceInstance。&lt;/li>
&lt;li>每个 ServiceDiscovery 注册 ServiceInstance。&lt;/li>
&lt;/ol>
&lt;p>保证 Provider 在注册成功之后,才暴露元数据信息。&lt;/p>
&lt;h2 id="2-支持基于-seata-的事务">2 支持基于 Seata 的事务&lt;/h2>
&lt;p>基于 Seata 扩展实现。通过增加过滤器,在服务端接收 xid 并结合 &lt;a href="https://github.com/seata-golang/seata-golang">seata-golang&lt;/a> 达到支持分布式事务的目的。 从而使 Dubbo-go 在分布式场景下,让用户有更多的选择,能适应更多的个性化场景。&lt;/p>
&lt;p>我们在 dubbo-samples 中给出了 &lt;a href="https://github.com/apache/dubbo-go-samples/tree/1.5/seata">事务测试用例&lt;/a> 。&lt;/p>
&lt;h2 id="3-多注册中心集群负载均衡">3 多注册中心集群负载均衡&lt;/h2>
&lt;p>对于多注册中心订阅的场景,选址时的多了一层注册中心集群间的负载均衡:&lt;/p>
&lt;p>在 Cluster Invoker 这一级,我们支持的选址策略有:&lt;/p>
&lt;ul>
&lt;li>指定优先级&lt;/li>
&lt;li>同 zone 优先&lt;/li>
&lt;li>权重轮询&lt;/li>
&lt;/ul>
&lt;h2 id="4-传输链路安全性">4 传输链路安全性&lt;/h2>
&lt;p>该版本在传输链路的安全性上做了尝试,对于内置的 Dubbo getty Server 提供了基于 TLS 的安全链路传输机制。&lt;/p>
&lt;p>为尽可能保证应用启动的灵活性,TLS Cert 的指定通过配置文件方式,具体请参见 Dubbo-go 配置读取规则与 TLS 示例:&lt;/p>
&lt;h2 id="5-路由功能增强">5 路由功能增强&lt;/h2>
&lt;p>本次路由功能重点支持了 动态标签路由 和 应用/服务级条件路由。&lt;/p>
&lt;h3 id="51-动态标签路由">5.1 动态标签路由&lt;/h3>
&lt;p>标签路由通过将某一个或多个服务的提供者划分到同一个分组,约束流量只在指定分组中流转,从而实现流量隔离的目的,可以作为蓝绿发布、灰度发布等场景的能力基础。&lt;/p>
&lt;p>标签主要是指对 Provider 端应用实例的分组,目前有两种方式可以完成实例分组,分别是&lt;code>动态规则打标&lt;/code>和&lt;code>静态规则打标&lt;/code>,其中动态规则相较于静态规则优先级更高,而当两种规则同时存在且出现冲突时,将以动态规则为准。&lt;/p>
&lt;h4 id="511-动态规则打标">5.1.1 动态规则打标&lt;/h4>
&lt;p>可随时在&lt;a href="https://dubbo.apache.org/zh-cn/docsv2.7/user/examples/routing-rule/">服务治理控制台&lt;/a>下发标签归组规则&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># governance-tagrouter-provider应用增加了两个标签分组tag1和tag2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># tag1包含一个实例 127.0.0.1:20880&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># tag2包含一个实例 127.0.0.1:20881&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: &lt;span style="color:#cb4b16">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">runtime&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">enabled&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: governance-tagrouter-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">tags&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: tag1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">addresses&lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:20880&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: tag2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">addresses&lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;127.0.0.1:20881&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="512-静态规则打标">5.1.2 静态规则打标&lt;/h4>
&lt;p>可以在 server 配置文件的 tag 字段里设置&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;hangzhouzk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">warmup&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">tag&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">loadbalance&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;random&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>consumer 添加 tag 至 attachment 即可&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>ctx &lt;span style="color:#719e07">:=&lt;/span> context.&lt;span style="color:#268bd2">Background&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>attachment &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">make&lt;/span>(&lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#dc322f">string&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>attachment[&lt;span style="color:#2aa198">&amp;#34;dubbo.tag&amp;#34;&lt;/span>] = &lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ctx = context.&lt;span style="color:#268bd2">WithValue&lt;/span>(ctx, constant.AttachmentKey, attachment)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(ctx, []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>请求标签的作用域为每一次 invocation,使用 attachment 来传递请求标签,注意保存在 attachment 中的值将会在一次完整的远程调用中持续传递,得益于这样的特性,我们只需要在起始调用时,通过一行代码的设置,达到标签的持续传递。&lt;/p>
&lt;h4 id="513-规则详解">5.1.3 规则详解&lt;/h4>
&lt;h5 id="格式">格式&lt;/h5>
&lt;ul>
&lt;li>&lt;code>Key&lt;/code>明确规则体作用到哪个应用。&lt;strong>必填&lt;/strong>。&lt;/li>
&lt;li>&lt;code>enabled=true&lt;/code> 当前路由规则是否生效,可不填,缺省生效。&lt;/li>
&lt;li>&lt;code>force=false&lt;/code> 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 &lt;code>false&lt;/code>。&lt;/li>
&lt;li>&lt;code>runtime=false&lt;/code> 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为 &lt;code>true&lt;/code>,需要注意设置会影响调用的性能,可不填,缺省为 &lt;code>false&lt;/code>。&lt;/li>
&lt;li>&lt;code>priority=1&lt;/code> 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 &lt;code>0&lt;/code>。&lt;/li>
&lt;li>&lt;code>tags&lt;/code> 定义具体的标签分组内容,可定义任意n(n&amp;gt;=1)个标签并为每个标签指定实例列表。&lt;strong>必填&lt;/strong>&lt;/li>
&lt;li>
&lt;ul>
&lt;li>name, 标签名称&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>addresses, 当前标签包含的实例列表&lt;/li>
&lt;/ul>
&lt;h5 id="降级约定">降级约定&lt;/h5>
&lt;ol>
&lt;li>&lt;code>dubbo.tag=tag1&lt;/code> 时优先选择 标记了 &lt;code>tag=tag1&lt;/code> 的 provider。若集群中不存在与请求标记对应的服务,默认将降级请求 tag 为空的 provider;如果要改变这种默认行为,即找不到匹配 tag1 的 provider 返回异常,需设置&lt;code>dubbo.force.tag=true&lt;/code>。&lt;/li>
&lt;li>&lt;code>dubbo.tag&lt;/code> 未设置时,只会匹配 tag 为空的 provider。即使集群中存在可用的服务,若 tag 不匹配也就无法调用,这与约定 1 不同,携带标签的请求可以降级访问到无标签的服务,但不携带标签/携带其他种类标签的请求永远无法访问到其他标签的服务。&lt;/li>
&lt;/ol>
&lt;h3 id="52-应用服务级条件路由">5.2 应用/服务级条件路由&lt;/h3>
&lt;p>您可以在路由规则配置中配置多个条件路由及其粒度&lt;/p>
&lt;p>Sample:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># dubbo router yaml configure file&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">routerRules&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">scope&lt;/span>: application
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: BDTService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">priority&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">enable&lt;/span>: &lt;span style="color:#cb4b16">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">conditions &lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;host = 192.168.199.208 =&amp;gt; host = 192.168.199.208 &amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">scope&lt;/span>: service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">key&lt;/span>: com.ikurento.user.UserProvider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">priority&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">conditions &lt;/span>: [&lt;span style="color:#2aa198">&amp;#34;host = 192.168.199.208 =&amp;gt; host = 192.168.199.208 &amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="521-规则详解">5.2.1 规则详解&lt;/h4>
&lt;h5 id="各字段含义">各字段含义&lt;/h5>
&lt;ul>
&lt;li>scope表示路由规则的作用粒度,scope的取值会决定key的取值。必填。&lt;/li>
&lt;li>
&lt;ul>
&lt;li>service 服务粒度&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>application 应用粒度&lt;/li>
&lt;li>Key明确规则体作用在哪个服务或应用。必填。&lt;/li>
&lt;li>
&lt;ul>
&lt;li>scope=service时,key取值为[{group}/]{service}[:{version}]的组合&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>scope=application时,key取值为application名称&lt;/li>
&lt;li>enabled=true 当前路由规则是否生效,可不填,缺省生效。&lt;/li>
&lt;li>force=false 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 false。&lt;/li>
&lt;li>runtime=false 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为 true,需要注意设置会影响调用的性能,可不填,缺省为 false。&lt;/li>
&lt;li>priority=1 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0。&lt;/li>
&lt;li>conditions 定义具体的路由规则内容。必填。&lt;/li>
&lt;/ul>
&lt;h2 id="6-回顾与展望">6 回顾与展望&lt;/h2>
&lt;p>Dubbo-go 处于一个比较稳定成熟的状态。目前新版本正处于往云原生方向的尝试,应用服务维度注册是首先推出的功能,这是一个和之前模型完全不一样的新注册模型。该版本是我们朝云原生迈进新一步的关键版本。除此之外,包含在该版本也有一些之前提到的优化。&lt;/p>
&lt;p>下一个版本 v1.5.2,本次的关注重点以通信模型改进为主,除此之外,与 2.7.x 的兼容性、易用性及质量保证也是本次关注的信息。&lt;/p>
&lt;p>在&lt;strong>服务发现&lt;/strong>,会支持更加多的方式,如:文件、Consul。 从而使 Dubbo-go 在服务发现场景下,让用户有更多的选择,能适应更多的个性化场景。&lt;/p>
&lt;p>另外 &lt;strong>易用性及质量保证&lt;/strong>,主要关注的是 samples 与自动化构建部分。可降低用户上手 Dubbo-go 的难度,提高代码质量。&lt;/p>
&lt;p>目前下一个版本正在紧锣密鼓的开发中,具体规划及任务清单&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> ,都已经在 Github 上体现。&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://github.com/apache/dubbo-go/projects/10">https://github.com/apache/dubbo-go/projects/10&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Blog: Dubbo Go Hessian2 v1.7.0</title><link>https://dubbo.apache.org/zh-cn/blog/1/01/01/dubbo-go-hessian2-v1.7.0/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/1/01/01/dubbo-go-hessian2-v1.7.0/</guid><description>
&lt;p>Dubbo-go-hessian2 v1.7.0已发布,详见 &lt;a href="https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0">https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0&lt;/a>, 以下对这次更新内容进行详细整理。&lt;/p>
&lt;p>另外v1.6.3 将 attachment 类型由 map[string]stiring 改为map[string]interface{} 导致版本不兼容问题,这部分已还原,后续的计划是将dubbo协议的request/response对象整体迁移到dubbogo项目中进行迭代修改, hessian2中将不再改动到request/response对象。&lt;/p>
&lt;h2 id="1-new-features">1. New Features&lt;/h2>
&lt;h3 id="11-add-getstacktrace-method-into-throwabler-and-its-implements-207httpsgithubcomapachedubbo-go-hessian2pull207">1.1 add GetStackTrace method into Throwabler and its implements. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/207">#207&lt;/a>&lt;/h3>
&lt;p>go语言client请求java语言服务时,如果java语言抛出了异常,异常对应的堆栈信息是被保存在StackTraceElement中。&lt;/p>
&lt;p>这个异常信息在日志中最好能被打印出来,以方便客户端排查问题,所以在Throwabler和对应子类中增加了StackTraceElement的获取。&lt;/p>
&lt;p>注:其实还有一种更好的方法,所有的具体的异常类型都包含java_exception/exception.go的Throwable struct。这样只需要在Throwable中增加GetStackTrace方法就可以了。但是这种方式需要更多的测试验证,改动的逻辑相对会复杂一些。但是代码会更整洁。 这里先不用这种方法。&lt;/p>
&lt;h3 id="12-catch-user-defined-exceptions-208httpsgithubcomapachedubbo-go-hessian2pull208">1.2 catch user defined exceptions. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/208">#208&lt;/a>&lt;/h3>
&lt;p>golang中增加一个java中Exception对象的序列化输出方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">JavaException&lt;/span>() []&lt;span style="color:#dc322f">byte&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e &lt;span style="color:#719e07">:=&lt;/span> hessian.&lt;span style="color:#268bd2">NewEncoder&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception &lt;span style="color:#719e07">:=&lt;/span> java_exception.&lt;span style="color:#268bd2">NewException&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;java_exception&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.&lt;span style="color:#268bd2">Encode&lt;/span>(exception)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> e.&lt;span style="color:#268bd2">Buffer&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在output/output.go 提供调用入口:添加如下函数初始化声明&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> funcMap[&lt;span style="color:#2aa198">&amp;#34;JavaException&amp;#34;&lt;/span>] = testfuncs.JavaException
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>java代码中增加调用go方法序列化结果:&lt;/p>
&lt;p>&lt;strong>说明&lt;/strong>: Assert.assertEquals 不能直接比较Exception对象是否相等&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * test java java.lang.Exception object and go java_exception Exception struct
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Test&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">testException&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Exception exception &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Exception&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;java_exception&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object javaException &lt;span style="color:#719e07">=&lt;/span> GoTestUtil&lt;span style="color:#719e07">.&lt;/span>readGoObject&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;JavaException&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>javaException &lt;span style="color:#719e07">instanceof&lt;/span> Exception&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>assertEquals&lt;span style="color:#719e07">(&lt;/span>exception&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#719e07">((&lt;/span>Exception&lt;span style="color:#719e07">)&lt;/span> javaException&lt;span style="color:#719e07">).&lt;/span>getMessage&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="13-support-java8-time-object-212httpsgithubcomapachedubbo-go-hessian2pull212-221httpsgithubcomapachedubbo-go-hessian2pull221">1.3 support java8 time object. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/212">#212&lt;/a>, &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/221">#221&lt;/a>&lt;/h3>
&lt;p>golang中增加一个java8对象的序列化输出方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// test java8 java.time.Year
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">Java8TimeYear&lt;/span>() []&lt;span style="color:#dc322f">byte&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e &lt;span style="color:#719e07">:=&lt;/span> hessian.&lt;span style="color:#268bd2">NewEncoder&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> year &lt;span style="color:#719e07">:=&lt;/span> java8_time.Year{Year: &lt;span style="color:#2aa198">2020&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.&lt;span style="color:#268bd2">Encode&lt;/span>(year)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> e.&lt;span style="color:#268bd2">Buffer&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// test java8 java.time.LocalDate
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">Java8LocalDate&lt;/span>() []&lt;span style="color:#dc322f">byte&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e &lt;span style="color:#719e07">:=&lt;/span> hessian.&lt;span style="color:#268bd2">NewEncoder&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> date &lt;span style="color:#719e07">:=&lt;/span> java8_time.LocalDate{Year: &lt;span style="color:#2aa198">2020&lt;/span>, Month: &lt;span style="color:#2aa198">9&lt;/span>, Day: &lt;span style="color:#2aa198">12&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.&lt;span style="color:#268bd2">Encode&lt;/span>(date)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> e.&lt;span style="color:#268bd2">Buffer&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在output/output.go 提供调用入口:添加函数初始化声明&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> funcMap[&lt;span style="color:#2aa198">&amp;#34;Java8TimeYear&amp;#34;&lt;/span>] = testfuncs.Java8TimeYear
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> funcMap[&lt;span style="color:#2aa198">&amp;#34;Java8LocalDate&amp;#34;&lt;/span>] = testfuncs.Java8LocalDate
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>java代码中增加调用go方法序列化结果:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * test java8 java.time.* object and go java8_time/* struct
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Test&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">testJava8Year&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Year year &lt;span style="color:#719e07">=&lt;/span> Year&lt;span style="color:#719e07">.&lt;/span>of&lt;span style="color:#719e07">(&lt;/span>2020&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>assertEquals&lt;span style="color:#719e07">(&lt;/span>year
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">,&lt;/span> GoTestUtil&lt;span style="color:#719e07">.&lt;/span>readGoObject&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Java8TimeYear&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LocalDate localDate &lt;span style="color:#719e07">=&lt;/span> LocalDate&lt;span style="color:#719e07">.&lt;/span>of&lt;span style="color:#719e07">(&lt;/span>2020&lt;span style="color:#719e07">,&lt;/span> 9&lt;span style="color:#719e07">,&lt;/span> 12&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>assertEquals&lt;span style="color:#719e07">(&lt;/span>localDate&lt;span style="color:#719e07">,&lt;/span> GoTestUtil&lt;span style="color:#719e07">.&lt;/span>readGoObject&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Java8LocalDate&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="14-support-test-golang-encoding-data-in-java-213httpsgithubcomapachedubbo-go-hessian2pull213">1.4 support test golang encoding data in java. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/213">#213&lt;/a>&lt;/h3>
&lt;p>为了更好的测试验证hessian库,原来已经支持在golang中测试java的序列化数据,现在增加在java中测试golang的序列化数据,实现双向测试验证。&lt;/p>
&lt;p>golang中增加序列化输出方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">HelloWorldString&lt;/span>() []&lt;span style="color:#dc322f">byte&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e &lt;span style="color:#719e07">:=&lt;/span> hessian.&lt;span style="color:#268bd2">NewEncoder&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e.&lt;span style="color:#268bd2">Encode&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;hello world&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> e.&lt;span style="color:#268bd2">Buffer&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>将该方法注册到output/output.go中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// add all output func here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> funcMap[&lt;span style="color:#2aa198">&amp;#34;HelloWorldString&amp;#34;&lt;/span>] = testfuncs.HelloWorldString
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>output/output.go 提供调用入口:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> flag.&lt;span style="color:#268bd2">Parse&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">*&lt;/span>funcName &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, _ = fmt.&lt;span style="color:#268bd2">Fprintln&lt;/span>(os.Stderr, &lt;span style="color:#2aa198">&amp;#34;func name required&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#268bd2">Exit&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f, exist &lt;span style="color:#719e07">:=&lt;/span> funcMap[&lt;span style="color:#719e07">*&lt;/span>funcName]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> !exist {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, _ = fmt.&lt;span style="color:#268bd2">Fprintln&lt;/span>(os.Stderr, &lt;span style="color:#2aa198">&amp;#34;func name not exist: &amp;#34;&lt;/span>, &lt;span style="color:#719e07">*&lt;/span>funcName)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#268bd2">Exit&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">defer&lt;/span> &lt;span style="color:#268bd2">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">recover&lt;/span>(); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, _ = fmt.&lt;span style="color:#268bd2">Fprintln&lt;/span>(os.Stderr, &lt;span style="color:#2aa198">&amp;#34;error: &amp;#34;&lt;/span>, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#268bd2">Exit&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> _, err &lt;span style="color:#719e07">:=&lt;/span> os.Stdout.&lt;span style="color:#268bd2">Write&lt;/span>(&lt;span style="color:#268bd2">f&lt;/span>()); err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, _ = fmt.&lt;span style="color:#268bd2">Fprintln&lt;/span>(os.Stderr, &lt;span style="color:#2aa198">&amp;#34;call error: &amp;#34;&lt;/span>, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#268bd2">Exit&lt;/span>(&lt;span style="color:#2aa198">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#268bd2">Exit&lt;/span>(&lt;span style="color:#2aa198">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>java代码中增加调用go方法序列化结果:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">GoTestUtil&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> Object &lt;span style="color:#268bd2">readGoObject&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String func&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;read go data: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> func&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Process process &lt;span style="color:#719e07">=&lt;/span> Runtime&lt;span style="color:#719e07">.&lt;/span>getRuntime&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>exec&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;go run output/output.go -func_name=&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> func&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> File&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;..&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> exitValue &lt;span style="color:#719e07">=&lt;/span> process&lt;span style="color:#719e07">.&lt;/span>waitFor&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exitValue &lt;span style="color:#719e07">!=&lt;/span> 0&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>fail&lt;span style="color:#719e07">(&lt;/span>readString&lt;span style="color:#719e07">(&lt;/span>process&lt;span style="color:#719e07">.&lt;/span>getErrorStream&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InputStream is &lt;span style="color:#719e07">=&lt;/span> process&lt;span style="color:#719e07">.&lt;/span>getInputStream&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Hessian2Input input &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Hessian2Input&lt;span style="color:#719e07">(&lt;/span>is&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> input&lt;span style="color:#719e07">.&lt;/span>readObject&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> String &lt;span style="color:#268bd2">readString&lt;/span>&lt;span style="color:#719e07">(&lt;/span>InputStream in&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IOException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> StringBuilder out &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> StringBuilder&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> InputStreamReader reader &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> InputStreamReader&lt;span style="color:#719e07">(&lt;/span>in&lt;span style="color:#719e07">,&lt;/span> StandardCharsets&lt;span style="color:#719e07">.&lt;/span>UTF_8&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">char&lt;/span>&lt;span style="color:#719e07">[]&lt;/span> buffer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> &lt;span style="color:#dc322f">char&lt;/span>&lt;span style="color:#719e07">[&lt;/span>4096&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> bytesRead&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">((&lt;/span>bytesRead &lt;span style="color:#719e07">=&lt;/span> reader&lt;span style="color:#719e07">.&lt;/span>read&lt;span style="color:#719e07">(&lt;/span>buffer&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#719e07">-&lt;/span>1&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> out&lt;span style="color:#719e07">.&lt;/span>append&lt;span style="color:#719e07">(&lt;/span>buffer&lt;span style="color:#719e07">,&lt;/span> 0&lt;span style="color:#719e07">,&lt;/span> bytesRead&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> out&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>增加java测试代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Test&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">testHelloWordString&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>assertEquals&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;hello world&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">,&lt;/span> GoTestUtil&lt;span style="color:#719e07">.&lt;/span>readGoObject&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;HelloWorldString&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="15-support-javasqltime--javasqldate-219httpsgithubcomapachedubbo-go-hessian2pull219">1.5 support java.sql.Time &amp;amp; java.sql.Date. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/219">#219&lt;/a>&lt;/h3>
&lt;p>增加了 java 类 java.sql.Time, java.sql.Date 支持,分别对应到hessian.Time 和 hessian.Date, 详见 &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/219/files">https://github.com/apache/dubbo-go-hessian2/pull/219/files&lt;/a>。&lt;/p>
&lt;h2 id="2-enhancement">2. Enhancement&lt;/h2>
&lt;h3 id="21-export-function-encnull-225httpsgithubcomapachedubbo-go-hessian2pull225">2.1 Export function EncNull. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/225">#225&lt;/a>&lt;/h3>
&lt;p>开放 hessian.EncNull 方法,以便用户特定情况下使用。&lt;/p>
&lt;h2 id="3-bugfixes">3. Bugfixes&lt;/h2>
&lt;h3 id="31-fix-enum-encode-error-in-request-203httpsgithubcomapachedubbo-go-hessian2pull203">3.1 fix enum encode error in request. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/203">#203&lt;/a>&lt;/h3>
&lt;p>原来在 dubbo request 对象中没有判断 enum 类型的情况,此pr增加了判断是不是POJOEnum类型。详见 &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/203/files">https://github.com/apache/dubbo-go-hessian2/pull/203/files&lt;/a>&lt;/p>
&lt;h3 id="32-fix-byte-field-decoding-issue-216httpsgithubcomapachedubbo-go-hessian2pull216">3.2 fix []byte field decoding issue. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/216">#216&lt;/a>&lt;/h3>
&lt;p>v1.7.0 之前如果 struct中包含[]byte字段时无法反序列化, 报错“error list tag: 0x29”,主要原因是被当做list进行处理,对于这种情况应该按照binary数据进行处理即可。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> Circular &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Num &lt;span style="color:#dc322f">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Previous &lt;span style="color:#719e07">*&lt;/span>Circular
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Next &lt;span style="color:#719e07">*&lt;/span>Circular
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ResponseDataBytes []&lt;span style="color:#dc322f">byte&lt;/span> &lt;span style="color:#586e75">// &amp;lt;----
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (Circular) &lt;span style="color:#268bd2">JavaClassName&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.company.Circular&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="33-fix-decoding-error-for-map-in-map-229httpsgithubcomapachedubbo-go-hessian2pull229">3.3 fix decoding error for map in map. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/229">#229&lt;/a>&lt;/h3>
&lt;p>v1.7.0 之前嵌套map无法正确解析,主要原因是对应的map对象被当做一个数据类型却未被自动加到类引用列表中,而嵌套map类信息是同一类型的引用,去类引用列表找,找不到就报错了。 解决这个问题的方法就是遇到map类对象,也将其加入到类引用列表中即可。 问题详细参考 &lt;a href="https://github.com/apache/dubbo-go-hessian2/issues/119">#119&lt;/a>.&lt;/p>
&lt;h3 id="34-fix-fields-name-mismatch-in-duration-class-234httpsgithubcomapachedubbo-go-hessian2pull234">3.4 fix fields name mismatch in Duration class. &lt;a href="https://github.com/apache/dubbo-go-hessian2/pull/234">#234&lt;/a>&lt;/h3>
&lt;p>这个 PR 解决了Duration对象中字段错误定义,原来是&amp;quot;second/nano&amp;quot;, 应该是&amp;quot;seconds/nanos&amp;quot;。&lt;/p>
&lt;p>同时改善了测试验证数据。之前使用0作为int字段的测试数据,这是不准确的,因为int类型默认值就是0.&lt;/p></description></item><item><title>Blog: 安全漏洞</title><link>https://dubbo.apache.org/zh-cn/blog/1/01/01/%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/1/01/01/%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/</guid><description>
&lt;h2 id="1-log4j-cve-2021-44228-漏洞">1. Log4j CVE-2021-44228 漏洞&lt;/h2>
&lt;p>最近,主流日志组件 &lt;a href="https://logging.apache.org/log4j/2.x/">log4j2&lt;/a> 爆出&lt;a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228">安全漏洞 CVE-2021-44228&lt;/a>。&lt;/p>
&lt;p>以下是漏洞 CVE-2021-44228 对 Apache Dubbo 框架的影响总结及用户应对指南。&lt;/p>
&lt;h2 id="dubbo-影响范围">Dubbo 影响范围&lt;/h2>
&lt;p>&lt;strong>该漏洞对 Dubbo 框架使用安全并无影响。&lt;/strong>&lt;/p>
&lt;p>Dubbo 本身不强依赖 log4j2 框架,也不会通过依赖传递将 log4j2 带到业务工程中去,因此,正在使用 Dubbo 2.7.x、3.0.x 等版本的用户均无需强制升级 Dubbo 版本。&lt;/p>
&lt;p>以下是 Dubbo 各组件对 log4j2 的依赖分析,涉及 &lt;code>dubbo-common&lt;/code>、&lt;code>dubbo-spring-boot-starter&lt;/code>、&lt;code>dubbo-spring-boot-actuator&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>dubbo-common 包含对 &lt;code>log4j-core&lt;/code> 的可选依赖,请检查项目自身是否启用了 log4j 依赖,如启用则对应升级即可。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ dubbo-common ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] org.apache.dubbo:dubbo-common:jar:2.7.14-SNAPSHOT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] +- org.apache.logging.log4j:log4j-api:jar:2.11.1:provided
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.apache.logging.log4j:log4j-core:jar:2.11.1:provided
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>dubbo-spring-boot-starter 通过 spring-boot 组件传递了 log4j-api 依赖,log4j-api 本身并无安全问题,升级 log4j-core 组件时注意与 log4j-api 的兼容性&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ dubbo-spring-boot-starter ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] org.apache.dubbo:dubbo-spring-boot-starter:jar:2.7.14-SNAPSHOT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.springframework.boot:spring-boot-starter:jar:2.3.1.RELEASE:compile (optional)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.springframework.boot:spring-boot-starter-logging:jar:2.3.1.RELEASE:compile (optional)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile (optional)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.apache.logging.log4j:log4j-api:jar:2.13.3:compile (optional)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>dubbo-spring-boot-actuator 通过 spring-boot 组件传递了 log4j-api 依赖,log4j-api 本身并无安全问题,升级 log4j-core 组件时应注意与 log4j-api 的兼容性&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>[INFO] org.apache.dubbo:dubbo-spring-boot-actuator:jar:2.7.14-SNAPSHOT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.springframework.boot:spring-boot-starter-web:jar:2.3.1.RELEASE:compile (optional)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.springframework.boot:spring-boot-starter:jar:2.3.1.RELEASE:compile
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.springframework.boot:spring-boot-starter-logging:jar:2.3.1.RELEASE:compile
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[INFO] \- org.apache.logging.log4j:log4j-api:jar:2.13.3:compile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-序列化">2. 序列化&lt;/h2>
&lt;p>Dubbo 支持序列化协议的扩展,理论上用户可以基于该扩展机制启用任意的序列化协议,这带来了极大的灵活的,但同时也要意识到其中潜藏的安全性风险。
数据反序列化是最容易被被攻击者利用的一个环节,攻击者利用它执行 RCE 攻击等窃取或破坏服务端数据,用户在切换序列化协议或实现前,
应充分调研目标序列化协议及其框架实现的安全性保障,并提前设置相应的安全措施(如设置黑/白名单)。Dubbo 框架自身并不能保证目标序列化机制的安全性。&lt;/p>
&lt;p>Dubbo 2.7 官方版本提供的序列化协议有如下几种:&lt;/p>
&lt;ul>
&lt;li>Hessian2&lt;/li>
&lt;li>Fastjson&lt;/li>
&lt;li>Kryo&lt;/li>
&lt;li>FST&lt;/li>
&lt;li>JDK&lt;/li>
&lt;li>Protostuff/Protobuf&lt;/li>
&lt;li>Avro&lt;/li>
&lt;li>Gson&lt;/li>
&lt;/ul>
&lt;p>针对以上序列化扩展,在发现或收到相关的漏洞报告之后,Dubbo 官方会跟进并升级依赖到最新的安全版本,但最终的漏洞修复方案取决于序列化的框架实现。&lt;/p>
&lt;blockquote>
&lt;p>针对使用 &lt;a href="https://github.com/apache/dubbo-hessian-lite/releases">dubbo hessian2&lt;/a> 版本的用户,Dubbo 官方会保证hessian2序列化机制的安全性并尽可能的修复上报的安全漏洞&lt;/p>
&lt;/blockquote></description></item><item><title>Blog: Dubbo 3 中的三层配置隔离</title><link>https://dubbo.apache.org/zh-cn/blog/1/01/01/dubbo-3-%E4%B8%AD%E7%9A%84%E4%B8%89%E5%B1%82%E9%85%8D%E7%BD%AE%E9%9A%94%E7%A6%BB/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/1/01/01/dubbo-3-%E4%B8%AD%E7%9A%84%E4%B8%89%E5%B1%82%E9%85%8D%E7%BD%AE%E9%9A%94%E7%A6%BB/</guid><description>
&lt;h2 id="models提供的隔离">Models提供的隔离&lt;/h2>
&lt;p>Dubbo目前提供了三个级别上的隔离:JVM级别、应用级别、服务(模块)级别,从而实现各个级别上的生命周期及配置信息的单独管理。这三个层次上的隔离由 FrameworkModel、ApplicationModel 和 ModuleModel 及它们对应的 Config 来完成。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/models.png" alt="image">&lt;/p>
&lt;ul>
&lt;li>
&lt;p>FrameworkModel :Dubbo 框架的顶级模型,表示 Dubbo 框架的全局运行环境,适配多应用混合部署的场景,降低资源成本。&lt;/p>
&lt;p>如:假设我们有一个在线教育平台,平台下有多个租户,而我们希望使这些租户的服务部署在同一个 JVM 上以节省资源,但它们之间可能使用不同的注册中心、监控设施、协议等,因此我们可以为每个租户分配一个 FrameWorkModel 实例来实现这种隔离。&lt;/p>
&lt;p>FrameworkModel负责管理整个Dubbo框架的各种全局配置、元数据以及默认配置。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ApplicationModel :应用程序级别模型,表示一个 Dubbo 应用(通常为一个 SpringApplication)。适配单JVM多应用场景,通常结合热发布使用,降低热发布对整个应用的影响范围。&lt;/p>
&lt;p>如,以上的在线教育平台有多个子系统,如课程管理、学生管理,而每个子系统都是独立的 Spring 应用,在同个 JVM 中运行,共享一个 FrameworkModel,也会共享 Framework 级别的默认配置和资源:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//课程管理应用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ClassApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> ApplicationModel classAppModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classAppModel
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>addRegistry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>REGISTRY_URL_CLASS&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...其它设置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SpringApplication&lt;span style="color:#719e07">.&lt;/span>run&lt;span style="color:#719e07">(&lt;/span>ClassApplication&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setApplicationModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel classAppModel&lt;span style="color:#719e07">){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>classAppModel &lt;span style="color:#719e07">=&lt;/span> classAppModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//学生管理应用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@SpringBootApplication&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">StudentApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> ApplicationModel studentAppModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> studentAppModel
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>addRegistry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>REGISTRY_URL_STUDENT&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...其它设置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> SpringApplication&lt;span style="color:#719e07">.&lt;/span>run&lt;span style="color:#719e07">(&lt;/span>StudentApplication&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">,&lt;/span> args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setApplicationModel&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel studentAppModel&lt;span style="color:#719e07">){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>studentAppModel &lt;span style="color:#719e07">=&lt;/span> studentAppModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//单机发布多应用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MultiContextLauncher&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> FrameworkModel frameworkModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//使用同一个FrameworkModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ClassApplication&lt;span style="color:#719e07">.&lt;/span>setApplicationModel&lt;span style="color:#719e07">(&lt;/span>frameworkModel&lt;span style="color:#719e07">.&lt;/span>newApplication&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> StudenAppication&lt;span style="color:#719e07">.&lt;/span>setApplicationModel&lt;span style="color:#719e07">(&lt;/span>frameworkModel&lt;span style="color:#719e07">.&lt;/span>newApplication&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> SpringApplicationBuilder&lt;span style="color:#719e07">(&lt;/span>ClassApplication&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>run&lt;span style="color:#719e07">(&lt;/span>args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> SpringApplicationBuilder&lt;span style="color:#719e07">(&lt;/span>StudentApplication&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>run&lt;span style="color:#719e07">(&lt;/span>args&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由此,我们可以重新部署单个应用与其对应的ApplicationModel,实现热发布,而不会影响到另一个应用。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ModuleModel :模块级别模型,表示应用下的子模块。适配一个应用下的多个模块(容器)。&lt;/p>
&lt;p>如,上述的课程管理子系统内,新增课程和发布课程两个业务模块可能使用不同的spring容器。我们可以为每个spirng容器提供一个ModuleModel实例来管理、隔离模块级别的配置和资源:&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@ComponentScan&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.example.demo.class&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ClassManageConfig&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 课程管理模块配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@ComponentScan&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;com.example.demo.student&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ClassPublishConfig&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 课程发布模块配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">EducationPlatformApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FrameworkModel frameworkModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationModel applicationModel &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>newApplication&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建课程管理模块,使用自己的ApplicationContext与ModuleModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleModel classManageModuleModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>newModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext classManageContext &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>classManageModuleModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classManageContext&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ClassManagementConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classManageContext&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 创建学生管理模块,使用自己的ApplicationContext与ModuleModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleModel classPublishModuleModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>newModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext classPublishContext &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>classPublishModuleModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classPublishContext&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>ClassPublishConfig&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classPublishContext&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三层的模型设计让Dubbo在处理复杂业务场景时具有更强的适应性和可扩展性。&lt;/p>
&lt;p>​ 以下是一个以传统方式(而非 DubboBootstrap)启动 Dubbo 的 Demo,可以帮助我们了解 FrameworkModel、ApplicationModel 和 ModuleModel 之间的组织关系。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startWithExportNew&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> InterruptedException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建三个层级的Model
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> FrameworkModel frameworkModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> FrameworkModel&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationModel applicationModel &lt;span style="color:#719e07">=&lt;/span> frameworkModel&lt;span style="color:#719e07">.&lt;/span>newApplication&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ModuleModel moduleModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>newModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//提供配置中心、元数据中心、协议的配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> RegistryConfig registryConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>REGISTRY_URL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataReportConfig metadataReportConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetadataReportConfig&lt;span style="color:#719e07">(&lt;/span>METADATA_REPORT_URL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ProtocolConfig protocolConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>1&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果之后需要获取注册中心或元数据中心,需要设置id,之后通过它们的Id获取(适配多注册中心)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> String registryId &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;registry-1&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registryConfig&lt;span style="color:#719e07">.&lt;/span>setId&lt;span style="color:#719e07">(&lt;/span>registryId&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//Model的配置管理通过对应的ConfigManager进行
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ConfigManager appConfigManager &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> appConfigManager&lt;span style="color:#719e07">.&lt;/span>setApplication&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider-app-1&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//提供应用层级的配置中心、元数据中心、协议默认设置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> appConfigManager&lt;span style="color:#719e07">.&lt;/span>addRegistry&lt;span style="color:#719e07">(&lt;/span>registryConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> appConfigManager&lt;span style="color:#719e07">.&lt;/span>addMetadataReport&lt;span style="color:#719e07">(&lt;/span>metadataReportConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> appConfigManager&lt;span style="color:#719e07">.&lt;/span>addProtocol&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ModuleConfigManager moduleConfigManager &lt;span style="color:#719e07">=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getConfigManager&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> moduleConfigManager&lt;span style="color:#719e07">.&lt;/span>setModule&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ModuleConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-provider-app-1-module-1&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> serviceConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//设置该ServiceConfig对应的ModuleModel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>moduleModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>setRef&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> DemoServiceImpl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为ModuleModel添加ServiceConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> moduleConfigManager&lt;span style="color:#719e07">.&lt;/span>addConfig&lt;span style="color:#719e07">(&lt;/span>serviceConfig&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceConfig&lt;span style="color:#719e07">.&lt;/span>export&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>1&lt;span style="color:#719e07">).&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>ApplicationModel 由 FrameWorkModel 创建,ModuleModel 由 ApplicationModel 创建,由此完成它们之间层级关系的引用。&lt;/li>
&lt;li>ServiceConfig 和 ReferenceConfig 需要设置其所属的 ScopeModel(实际只能为 ModuleModel )。然后,还需将它们设置到对应的ModuleModel 中。&lt;/li>
&lt;/ul></description></item><item><title>Blog: CoC Asia 2023 大会精彩回顾</title><link>https://dubbo.apache.org/zh-cn/blog/2023/08/25/coc-asia-2023-%E5%A4%A7%E4%BC%9A%E7%B2%BE%E5%BD%A9%E5%9B%9E%E9%A1%BE/</link><pubDate>Fri, 25 Aug 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/08/25/coc-asia-2023-%E5%A4%A7%E4%BC%9A%E7%B2%BE%E5%BD%A9%E5%9B%9E%E9%A1%BE/</guid><description>
&lt;p>本期演讲的 ppt、视频录像、文章等内容,稍后将在官网发布,敬请期待!也可关注 &amp;ldquo;apachedubbo&amp;rdquo; 微信公众号了解动态。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/dubbo-members-justin.jpg" alt="dubbo-members-justin">&lt;/p>
&lt;p>Apache Dubbo 社区骨干成员及 Apache Board Member, Justin Mclean 合影&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/dubbo-members-apache-members.jpg" alt="dubbo-members-apache-members">&lt;/p>
&lt;p>Apache Dubbo 社区骨干与 Apache Member 交流合影&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/huazhongming.jpg" alt="huazhongming">&lt;/p>
&lt;p>华钟明,Apache Dubbo PMC Member,杭州有赞科技有限公司中间件技术专家,《Apache Dubbo 静态化 GraalVM Native Image 解决方案与实践》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/songxiaosheng.jpg" alt="songxiaosheng">&lt;/p>
&lt;p>宋小生,Apache Dubbo Committer,平安壹钱包中间件资深工程师,《Apache Dubbo 云原生可观测性探索与实践》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/chenyouwei.jpg" alt="chenyouwei">&lt;/p>
&lt;p>陈有为,Apache Dubbo PMC Member,陌陌研发工程师,《基于 Triple 协议实现WEB、移动端、后端服务全面打通》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/jiangheqing.jpg" alt="jiangheqing">&lt;/p>
&lt;p>江河清,Apache Dubbo PMC Member,阿里云研发工程师,《精进云原生 - Dubbo Kubernetes 最佳实践》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/wangxiaobin.jpg" alt="wangxiaobin">&lt;/p>
&lt;p>王晓彬,Apache Dubbo Committer,政采云资深开发工程师,《政采云基于 Dubbo 的混合云跨网方案实践》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/hejiahuan.jpg" alt="hejiahuan">&lt;/p>
&lt;p>何家欢,Sentinel Maintainer,阿里云研发工程师,《OPENSERGO &amp;amp; DUBBO 微服务治理最佳实践》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/dingxingzhong.jpg" alt="dingxingzhong">&lt;/p>
&lt;p>丁兴中,中国工商银行软件研发中心云计算实验室架构师,《工商银行分布式建设及转型实践》&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon-summary/jimin.jpg" alt="jimin">&lt;/p>
&lt;p>季敏,Seata 开源项目创始人,阿里云分布式事务产品负责人,《SEATA:微服务架构下的一站式分布式事务解决方案》&lt;/p></description></item><item><title>Blog: Dubbo 微服务专题论坛 - 8月19日北京ApacheCon大会不见不散</title><link>https://dubbo.apache.org/zh-cn/blog/2023/08/07/dubbo-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%B8%93%E9%A2%98%E8%AE%BA%E5%9D%9B-8%E6%9C%8819%E6%97%A5%E5%8C%97%E4%BA%ACapachecon%E5%A4%A7%E4%BC%9A%E4%B8%8D%E8%A7%81%E4%B8%8D%E6%95%A3/</link><pubDate>Mon, 07 Aug 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/08/07/dubbo-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%B8%93%E9%A2%98%E8%AE%BA%E5%9D%9B-8%E6%9C%8819%E6%97%A5%E5%8C%97%E4%BA%ACapachecon%E5%A4%A7%E4%BC%9A%E4%B8%8D%E8%A7%81%E4%B8%8D%E6%95%A3/</guid><description>
&lt;p>Dubbo 微服务专题论坛将于8月19日下午在北京丽亭华苑酒店举行,作为本次 CommunityOverCode Asia 2023(原 ApacheCon Asia)大会上的重磅议题,我们将以 Apache Dubbo 为中心展开,给大家带来开源微服务技术方向发展、云原生微服务选型、企业实践分享等精彩内容!&lt;/p>
&lt;p>打开 &lt;a href="https://www.bagevent.com/event/cocasia-2023">官网购票链接&lt;/a>,输入 cocasia 优惠码可八折购票!&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/8/apachecon/apachecon-rpc-schedule.png" alt="apachecon-rpc-schedule">&lt;/p>
&lt;p>作为 Apache 软件基金会(ASF)的官方全球系列大会,每年的 CommunityOverCode Asia 都吸引着来自全球各个层次的参与者、社区共同探索 &amp;ldquo;明天的技术&amp;rdquo;。8 月 18 日至 20 日,即将强势来袭的 CommunityOverCode Asia 2023 上,大家可以近距离感受来自 Apache 项目的最新发展和新兴创新。&lt;/p></description></item><item><title>Blog: GLCC x Apache Dubbo编程夏令营报名启动</title><link>https://dubbo.apache.org/zh-cn/blog/2023/07/05/glcc-x-apache-dubbo%E7%BC%96%E7%A8%8B%E5%A4%8F%E4%BB%A4%E8%90%A5%E6%8A%A5%E5%90%8D%E5%90%AF%E5%8A%A8/</link><pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/07/05/glcc-x-apache-dubbo%E7%BC%96%E7%A8%8B%E5%A4%8F%E4%BB%A4%E8%90%A5%E6%8A%A5%E5%90%8D%E5%90%AF%E5%8A%A8/</guid><description>
&lt;h2 id="背景介绍">背景介绍&lt;/h2>
&lt;p>GitLink编程夏令营(GLCC),是在CCF中国计算机学会指导下,由CCF开源发展委员会(CCF ODC)举办的面向全国高校学生的暑期编程活动。活动将覆盖近千所高校,并联合各大开源基金会、开源企业、开源社区、开源专家,旨在鼓励青年学生通过参加真实的开源软件开发,提升自身技术能力,为开源社区输送优秀人才。为青年学生提供开放友好的交流平台,希望进一步推动国内开源社区的繁荣发展。&lt;/p>
&lt;h2 id="报名方式">报名方式&lt;/h2>
&lt;p>此次报名仅限 GLCC “无奖金项目”。具体请在此查看 &lt;strong>&lt;a href="https://www.gitlink.org.cn/glcc/freeproject" target="_blank">项目详情与报名方式&lt;/a>&lt;/strong>。&lt;/p>
&lt;p>&lt;strong>Apache Dubbo 项目报名截止日期:2023-07-30&lt;/strong>&lt;/p>
&lt;p>如有更多问题,请扫描官方公众号咨询:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/contacts/wechat-account.jpg" alt="wechat-account">&lt;/p></description></item><item><title>Blog: 走向 Native 化:Spring&amp;Dubbo AOT 技术示例与原理讲解</title><link>https://dubbo.apache.org/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/06/28/%E8%B5%B0%E5%90%91-native-%E5%8C%96springdubbo-aot-%E6%8A%80%E6%9C%AF%E7%A4%BA%E4%BE%8B%E4%B8%8E%E5%8E%9F%E7%90%86%E8%AE%B2%E8%A7%A3/</guid><description>
&lt;p>Java 应用在云计算时代面临“冷启动”慢、内存占用高、预热时间长等问题,无法很好的适应 Serverless 等云上部署模式,GraalVM 通过静态编译、打包等技术在很大程度上解决了这些问题,同时针对 GraalVM 的一些使用限制,Spring 和 Dubbo 等主流框架也都提供了相应的 AOT 解决方案。&lt;/p>
&lt;p>本文我们将详细分析 Java 应用在云时代面临的挑战,GraalVM Native Image 是如何解决这些问题,GraalVM 的基本概念与工作原理,最后我们通过一个 Spring6 + Dubbo3 的微服务应用示例演示了如何将一个普通微服务应用进行静态化打包。&lt;/p>
&lt;p>本文主要分为以下四个部分展开&lt;/p>
&lt;ol>
&lt;li>首先我们会先看一下在云计算快速发展的当下,云上应用应该具备的特点,Java 应用在云上所面临的挑战有哪些。&lt;/li>
&lt;li>其次,我会介绍一下 GraalVM,什么是 Native Image,如何通过 GraalVM 对 Java 应用进行静态化打出 Native Image 可执行的二进制程序。&lt;/li>
&lt;li>第三部分,我们知道 GraalVM 的使用是有一定限制的,比如 Java 的反射等动态特性是不被支持的,因此我们需要提供特殊的 Metadata 配置来绕过这些限制,在这一部分我们会讲解如何加入引入 AOT Processing 来实现自动化的 Metadata 配置,包括 Spring6 框架中 AOT 处理、Dubbo3 框架的 AOT 处理等。&lt;/li>
&lt;li>最后,我们将通过一个 Spring6+Dubbo3 的应用示例,来演示如何将这么一个 Java 应用进行静态化打包。&lt;/li>
&lt;/ol>
&lt;h2 id="java-应用在云时代所面临的挑战">Java 应用在云时代所面临的挑战&lt;/h2>
&lt;p>首先,我们先看一下云计算时代的应用特点,以及 Java 在云时代所面临的挑战。从各个统计机构给出的数据来看,Java 语言仍然是当今最受开发者欢迎的编程语言之一,仅次于一些脚本开发语言。使用 Java 语言可以非常高效的开发业务应用,丰富的生态使得 Java 具有非常高的开发和运行效率,有无数的应用基于 Java 语言开发。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/language-rank.png" alt="image.png">&lt;/p>
&lt;p>但在来到云计算时代,Java 应用的部署和运行开始面临非常多的问题。我们以Serverless为例,Serverless是云上的一种越来越主流的部署模式,它让开发者更专注业务逻辑、通过快速弹性等帮助解决资源问题,根据最新的一些数据,Java在所有云计算厂商的 Serverless 运行时中所占比例并不高,远远不能和它在传统应用开发中所占的比例相匹配。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/serverless-lang-rank.png" alt="image.png">&lt;/p>
&lt;p>出现这样的原因,主要是Java应用不能很好的满足Serverless场景的几个关键要求。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>首先是启动速度问题,Java 冷启动的耗时是比较长的&lt;/strong>。这对于Serverless需要快速弹起的场景是一个非常大的挑战,因为 Java 应用的拉起时间可能是秒、数十秒级别的;&lt;/li>
&lt;li>&lt;strong>第二点,Java应用往往都需要一定的预热时间,才能达到最佳的性能状态&lt;/strong>,刚刚拉起的应用如果分配比较大的流量是不合适的,往往会出现请求超时、资源占用过高等问题,这就进一步拉长了 Java 应用的有效拉起时间;&lt;/li>
&lt;li>&lt;strong>第三点是 Java 应用对运行环境的要求,它往往需要较大的内存、计算资源&lt;/strong>,而这些真正分配给业务自身的并不多,都消耗在一些JVM运行时上,这与用云降本提效的目标并补匹配;&lt;/li>
&lt;li>&lt;strong>最后,Java应用打出来的包或者镜像也是非常大&lt;/strong>,从总体上也影响存储、拉取的效率。&lt;/li>
&lt;/ul>
&lt;p>接下来,我们具体看一下针对 Java 应用所面临的这些问题, GraalVM 这样一种打包和运行时技术是如何解决的。&lt;/p>
&lt;h2 id="graalvm-简介">GraalVM 简介&lt;/h2>
&lt;blockquote>
&lt;p>GraalVM compiles your Java applications ahead of time into standalone binaries that start instantly, provide peak performance with no warmup, and use fewer resources.&lt;/p>
&lt;/blockquote>
&lt;p>引用官方介绍来看,GraalVM 为 Java 应用提供 AOT 编译和二进制打包能力,基于 GraalVM 打出的二进制包可以实现快速启动、具有超高性能、无需预热时间、同时需要非常少的资源消耗。这里所说的 AOT 是发生在编译期间的一个技术简称,即 Ahead-of-time,这一点我们后续会讲到。总的来说 GraalVM 可以分为两部分内容来看&lt;/p>
&lt;ul>
&lt;li>首先,&lt;strong>GraalVM 是一个完整的 JDK 发行版本&lt;/strong>,从这一点它是与 OpenJDK 对等的,可以运行任何面向jvm的语言开发的应用;&lt;/li>
&lt;li>其次,&lt;strong>GraalVM提供了 Native Image 打包技术&lt;/strong>,这可以将应用打包为可以独立运行的二进制包,这个包是自包含的、可脱离 JVM 运行的应用程序。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-compilation.png" alt="image.png">&lt;/p>
&lt;p>如上图所示,GraalVM 编译器提供了 JIT 和 AOT 两种模式。&lt;/p>
&lt;ul>
&lt;li>对于 JIT 而言,我们都知道Java类会被编译为 .class 格式的文件,这里编译后就是 jvm 识别的字节码,在 Java 应用运行的过程中,而 JIT 编译器又将一些热点路径上的字节码编译为机器码,已实现更快的执行速度;&lt;/li>
&lt;li>对于 AOT 模式来说,它直接在编译期间就将字节码转换为机器码,直接省去了运行时对jvm的依赖,由于省去了 jvm 加载和字节码运行期预热的时间,AOT 编译和打包的程序具有非常高的运行时效率。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-compilation2.png" alt="image.png">&lt;/p>
&lt;p>总的来说,JIT 使得应用可以具备更高的极限处理能力,可以降低请求的最大延迟这一关键指标;而 AOT 则可以进一步的提升应用的冷启动速度、具有更小的二进制包提及、在运行态需要更少的内存等资源。&lt;/p>
&lt;h2 id="什么是-native-image">什么是 Native Image?&lt;/h2>
&lt;p>我们上面多次提到 GraalVM 中 Native Image 概念,Native Image 是一项将 Java 代码编译打包为可执行二进制程序的技术,打出的包中仅包含运行期所需要的代码,包括应用自身代码、标准依赖包、 语言运行时、JDK 库关联的静态代码。这个包的运行不再需要 jvm 环境,当然它是和具体的机器环境相绑定的,需要为不同的机器环境单独打包。 Native Image 有这里列出来的一系列特点:&lt;/p>
&lt;ul>
&lt;li>仅包含 JVM 运行所需的一部分资源,运行成本更低&lt;/li>
&lt;li>毫秒级的启动时间&lt;/li>
&lt;li>启动后即进入最佳状态,无需预热&lt;/li>
&lt;li>可打包为更轻量的二进制包,让部署速度更快更高效&lt;/li>
&lt;li>安全程度更高&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-advantages.png" alt="image.png">&lt;/p>
&lt;p>总结起来就是这里的关键几项:更快的启动个速度、更少的资源占用、更小的安全漏洞风险、更紧凑的二进制包体积。解决 Java 应用在 Sererless 等云计算应用场景中面临的突出问题。&lt;/p>
&lt;h2 id="graalvm-native-image-的基本原理与使用">GraalVM Native Image 的基本原理与使用&lt;/h2>
&lt;p>接下来,我们看一下 GraalVM 的基本使用方式,首先,需要安装 native-image 需要的相关基础依赖,根据不同的操作系统环境会有所差异,接下来可以使用 GraalVM JDK 下载器下载 native-image。都安装好之后,第二步,就可以使用 native-image 命令编译和打包 Java 应用了,输入可以是 class 文件、jar文件、Java模块等,最终打包为一个可独立运行的可执行文件,比如这里的 HelloWorld。另外,GraalVM 也提供了对应的 Maven和Gradle构建工具插件,让打包过程更容易。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/graalvm-principal.png" alt="image.png">&lt;/p>
&lt;p>GraalVM 基于叫做 “closed world assumption” 即封闭世界假设的概念,要求在编译期间程序的所有运行时资源和行为即能被完全确定下来。图中是具体的 AOT 编译和打包过程,左侧应用代码、仓库、jdk等全部作为输入,GraalVM以 main 为入口,扫描所有可触达的代码与执行路径,在处理过程中可能会涉及到一些前置初始化动作,最终 AOT 编译的机器码和一些初始化资源等状态数据,被打包为可执行的 Native 包。&lt;/p>
&lt;p>相比于传统的 JVM 部署模式,GraalVM Native Image 模式带来的非常大的不同。&lt;/p>
&lt;ul>
&lt;li>GraalVM 在编译构建期间就会以 main 函数为入口,完成对应用代码的静态分析&lt;/li>
&lt;li>在静态分析期间无法被触达的代码,将会被移除,不会包含在最终的二进制包中&lt;/li>
&lt;li>GraalVM 无法识别代码中的一些动态调用行为,如反射、resource资源加载、序列化、动态代理等都动态行为都将受限&lt;/li>
&lt;li>Classpath 在构建阶段就固化下来,无法修改&lt;/li>
&lt;li>不再支持延迟的类加载,所有可用类和代码在程序启动阶段就确定了&lt;/li>
&lt;li>还有一些其他的 Java 应用能力是受限使用的(比如类初始化提前等)&lt;/li>
&lt;/ul>
&lt;p>GraalVM 不支持反射等动态特性,而我们的很多应用和框架中却大量使用了反射、动态代理等特性,如何才能将这些应用打包为 Native Image 实现静态化那? GraalVM 提供了元数据配置入口,通过为所有动态特性提供配置文件,“closed world assumption” 模式还是成立的,可以让 GraalVM 在编译期知道所有的预期行为。这里给了两个例子:&lt;/p>
&lt;ol>
&lt;li>编码方式上,比如这里反射的编码方式,可以让 GraalVM 通过代码分析计算 Metadata&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-1.png" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-2.png" alt="image.png">&lt;/p>
&lt;ol start="2">
&lt;li>另一个示例是提供额外的 json 配置文件并放在指定的目录 META-INF/native-image/&amp;lt;group.id&amp;gt;/&amp;lt;artifact.id&amp;gt; 下。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/metadata-3.png" alt="image.png">&lt;/p>
&lt;h2 id="aot-processing">AOT Processing&lt;/h2>
&lt;p>Java 应用或框架中的反射等动态特性的使用是影响 GraalVM 使用的障碍,而大量的框架都存在这个限制,如果都要求应用或者开发者提供 Metadata 配置的话将会是一项非常有挑战的任务,因此,Spring 和 Dubbo 等框架都在 AOT Compilation 即 AOT 编译之前引入了 AOT Processing 即 AOT 预处理的过程,AOT Processing 用来完成自动化的 Metadata 采集,并将 Metadata 提供给 AOT 编译器使用。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/aot.png" alt="image.png">&lt;/p>
&lt;p>AOT 编译机制是对所有 Java 应用通用的,但相比于 AOT 编译,AOT Processing 采集 Metadata 的过程是每个框架都不同的,因为每个框架对于反射、动态代理等都有自己的用法。
我们以一个典型的 Spring + Dubbo 的微服务应用为例,要实现这个应用的静态化打包,这里涉及到 Spring、Dubbo 以及一众第三方依赖的 Metadata 处理过程。&lt;/p>
&lt;ul>
&lt;li>Spring - Spring AOT processing&lt;/li>
&lt;li>Dubbo - Dubbo AOT processing&lt;/li>
&lt;li>Third-party libraries - Reachability Metadata&lt;/li>
&lt;/ul>
&lt;p>对于 Spring 来说,Spring6 中发布了 Spring AOT 机制,用来支持 Spring 应用的静态化预处理;Dubbo 最近也在 3.2 版本中发布了 Dubbo AOT 机制,让 Dubbo 相关组件可以自动化实现 Native 预处理;除了这两个与业务开发密切相关的框架,一个应用中往往还有大量的第三方依赖,这些依赖的 Metadata 也是影响静态化的关键,如果它们中有反射、类加载等行为,那么需要为它们提供 Metadata 配置,对于这些第三方应用目前有两个渠道,一个是 GraalVM 官方提供的共享空间,这里有相当一部分依赖的 Metadata 配置可供使用(https://github.com/oracle/graalvm-reachability-metadata),另一种方式则是要求组件官方发布的发布中包含 Metadata 配置,对于这两种情况 GraalVM 都可以做到对于 Metadata 的自动读取。&lt;/p>
&lt;h3 id="spring-aot">Spring AOT&lt;/h3>
&lt;p>接下来我们看一下 Spring AOT 做了哪些编译之前的预处理工作,Spring 框架中有非常多的动态特性,比如自动配置、条件 Bean 等特性。Spring AOT 就是针对针对这些动态特性,在构建阶段进行预处理,生成可供 GraalVM 使用的一系列 Metadata 输入,这里生成的内容包括:&lt;/p>
&lt;ul>
&lt;li>Spring Bean 定义相关的代码预生成,如下图展示代码段&lt;/li>
&lt;li>在构建阶段生成动态代理相关代码&lt;/li>
&lt;li>关于一些反射等使用的 JSON 元数据文件&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/spring-aot.png" alt="image.png">&lt;/p>
&lt;h3 id="dubbo-aot">Dubbo AOT&lt;/h3>
&lt;p>Dubbo AOT 做的事情与 Spring AOT 非常类似,只不过 Dubbo AOT 是专门针对 Dubbo 框架特有的使用方式进行预处理,这包括:&lt;/p>
&lt;ul>
&lt;li>SPI 扩展相关的源代码生成&lt;/li>
&lt;li>一些反射使用的 JSON 配置文件生成&lt;/li>
&lt;li>RPC 代理类代码生成&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/dubbo-aot-1.png" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/dubbo-aot-2.png" alt="image.png">&lt;/p>
&lt;h2 id="spring6--dubbo3-示例演示">Spring6 + Dubbo3 示例演示&lt;/h2>
&lt;p>接下来,我们通过一个 Spring6 + Dubbo3 的示例微服务应用,演示如何使用 Spring AOT、Dubbo AOT 等,来实现应用的 Native Image 打包。&lt;/p>
&lt;p>完整的代码示例可在这里下载:&lt;a href="https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image">dubbo-samples-native-image&lt;/a>&lt;/p>
&lt;h3 id="第一步安装graalvm">第一步:安装GraalVM&lt;/h3>
&lt;ol>
&lt;li>在Graalvm官网根据自己的系统选取对应Graalvm版本:&lt;a href="https://www.graalvm.org/downloads/">https://www.graalvm.org/downloads/&lt;/a>&lt;/li>
&lt;li>根据官方文档安装 native-image:&lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image">Getting Started with Native Image&lt;/a>&lt;/li>
&lt;/ol>
&lt;h3 id="第二步创建项目">第二步:创建项目&lt;/h3>
&lt;p>这个示例应用就是普通的、常见的微服务应用,我们使用 SpringBoot3 进行应用配置开发,使用 Dubbo3 定义并发布 RPC 服务;应用构建工具使用 Maven。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/demo-1.png" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/6/graalvm/demo-2.png" alt="image.png">&lt;/p>
&lt;h3 id="第三步配置-maven-插件">第三步:配置 Maven 插件&lt;/h3>
&lt;p>重点是增加 spring-boot-maven-plugin、native-maven-plugin、dubbo-maven-plugin 三个插件配置,开启 AOT 处理过程,修改dubbo-maven-plugin中的mainClass为所需的启动类全路径。(其中API使用方式无需添加spring-boot-maven-plugin依赖。)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;profiles&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;profile&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>native&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>maven-compiler-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;release&amp;gt;&lt;/span>17&lt;span style="color:#268bd2">&amp;lt;/release&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;fork&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/fork&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;verbose&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/verbose&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>process-aot&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>process-aot&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.graalvm.buildtools&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>native-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>0.9.20&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;classesDirectory&amp;gt;&lt;/span>${project.build.outputDirectory}&lt;span style="color:#268bd2">&amp;lt;/classesDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;metadataRepository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/metadataRepository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;requiredVersion&amp;gt;&lt;/span>22.3&lt;span style="color:#268bd2">&amp;lt;/requiredVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;id&amp;gt;&lt;/span>add-reachability-metadata&lt;span style="color:#268bd2">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>add-reachability-metadata&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${dubbo.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;mainClass&amp;gt;&lt;/span>com.example.nativedemo.NativeDemoApplication&lt;span style="color:#268bd2">&amp;lt;/mainClass&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;phase&amp;gt;&lt;/span>process-sources&lt;span style="color:#268bd2">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>dubbo-process-aot&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/profile&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/profiles&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第四步在pom依赖中添加native相关的依赖">第四步:在Pom依赖中添加native相关的依赖&lt;/h3>
&lt;p>另外,对于 Dubbo 而言,由于当前一些 Native 机制依赖 JDK17 等版本,Dubbo 没有将一些包默认打包到发行版本中,因此需要增加两个额外的依赖 dubbo-spring6 适配和 dubbo-native 组件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-config-spring6&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${dubbo.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-native&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${dubbo.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第五步调整compilerproxyserialization和logger">第五步:调整compiler、proxy、serialization和logger&lt;/h3>
&lt;p>同时,这个示例对于第三方组件的支持目前也是受限的,主要是第三方组件的 Reachability Metadata 。比如目前支持的网络通信或编码组件有 Netty 和 Fastjson2;支持的日志等组件为 Logback;微服务组件有 Nacos、Zookeeper 等。&lt;/p>
&lt;ul>
&lt;li>序列化方式目前支持的比较好的是Fastjson2&lt;/li>
&lt;li>compiler、proxy目前只能选择jdk&lt;/li>
&lt;li>logger目前需要配置slf4j,目前仅支持logback&lt;/li>
&lt;/ul>
&lt;p>示例配置如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">application&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: ${spring.application.name}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">logger&lt;/span>: slf4j
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: -&lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serialization&lt;/span>: fastjson2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">id&lt;/span>: zk-registry
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">config-center&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">metadata-report&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: zookeeper://127.0.0.1:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">provider&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serialization&lt;/span>: fastjson2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">consumer&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">serialization&lt;/span>: fastjson2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第六步编译">第六步:编译&lt;/h3>
&lt;p>在项目根路径下执行以下编译命令:&lt;/p>
&lt;ul>
&lt;li>API方式直接执行&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span> mvn clean install -P native -Dmaven.test.skip=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>注解和xml方式(Springboot3集成的方式)&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span> mvn clean install -P native native:compile -Dmaven.test.skip&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#b58900">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第七步执行二进制文件即可">第七步:执行二进制文件即可&lt;/h3>
&lt;p>二进制文件在 target/ 目录下,一般以工程名称为二进制包的名称,比如 target/native-demo&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>GraalVM 技术为 Java 在云计算时代的应用带来了新的变革,帮助解决了 Java 应用启动慢、资源占用,但同时我们也看到了 GraalVM 的使用也存在一些限制,因此 Spring6、SpringBoot3、Dubbo3 都提供了相应的 Native 解决方案。 Apache Dubbo 社区接下来将在周边生态组件等推进整体的 Native 静态化。&lt;/p></description></item><item><title>Blog: [Google Paper] 面向云时代的应用开发新模式</title><link>https://dubbo.apache.org/zh-cn/blog/2023/05/26/google-paper-%E9%9D%A2%E5%90%91%E4%BA%91%E6%97%B6%E4%BB%A3%E7%9A%84%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%96%B0%E6%A8%A1%E5%BC%8F/</link><pubDate>Fri, 26 May 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/05/26/google-paper-%E9%9D%A2%E5%90%91%E4%BA%91%E6%97%B6%E4%BB%A3%E7%9A%84%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%96%B0%E6%A8%A1%E5%BC%8F/</guid><description>
&lt;blockquote>
&lt;p>本文翻译自发表在以下地址的论文:https://serviceweaver.dev/assets/docs/hotos23_vision_paper.pdf&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>原文作者(Authors): Sanjay Ghemawat, Robert Grandl, Srdjan Petrovic, Michael Whittaker, Parveen Patel, Ivan Posva, Amin Vahdat&lt;/p>
&lt;p>转载或发布请遵循原文许可:
Permission to make digital or hard copies of part or all of this work for
personal or classroom use is granted without fee provided that copies are
not made or distributed for profit or commercial advantage and that copies
bear this notice and the full citation on the first page. Copyrights for thirdparty components of this work must be honored. For all other uses, contact
the owner/author(s).
HOTOS ’23, June 22–24, 2023, Providence, RI, USA
© 2023 Copyright held by the owner/author(s).
ACM ISBN 979-8-4007-0195-5/23/06.
&lt;a href="https://doi.org/10.1145/3593856.3595909">https://doi.org/10.1145/3593856.3595909&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 id="摘要">摘要&lt;/h2>
&lt;p>在编写分布式应用程序时,传统的明智做法是将您的应用程序拆分为可以分别拉起的独立服务。这种方式的用意是好的,但像这样基于微服务的架构经常会适得其反,带来的挑战抵消了架构试图实现的好处。从根本上说,这是因为微服务将逻辑边界(代码的编写方式)与物理边界(代码的部署方式)混为一谈。在本文中,我们提出了一种不同的编程方法,将两者(代码编写与部署方式)分离以解决这些挑战。通过我们的方法,开发人员将他们的应用程序编写为逻辑上的单体,将有关如何分发和运行应用程序的决策放到一套自动化运行时 (runtime),并以原子方式部署应用程序。与当前的微服务开发模式相比,我们的原型应用最多可减少延迟 15 倍、成本最多减少了 9 倍。&lt;/p>
&lt;p>ACM 参考格式:
Sanjay Ghemawat, Robert Grandl, Srdjan Petrovic, Michael Whit-taker, Parveen Patel, Ivan Posva, Amin Vahdat. 2023. Towards Mod-ern Development of Cloud Applications. In Workshop on Hot Topics in Operating Systems (HOTOS ’23), June 22–24, 2023, Providence, RI, USA. ACM, New York, NY, USA, 8 pages. &lt;a href="https://doi.org/10.1145/3593856.3595909">https://doi.org/10.1145/3593856.3595909&lt;/a>&lt;/p>
&lt;h2 id="1-介绍">1 介绍&lt;/h2>
&lt;p>近年来,云计算出现了前所未有的增长。编写和部署可扩展到数百万用户的分布式应用程序从未如此简单,这在很大程度上归功于 Kubernetes [25] 等框架,[7, 18, 31, 33, 40, 60] 等消息传递解决方案,以及数据格式如 [5,6, 23, 30]。使用这些技术时,普遍的做法是手动将您的应用程序拆分为可以独立部署的独立微服务。
通过对各种基础设施团队的内部调查,我们发现大多数开发人员出于以下原因之一将他们的应用程序拆分为多个二进制包:(1) 提升性能。单独的二进制包可以独立扩展,从而提高资源利用率。 (2) 提升容错能力。一个微服务的崩溃不会导致其他微服务崩溃,从而限制了错误的传播范围。 (3) 改进抽象边界。微服务需要清晰明确的 API,并且代码纠缠的可能性会大大降低。 (4) 允许灵活的滚动发布。不同的二进制包可以以不同的速率发布,从而导致更敏捷的代码升级。
然而,将应用程序拆分为可独立部署的微服务并非没有挑战,其中一些直接与收益相矛盾。&lt;/p>
&lt;ul>
&lt;li>C1:影响性能。序列化数据并通过网络发送数据的开销越来越成为瓶颈 [72]。当开发人员过度拆分他们的应用程序时,这些开销也会增加 [55]。&lt;/li>
&lt;li>C2:损害正确性。推断每个微服务的每个已部署版本之间的交互是极具挑战性的。在对八个广泛使用的系统的 100 多个灾难性故障进行的案例研究中,三分之二的故障是由系统的多个版本之间的交互引起的 [78]。&lt;/li>
&lt;li>C3:很难管理。开发人员必须按照自己的发布计划管理不同的二进制包,而不是使用一个二进制文件来构建、测试和部署。如果在本地运行一个应用程序,同时需要执行端到端的集成测试,那可是一个不小的工程。&lt;/li>
&lt;li>C4:API 冻结。一旦微服务建立了 API,就很难在不破坏使用该 API 的其他服务的情况下进行更改。遗留的 API 不得不长期存在,只能不停的在上面打补丁。&lt;/li>
&lt;li>C5:降低应用程序的开发速度。当开发活动影响多个微服务的更改时,开发人员无法以原子方式实施和部署更改。&lt;/li>
&lt;/ul>
&lt;p>开发人员必须仔细计划规划发布时间表,已决定在何时跨微服务引入更改。根据我们的经验,我们发现许多开发人员将上述挑战视为开展业务的必要部分,并且这个比例是压倒性的。许多云原生公司实际上正在开发旨在缓解上述一些挑战的内部框架和流程,但这不会从根本上改变或完全消除它们。例如,&lt;/p>
&lt;p>持续部署框架 [12, 22, 37] 简化了单个二进制包的构建、推送到生产环境的方式,但它们没有解决版本控制问题;如果它有提供这个能力的话,情况可能会更糟,因为代码将以更快的速度被发布并投入生产。各种编程库 [13、27] 使创建和发现网络端点变得更加容易,但对简化应用程序管理没有任何帮助。像 gRPC [18] 这样的网络协议和像 Protocol Buffers [30]这样的数据格式在不断改进,但仍然占据了应用程序执行成本的主要部分。&lt;/p>
&lt;p>这些基于微服务的解决方案无法解决上述 C1-C5 的原因有两个。第一个原因是他们都假设开发人员手动将他们的应用程序拆分为多个二进制包。这意味着应用程序的网络布局由应用程序开发人员预先确定。此外,一旦确定,网络布局就会通过将网络代码添加到应用程序中而变得更加坚固(例如,网络端点、客户端/服务器存根、网络优化数据结构,如 [30] )。这意味着撤消或修改拆分变得更加困难,即使这样做是有意义的。这隐含地促成了上述挑战 C1、C2 和 C4。&lt;/p>
&lt;p>第二个原因是假设应用程序二进制包是单独(在某些情况下是连续的)发布到生产环境中。这使得对跨二进制协议进行更改变得更加困难。此外,它还引入了版本控制问题并强制使用更低效的数据格式,如[23、30]。这反过来又会导致上面列出的挑战 C1-C5。&lt;/p>
&lt;p>在本文中,我们提出了一种不同的编写和部署分布式应用程序的方法,一种解决 C1-C5 问题的方法。我们的编程方法包括三个核心原则:&lt;/p>
&lt;ul>
&lt;li>(1) 以模块化的方式编写逻辑上划分为多个组件的单体应用程序。&lt;/li>
&lt;li>(2) 利用运行时根据执行特征动态自动地将逻辑组件分配给物理进程。&lt;/li>
&lt;li>(3) 以原子方式部署应用程序,防止应用程序的不同版本交互。&lt;/li>
&lt;/ul>
&lt;p>其他解决方案(例如 actor 系统)也尝试提高抽象度。但是,它们无法解决其中一项或多项挑战(第 7 节)。尽管这些挑战和我们的提案是在服务型应用 (serving application) 的背景下讨论的,但我们相信我们的观察和解决方案具有广泛的用途。&lt;/p>
&lt;h2 id="2-提出的解决方案">2 提出的解决方案&lt;/h2>
&lt;p>我们提案的两个主要部分是 (1) 具有抽象的编程模型(programing model),允许开发人员编写仅关注业务逻辑的单一二进制模块化应用程序,(2) 用于构建、部署和优化这些应用程序的运行时(runtime)。&lt;/p>
&lt;p>编程模型使开发人员能够将分布式应用程序编写为单个程序,其中代码被拆分为称为组件的模块化单元(第 3 节)。这类似于将应用程序拆分为微服务,除了微服务将逻辑和物理边界混为一谈。相反,我们的解决方案将两者分离:组件以基于应用程序业务逻辑的逻辑边界为中心,而运行时以基于应用程序性能的物理边界为中心(例如,两个组件应位于同一位置以提高性能)。这种解耦——连同边界可以自动更改的事实——解决了 C4。&lt;/p>
&lt;p>通过将所有执行责任委托给运行时,我们的解决方案能够提供与微服务相同的优势,但性能更高,成本更低(解决 C1)。例如,运行时决定如何运行、放置、复制和缩放组件(第 4 节)。由于应用程序是原子部署的,因此运行时可以鸟瞰应用程序的执行情况,从而实现进一步的优化。例如,运行时可以使用自定义序列化和传输协议,利用所有参与者都以相同版本执行的事实。&lt;/p>
&lt;p>将应用程序编写为单个二进制文件并以原子方式部署它还可以更轻松地推断其正确性(解决 C2)并使应用程序更易于管理(解决 C3)。我们的提案为开发人员提供了一个编程模型,使他们能够专注于应用程序业务逻辑,将部署复杂性委托给运行时(解决 C5)。最后,我们的提案支持未来的创新,例如分布式应用程序的自动化测试(第 5 节)。&lt;/p>
&lt;h2 id="3-编程模型">3 编程模型&lt;/h2>
&lt;h3 id="31-组件">3.1 组件&lt;/h3>
&lt;p>我们提案的关键抽象是组件 (component)。组件是一种长期存在的、可复制的计算代理,类似于 actor [2]。每个组件都实现一个接口(interface),与组件交互的唯一方法是调用其接口上的方法。组件可能由不同的操作系统进程托管(可能跨越多台机器)。组件方法调用在必要时变成远程过程调用,但如果调用者和被调用者组件在同一个进程中,则仍然是本地过程调用。&lt;/p>
&lt;p>组件如 图-1 所示。示例应用程序包含三个组件:A、B 和 C。当部署应用程序时,运行时决定如何共同定位和复制组件。在此示例中,组件 A 和组件 B 位于同一个操作系统进程中,因此它们之间的方法调用作为常规方法调用执行。组件 C 不与任何其他组件位于同一位置,同时组件 C 被部署到了两台不同机器上,组件 C 之间的方法调用是通过跨网络 RPC 完成的。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/5/pic1-arch.png" alt="pic1-arch.png">&lt;/p>
&lt;p>图-1:说明如何编写和部署组件。应用程序被编写为一组组件(左)并跨机器部署(右)。请注意,组件可以复制和放置在同一位置。&lt;/p>
&lt;p>组件通常是长期存在的,但运行时可能会根据负载随时间增加或减少组件的副本数量。同样,组件副本可能会失败并重新启动。运行时还可以四处移动组件副本,例如,将两个交互非常多的组件放在同一个操作系统进程中,以便组件之间的通信在本地而不是通过网络完成。&lt;/p>
&lt;h3 id="32--接口">3.2 接口&lt;/h3>
&lt;p>为了具体起见,我们在 Go 中提供了一个组件 API,尽管我们的想法与语言无关。图-2 给出了一个“Hello, World!” 应用程序。组件接口表示为 Go 接口,组件实现表示为实现这些接口的 Go 结构。在图-2 中, &lt;code>hello&lt;/code> 结构嵌入了 **Implements[ Hello] **结构来表示它是 &lt;code>Hello&lt;/code> 组件的实现。&lt;/p>
&lt;p>**Init **初始化应用程序。**Get[Hello] **将客户端返回给具有接口 &lt;code>Hello&lt;/code> 的组件,必要时创建它。对 &lt;code>hello.Greet&lt;/code> 的调用看起来像是常规方法调用,开发人员不需要关心任何序列化和远程过程调用相关内容。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/5/pic2-hello-world.png" alt="image.png">&lt;/p>
&lt;p>图-2: “Hello, World!” 应用&lt;/p>
&lt;h2 id="4---运行时">4 运行时&lt;/h2>
&lt;h3 id="41--概述">4.1 概述&lt;/h3>
&lt;p>在编程模型之下是一个负责分发(distributing)和执行(executing)组件的运行时。运行时做出关于如何运行组件的所有高级决策。例如,它决定将哪些组件放在一起并进行多副本部署。运行时还负责底层细节,例如将组件运行到物理资源以及在组件失败时重新启动组件。最后,运行时负责执行原子滚动更新,确保一个应用程序版本中的组件永远不会与不同版本中的组件进行通信。&lt;/p>
&lt;p>有许多方法可以实现运行时。本文的目的不是规定任何特定的实现。不过,重要的是要认识到运行时并没有什么神奇魔法。在本节的其余部分,我们将概述运行时的关键部分并揭开其内部工作原理的神秘面纱。&lt;/p>
&lt;h3 id="42---代码生成">4.2 代码生成&lt;/h3>
&lt;p>运行时的首要职责是代码生成。通过检查一个项目中使用 **Implements[T] **的相关源码调用,代码生成器即可计算出所有组件接口和实现的集合。然后它生成代码来编码和解码组件方法的参数。它还生成代码以将这些方法作为远程过程调用来执行。生成的代码将与开发人员的代码一起编译成一个二进制文件。&lt;/p>
&lt;h3 id="43---应用-运行时交互">4.3 应用-运行时交互&lt;/h3>
&lt;p>根据我们的提案,应用程序不需要包含任何特定于其部署环境的代码,但由于它们最终必须运行并集成到特定环境中(例如在本地集群中跨机器或在公共云中跨区域运行),为了支持这种集成,我们引入了一个 API(在表-1 中进行了部分概述),它将应用程序逻辑与部署环境的细节隔离开来。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/5/table1.png" alt="table1.png">&lt;/p>
&lt;p>表-1:应用程序和运行时之间的示例 API。&lt;/p>
&lt;p>API 的调用者是一个 &lt;code>proclet&lt;/code>。每个应用程序二进制文件都会运行一个小型的、与环境无关的守护进程,称为 &lt;code>proclet&lt;/code>,它在编译期间链接到二进制文件中。 proclet 管理正在运行的二进制文件中的组件:运行、启动、停止、在失败时重新启动这些组件等等。
API 的实现者是运行时,它负责所有控制平面操作。运行时决定 &lt;code>proclet&lt;/code> 应该如何运行以及在何处运行。例如,多进程运行时可以运行子进程中的每个 &lt;code>proclet&lt;/code>; SSH 运行时可以通过 SSH 运行 &lt;code>proclet&lt;/code>;云运行时可以将 &lt;code>proclet&lt;/code> 作为 Kubernetes pod [25、28] 运行。&lt;/p>
&lt;p>具体而言,&lt;code>proclet&lt;/code> 通过 Unix Pipeline 与运行时交互。例如,当构造一个 &lt;code>proclet&lt;/code> 时,它会通过管道发送一条 &lt;code>RegisterReplica&lt;/code> 消息,以将自己标记为活动和就绪。它定期发出 &lt;code>ComponentsToHost&lt;/code> 请求以了解它应该运行哪些组件。如果组件调用不同组件的方法,&lt;code>proclet&lt;/code> 会发出 &lt;code>StartComponent&lt;/code> 请求以确保它已启动。&lt;/p>
&lt;p>运行时以对部署环境有意义的方式实现这些 API。我们希望大多数运行时实现包含以下两部分:&lt;/p>
&lt;ul>
&lt;li>(1) 一组通过 UNIX 管道与 proclet 直接通信的信封(Envelope)进程,以及&lt;/li>
&lt;li>(2) 协调 proclet 执行的全局管理器(Global Manager)(参见图-3)。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/5/pic3-runtime.png" alt="image.png">&lt;/p>
&lt;p>图-3:提案中的 Deployer 架构&lt;/p>
&lt;p>信封(Envelope)作为 proclet 的父进程运行,并将 API 调用中继到管理器。管理器跨可用资源集(例如服务器、VM)启动信封和(间接)proclet。在应用程序的整个生命周期中,管理器与信封交互以收集运行组件的健康和负载信息;聚合组件导出的指标、日志和跟踪;并处理启动新组件的请求。管理器还发布特定于环境的 API(例如,谷歌云[16]、AWS [4])更新流量分配并根据负载、健康状况和性能约束扩展和缩减组件。请注意,运行时实现控制平面(Golbal Manager)而不是数据平面,Proclet 直接相互通信。&lt;/p>
&lt;h3 id="44---原子滚动更新rollout">4.4 原子滚动更新(Rollout)&lt;/h3>
&lt;p>开发人员不可避免地必须发布其应用程序的新版本。一种广泛使用的方法是执行滚动更新,其中部署中的机器一台一台地从旧版本更新到新版本。在滚动更新期间,运行不同版本代码的机器必须相互通信,这可能会导致失败。 [78]表明大多数更新失败是由这些跨版本交互引起的。
为了解决这些复杂性,我们提出了一种不同的方法。运行时确保应用程序版本以原子方式推出,这意味着所有组件通信都发生在应用程序的单个版本中。运行时逐渐将流量从旧版本转移到新版本,但是一旦用户请求转发到特定版本,它就会完全在该版本内处理。原子部署的一种流行实现是使用蓝/绿部署[9]。&lt;/p>
&lt;h2 id="5-启用创新">5 启用创新&lt;/h2>
&lt;h3 id="51-传输transport放置placement和缩容scaling">5.1 传输(Transport)、放置(Placement)和缩容(Scaling)&lt;/h3>
&lt;p>运行时可以鸟瞰应用程序执行,这为性能优化开辟了新途径。例如,我们的框架可以在组件之间构建一个细粒度的调用图,并用它来识别关键的路径路径、瓶颈组件、频繁交互型组件等。使用这些信息,运行时可以做出更智能的扩缩容、独立部署和组合部署决策。此外,由于序列化和传输机制对开发者透明(Code Generate 机制自动实现),运行时可以自由地优化它们。例如,对于网络瓶颈应用程序,运行时可能决定压缩网络上的消息,对于某些部署,传输可能会利用 RDMA [32] 等技术。&lt;/p>
&lt;h3 id="52---路由routing">5.2 路由(Routing)&lt;/h3>
&lt;p>当请求以亲和力(affinity)路由时,某些组件的性能会大大提高。例如,考虑由基于磁盘的底层存储系统支持的内存缓存组件。当对相同键的请求被路由到相同的缓存副本时,缓存命中率和整体性能会提高。 Slicer [44]表明,许多应用程序可以从这种基于亲和力的路由中受益,并且当路由嵌入到应用程序本身时,路由效率最高[43]。我们的编程框架可以自然地扩展为包含路由 API。运行时还可以了解哪些方法从路由中获益最多并自动路由它们。&lt;/p>
&lt;h3 id="53--自动化测试automated-testing">5.3 自动化测试(Automated Testing)&lt;/h3>
&lt;p>微服务架构被吹捧的好处之一是容错。这个想法是,如果应用中的一个服务组件失败,应用的部分功能可用性会降低,但整个应用仍然可用。这在理论上很棒,但在实践中它依赖于开发人员确保他们的应用对故障具有弹性,更重要的是,测试他们的故障处理逻辑是否正确。由于构建和运行不同的微服务、系统地失败和恢复它们以及检查正确行为的开销,测试尤其具有挑战性。结果,只有一小部分基于微服务的系统针对这种类型的容错进行了测试。根据我们的建议,运行端到端测试能带来的帮助是微不足道的。因为应用程序是用单一编程语言编写的单个二进制文件,所以端到端测试变成了简单的单元测试。这为自动化容错测试打开了大门,类似于混沌测试[47]、Jepsen 测试[14]和模型检查[62]。&lt;/p>
&lt;h3 id="54--有状态应用滚动更新rollout">5.4 有状态应用滚动更新(Rollout)&lt;/h3>
&lt;p>我们的建议确保一个应用程序版本中的组件永远不会与不同版本中的组件通信。这使开发人员更容易推理正确性。但是,如果应用程序更新持久存储系统(如数据库)中的状态,则应用程序的不同版本将通过它们读取和写入的数据间接影响彼此。这些跨版本交互是不可避免的——持久状态,根据定义,跨版本持续存在 —— 但一个悬而未决的问题是如何测试这些交互并及早发现错误以避免在推出期间出现灾难性故障。&lt;/p>
&lt;h3 id="55--讨论">5.5 讨论&lt;/h3>
&lt;p>请注意,本节讨论的领域中的创新并不是我们提案所独有的。对传输协议[63、64]、路由[44、65]、测试[45、75]、资源管理[57、67、71]、故障排除[54、56]等。然而,我们的编程模型的独特功能支持新的创新,并使现有的创新更容易实现实施。&lt;/p>
&lt;p>例如,通过在我们的提议中利用原子部署,我们可以设计高效的序列化协议,可以安全地假设所有参与者都使用相同的模式。此外,我们的编程模型可以轻松地将路由逻辑直接嵌入到用户的应用程序中,从而提供一系列好处[43]。同样,我们的提案提供应用程序鸟瞰图的能力允许
研究人员专注于开发用于调整应用程序和降低部署成本的新解决方案。&lt;/p>
&lt;h2 id="6--原型实现">6 原型实现&lt;/h2>
&lt;p>我们的原型实现是用 Go [38]编写的,包括图2 中描述的组件 API、第4.2节中描述的代码生成器以及第 4.3 节中描述的 proclet 架构。该实现使用自定义序列化格式和直接构建在 TCP 之上的自定义传输协议。该原型还带有一个谷歌 Kubernetes 引擎 (GKE) 部署器,它通过渐进的蓝/绿部署实现多区域部署。它使用 Horizontal Pod Autoscalers [20]根据负载动态调整容器副本的数量,并遵循类似于图3中的架构。我们的实现可在github.com/ServiceWeaver 获得。&lt;/p>
&lt;h3 id="61--评价">6.1 评价&lt;/h3>
&lt;p>为了评估我们的原型,我们使用了一个流行的 Web 应用程序[41],它代表了开发人员编写的各种微服务应用程序。该应用程序有 11 个微服务,并使用 gRPC [18]和 Kubernetes [25]部署在云端。该应用程序是用各种编程语言编写的,因此为了公平比较,我们将应用程序移植为完全用 Go 编写。然后我们将应用程序移植到我们的原型中,每个微服务都被重写为一个组件。我们使用 Locust [26],一种工作负载生成器,在有和没有我们的原型的情况下对应用程序进行负载测试。&lt;/p>
&lt;p>工作负载生成器向应用程序发送稳定速率的 HTTP 请求。两个应用程序版本都配置为自动缩放容器副本的数量以响应负载。我们测量了应用程序版本在稳定状态下使用的 CPU 内核数量,以及它们的端到端延迟。表-2 显示了我们的结果。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/5/table2.png" alt="table2.png">&lt;/p>
&lt;p>表-2&lt;/p>
&lt;p>我们原型的大部分性能优势来自它使用专为非版本化数据交换设计的自定义序列化格式,以及它使用直接构建在 TCP 之上的流线型传输协议。例如,使用的序列化格式不需要对字段编号或类型信息进行任何编码。这是因为所有编码器和解码器都以完全相同的版本运行,并且预先就字段集以及它们的编码和解码顺序达成一致。&lt;/p>
&lt;p>为了与基线进行同类比较,我们没有将任何组件放在一起。当我们共同定位所有将 11 个组件集成到单个操作系统进程中,内核数量下降到 9,中值延迟下降到 0.38 毫秒,均比基线低一个数量级。这反映了行业经验[34、39]。&lt;/p>
&lt;h2 id="7--相关工作">7 相关工作&lt;/h2>
&lt;p>演员系统。最接近我们建议的解决方案是 Orleans [74]和 Akka [3]。这些框架还使用抽象来解耦应用程序和运行时。 Ray [70]是另一个基于角色的框架,但专注于 ML 应用程序。这些系统都不支持原子部署,而原子部署是完全应对 C2-C5 挑战的必要组成部分。其他流行的基于 actor 的框架,如 Er-lang [61]、E [52]、Thorn [48]和 C++ Actor Framework [10],给开发人员带来了处理系统和有关部署和执行的低级细节的负担,因此它们未能分离应用程序和运行时之间的关注点,因此没有完全解决 C1-C5。 CORBA、DCOM 和 Java RMI 等分布式对象框架使用与我们类似的编程模型,但存在许多技术和组织问题[58],并且也没有完全解决 C1-C5。&lt;/p>
&lt;p>基于微服务的系统。 Kubernetes [25]广泛用于在云中部署基于容器的应用程序。但是,它的重点与我们的提案正交,不涉及 C1-C5 中的任何一个。 Docker Compose [15]、Acorn [1]、Helm [19]、Skaffold [35]和 Istio [21]抽象出了一些微服务挑战(例如,配置生成)。然而,与将应用程序拆分为微服务、版本化推出和测试相关的挑战仍然留给了用户。因此,它们不满足 C1-C5。&lt;/p>
&lt;p>其他系统。还有许多其他解决方案可以让开发人员更轻松地编写分布式应用程序,包括数据流系统[51、59、77]、ML 推理服务系统[8、17、42、50、73]、无服务器解决方案[11, 24、36]、数据库[29、49]和 Web 应用程序[66]。最近,服务网格[46、69]提出了网络抽象以分解出常见的通信功能。我们的提案体现了这些相同的想法,但在通用服务系统和分布式应用程序的新领域中。在这种情况下,出现了新的挑战(例如,原子推出)。&lt;/p>
&lt;h2 id="8-讨论">8 讨论&lt;/h2>
&lt;h3 id="81-多个应用程序二进制文件">8.1 多个应用程序二进制文件&lt;/h3>
&lt;p>我们认为应用程序应该作为单个二进制文件来编写和构建,但我们承认这可能并不总是可行的。例如,应用程序的大小可能超出单个团队的能力,或者不同的应用程序服务可能出于组织原因需要不同的发布周期。在所有这些情况下,应用程序可能需要包含多个二进制文件。&lt;/p>
&lt;p>虽然本文没有解决需要使用多个二进制文件的情况,但我们相信我们的提议允许开发人员编写更少的二进制文件(即尽可能将多个服务分组为单个二进制文件),实现更好的性能,并推迟做出艰难的决定与如何划分应用程序有关。我们正在探索如何容纳以多种语言编写并编译成单独的二进制文件的应用程序。&lt;/p>
&lt;h3 id="82-与外部服务集成">8.2 与外部服务集成&lt;/h3>
&lt;p>应用程序通常需要与外部服务(例如,Postgres 数据库[29])进行交互。我们的编程模型允许应用程序像任何应用程序一样与这些服务交互。什么都不是,一切都必须是一个组件。但是,当外部服务在应用程序内部和跨应用程序广泛使用时,定义相应的组件可能会提供更好的代码重用。&lt;/p>
&lt;h3 id="83-分布式系统挑战">8.3 分布式系统挑战&lt;/h3>
&lt;p>虽然我们的编程模型允许开发人员专注于他们的业务逻辑并推迟将他们的应用程序部署到运行时的大量复杂性,但我们的提议并没有解决分布式系统的基本挑战 [53, 68, 76]。应用程序开发人员仍然需要意识到组件可能会失败或经历高延迟。&lt;/p>
&lt;h3 id="84-编程指导">8.4 编程指导&lt;/h3>
&lt;p>没有关于如何编写分布式应用程序的官方指南,因此关于将应用程序编写为单体应用程序还是微服务是更好的选择,一直存在着长期而激烈的争论。但是,每种方法都有其优点和缺点。我们认为开发人员应该使用我们的建议将他们的应用程序编写为单个二进制文件,然后再决定他们是否真的需要迁移到基于微服务的架构。通过推迟决定如何准确地拆分成不同的微服务,它允许他们编写更少但更好的微服务。&lt;/p>
&lt;h2 id="9--结论">9 结论&lt;/h2>
&lt;p>编写分布式应用程序时的现状涉及将应用程序拆分为可独立部署的服务。这种架构有很多好处,但也有很多缺点。在本文中,我们提出了一种不同的编程范式来回避这些缺点。我们的提议鼓励开发人员 (1) 编写划分为逻辑组件的单体应用程序,(2) 将物理分布和执行模块化单体的挑战推迟到运行时,以及 (3) 原子部署应用程序。这三个指导原则带来了许多好处,并为未来的创新打开了大门。与现状相比,我们的原型实施将应用程序延迟最多减少了 15 倍,并将成本最多减少了 9 倍。&lt;/p>
&lt;p>[1] Acorn. &lt;a href="https://www.acorn.io/">https://www.acorn.io/.&lt;/a>&lt;/p>
&lt;p>[2] Actor model. &lt;a href="https://en.wikipedia.org/wiki/Actor_model">https://en.wikipedia.org/wiki/Actor_model.&lt;/a>&lt;/p>
&lt;p>[3] Akka. &lt;a href="https://akka.io">https://akka.io.&lt;/a>&lt;/p>
&lt;p>[4] Amazon Web Services. &lt;a href="https://aws.amazon.com/">https://aws.amazon.com/.&lt;/a>&lt;/p>
&lt;p>[5] Apache avro. &lt;a href="https://avro.apache.org/docs/1.2.0/">https://avro.apache.org/docs/1.2.0/.&lt;/a>&lt;/p>
&lt;p>[6] Apache thrift. &lt;a href="https://thrift.apache.org/">https://thrift.apache.org/.&lt;/a>&lt;/p>
&lt;p>[7] AWS Cloud Map. &lt;a href="https://aws.amazon.com/cloud-map/">https://aws.amazon.com/cloud-map/.&lt;/a>&lt;/p>
&lt;p>[8] Azure Machine Learning. &lt;a href="https://docs.microsoft.com/en-us/azure/machine-learning">https://docs.microsoft.com/en-us/azure/&lt;/a>&lt;a href="https://docs.microsoft.com/en-us/azure/machine-learning">machine-learning.&lt;/a>&lt;/p>
&lt;p>[9] Blue/green deployments. &lt;a href="https://tinyurl.com/3bk64ch2">https://tinyurl.com/3bk64ch2.&lt;/a>&lt;/p>
&lt;p>[10] The c++ actor framework. &lt;a href="https://www.actor-framework.org/">https://www.actor-framework.org/.&lt;/a>&lt;/p>
&lt;p>[11] Cloudflare Workers. &lt;a href="https://workers.cloudflare.com/">https://workers.cloudflare.com/.&lt;/a>&lt;/p>
&lt;p>[12] Continuous integration and delivery - circleci. &lt;a href="https://circleci.com/">https://circleci.com/.&lt;/a>&lt;/p>
&lt;p>[13] Dapr - distributed application runtime. &lt;a href="https://dapr.io/">https://dapr.io/.&lt;/a>&lt;/p>
&lt;p>[14] Distributed systems safety research. &lt;code>https://jespen.io.&lt;/code>&lt;/p>
&lt;p>[15] Docker compose. &lt;a href="https://docs.docker.com/compose/">https://docs.docker.com/compose/.&lt;/a>&lt;/p>
&lt;p>[16] Google Cloud. &lt;a href="https://cloud.google.com/">https://cloud.google.com/.&lt;/a>&lt;/p>
&lt;p>[17] Google Cloud AI Platform. &lt;a href="https://cloud.google.com/ai-platform">https://cloud.google.com/ai-platform.&lt;/a>&lt;/p>
&lt;p>[18] grpc. &lt;a href="https://grpc.io/">https://grpc.io/.&lt;/a>&lt;/p>
&lt;p>[19] Helm. &lt;a href="http://helm.sh">http://helm.sh.&lt;/a>&lt;/p>
&lt;p>[20] Horizontal Pod Autoscaling. &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">https://kubernetes.io/docs/tasks/run-&lt;/a>&lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">application/horizontal-pod-autoscale/.&lt;/a>&lt;/p>
&lt;p>[21] Istio. &lt;a href="https://istio.io/">https://istio.io/.&lt;/a>&lt;/p>
&lt;p>[22] Jenkins. &lt;a href="https://www.jenkins.io/">https://www.jenkins.io/.&lt;/a>&lt;/p>
&lt;p>[23] Json. &lt;a href="https://www.json.org/json-en.html">https://www.json.org/json-en.html.&lt;/a>&lt;/p>
&lt;p>[24] Kalix. &lt;a href="https://www.kalix.io/">https://www.kalix.io/.&lt;/a>&lt;/p>
&lt;p>[25] Kubernetes. &lt;a href="https://kubernetes.io/">https://kubernetes.io/.&lt;/a>&lt;/p>
&lt;p>[26] Locust. &lt;a href="https://locust.io/">https://locust.io/.&lt;/a>&lt;/p>
&lt;p>[27] Micro | powering the future of cloud. &lt;a href="https://micro.dev/">https://micro.dev/.&lt;/a>&lt;/p>
&lt;p>[28] Pods. &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/">https://kubernetes.io/docs/concepts/workloads/pods/.&lt;/a>&lt;/p>
&lt;p>[29] Postgresql. &lt;a href="https://www.postgresql.org/">https://www.postgresql.org/.&lt;/a>&lt;/p>
&lt;p>[30] Protocol buffers. &lt;a href="https://developers.google.com/protocol-buffers">https://developers.google.com/protocol-buffers.&lt;/a>&lt;/p>
&lt;p>[31] RabbitMQ. &lt;a href="https://www.rabbitmq.com/">https://www.rabbitmq.com/.&lt;/a>&lt;/p>
&lt;p>[32] Remote direct memory access. &lt;a href="https://en.wikipedia.org/wiki/Remote_direct_memory_access">https://en.wikipedia.org/wiki/Remote_&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Remote_direct_memory_access">direct_memory_access.&lt;/a>&lt;/p>
&lt;p>[33] REST API. &lt;a href="https://restfulapi.net/">https://restfulapi.net/.&lt;/a>&lt;/p>
&lt;p>[34] Scaling up the Prime Video audio/video monitoring service and reduc-ing costs by 90%. &lt;a href="https://tinyurl.com/yt6nxt63">https://tinyurl.com/yt6nxt63.&lt;/a>&lt;/p>
&lt;p>[35] Skaffold. &lt;a href="https://skaffold.dev/">https://skaffold.dev/.&lt;/a>&lt;/p>
&lt;p>[36] Temporal. &lt;a href="https://temporal.io/">https://temporal.io/.&lt;/a>&lt;/p>
&lt;p>[37] Terraform. &lt;a href="https://www.terraform.io/">https://www.terraform.io/.&lt;/a>&lt;/p>
&lt;p>[38] The Go programming language. &lt;a href="https://go.dev/">https://go.dev/.&lt;/a>&lt;/p>
&lt;p>[39] To Microservices and Back Again - Why Segment Went Back to a Monolith. &lt;a href="https://tinyurl.com/5932ce5n">https://tinyurl.com/5932ce5n.&lt;/a>&lt;/p>
&lt;p>[40] WebSocket. &lt;a href="https://en.wikipedia.org/wiki/WebSocket">https://en.wikipedia.org/wiki/WebSocket.&lt;/a>&lt;/p>
&lt;p>[41] Online boutique. &lt;a href="https://github.com/GoogleCloudPlatform/microservices-demo">https://github.com/GoogleCloudPlatform/&lt;/a>&lt;a href="https://github.com/GoogleCloudPlatform/microservices-demo">microservices-demo,&lt;/a>2023.&lt;/p>
&lt;p>[42] M. Abadi, P. Barham, J. Chen, Z. Chen, A. Davis, J. Dean, M. Devin,
S.Ghemawat, G. Irving, M. Isard, M. Kudlur, J. Levenberg, R. Monga,
S.Moore, D. G. Murray, B. Steiner, P. Tucker, V. Vasudevan, P. Warden,
M.Wicke, Y. Yu, and X. Zheng. Tensorflow: A system for large-scale machine learning. In OSDI, 2016.&lt;/p>
&lt;p>[43] A. Adya, R. Grandl, D. Myers, and H. Qin. Fast key-value stores: An idea whose time has come and gone. In HotOS, 2019.&lt;/p>
&lt;p>[44] A. Adya, D. Myers, J. Howell, J. Elson, C. Meek, V. Khemani, S. Fulger,
P.Gu, L. Bhuvanagiri, J. Hunter, R. Peon, L. Kai, A. Shraer, A. Merchant, and K. Lev-Ari. Slicer: Auto-sharding for datacenter applications. In OSDI, 2016.&lt;/p>
&lt;p>[45] D. Ardelean, A. Diwan, and C. Erdman. Performance analysis of cloud applications. In NSDI, 2018.&lt;/p>
&lt;p>[46] S. Ashok, P. B. Godfrey, and R. Mittal. Leveraging service meshes as a new network layer. In HotNets, 2021.&lt;/p>
&lt;p>[47] A. Basiri, N. Behnam, R. De Rooij, L. Hochstein, L. Kosewski, J.Reynolds, and C. Rosenthal. Chaos engineering. In IEEE Software, 2016.&lt;/p>
&lt;p>[48] B. Bloom, J. Field, N. Nystrom, J. Östlund, G. Richards, R. Strniša, J.Vitek, and T. Wrigstad. Thorn: Robust, concurrent, extensible script-ing on the jvm. In OOPSLA, 2009.&lt;/p>
&lt;p>[49] J. C. Corbett, J. Dean, M. Epstein, A. Fikes, C. Frost, J. J. Furman, S. Ghe-mawat, A. Gubarev, C. Heiser, P. Hochschild, W. Hsieh, S. Kanthak, E.Kogan, H. Li, A. Lloyd, S. Melnik, D. Mwaura, D. Nagle, S. Quin-lan, R. Rao, L. Rolig, Y. Saito, M. Szymaniak, C. Taylor, R. Wang, and D.Woodford. Spanner: Google’s globally-distributed database. In OSDI, 2012.&lt;/p>
&lt;p>[50] D. Crankshaw, X. Wang, G. Zhou, M. J. Franklin, J. E. Gonzalez, and I.Stoica. Clipper: A low-latency online prediction serving system. In NSDI, 2017.&lt;/p>
&lt;p>[51] J. Dean and S. Ghemawat. Mapreduce: Simplified data processing on large clusters. In OSDI, 2004.
[52] J. Eker, J. Janneck, E. Lee, J. Liu, X. Liu, J. Ludvig, S. Neuendorffer, S.Sachs, and Y. Xiong. Taming heterogeneity - the ptolemy approach. In Proceedings of the IEEE, 2003.&lt;/p>
&lt;p>[53] M. J. Fischer, N. A. Lynch, and M. S. Paterson. Impossibility of dis-tributed consensus with one faulty process. In ACM Journal, 1985.&lt;/p>
&lt;p>[54] Y. Gan, M. Liang, S. Dev, D. Lo, and C. Delimitrou. Sage: Practical and Scalable ML-Driven Performance Debugging in Microservices. In ASPLOS, 2021.&lt;/p>
&lt;p>[55] Y. Gan, Y. Zhang, D. Cheng, A. Shetty, P. Rathi, N. Katarki, A. Bruno, J.Hu, B. Ritchken, B. Jackson, et al. An open-source benchmark suite for microservices and their hardware-software implications for cloud &amp;amp; edge systems. In ASPLOS, 2019.&lt;/p>
&lt;p>[56] Y. Gan, Y. Zhang, K. Hu, Y. He, M. Pancholi, D. Cheng, and C. De-limitrou. Seer: Leveraging Big Data to Navigate the Complexity of Performance Debugging in Cloud Microservices. In ASPLOS, 2019.&lt;/p>
&lt;p>[57] R. Grandl, G. Ananthanarayanan, S. Kandula, S. Rao, and A. Akella. Multi-resource packing for cluster schedulers. In SIGCOMM, 2014.&lt;/p>
&lt;p>[58] M. Henning. The rise and fall of corba: There’s a lot we can learn from corba’s mistakes. In Queue, 2006.&lt;/p>
&lt;p>[59] M. Isard, M. Budiu, Y. Yu, A. Birrell, and D. Fetterly. Dryad: Distributed data-parallel programs from sequential building blocks. In Eurosys, 2007.&lt;/p>
&lt;p>[60] K. Jay, N. Neha, and R. Jun. Kafka : a distributed messaging system for log processing. In NetDB, 2011.&lt;/p>
&lt;p>[61] A. Joe. Erlang. In Communications of the ACM, 2010.&lt;/p>
&lt;p>[62] L. Lamport. The temporal logic of actions. In ACM TOPLS, 1994.&lt;/p>
&lt;p>[63] A. Langley, A. Riddoch, A. Wilk, A. Vicente, C. Krasic, D. Zhang, F.Yang, F. Kouranov, I. Swett, J. Iyengar, J. Bailey, J. Dorfman, J. Roskind, J.Kulik, P. Westin, R. Tenneti, R. Shade, R. Hamilton, V. Vasiliev, W.-T. Chang, and Z. Shi. The quic transport protocol: Design and internet-scale deployment. In SIGCOMM, 2017.&lt;/p>
&lt;p>[64] N. Lazarev, N. Adit, S. Xiang, Z. Zhang, and C. Delimitrou. Dagger: Towards Efficient RPCs in Cloud Microservices with Near-Memory Reconfigurable NICs. In ASPLOS, 2021.&lt;/p>
&lt;p>[65] S. Lee, Z. Guo, O. Sunercan, J. Ying, T. Kooburat, S. Biswal, J. Chen, K.Huang, Y. Cheung, Y. Zhou, K. Veeraraghavan, B. Damani, P. M. Ruiz, V.Mehta, and C. Tang. Shard manager: A generic shard management framework for geo-distributed applications. In SOSP, 2021.&lt;/p>
&lt;p>[66] B. Livshits and E. Kiciman. Doloto: Code splitting for network-bound web 2.0 applications. In FSE, 2008.&lt;/p>
&lt;p>[67] S. Luo, H. Xu, C. Lu, K. Ye, G. Xu, L. Zhang, Y. Ding, J. He, and C. Xu. Characterizing microservice dependency and performance: Alibaba trace analysis. In SOCC, 2021.&lt;/p>
&lt;p>[68] N. A. Lynch. Distributed algorithms. In Morgan Kaufmann Publishers Inc., 1996.&lt;/p>
&lt;p>[69] S. McClure, S. Ratnasamy, D. Bansal, and J. Padhye. Rethinking net-working abstractions for cloud tenants. In HotOS, 2021.&lt;/p>
&lt;p>[70] P. Moritz, R. Nishihara, S. Wang, A. Tumanov, R. Liaw, E. Liang, M. Eli-bol, Z. Yang, W. Paul, M. I. Jordan, and I. Stoica. Ray: A distributed framework for emerging ai applications. In OSDI, 2018.&lt;/p>
&lt;p>[71] H. Qiu, S. S. Banerjee, S. Jha, Z. T. Kalbarczyk, and R. K. Iyer. FIRM: An intelligent fine-grained resource management framework for SLO-Oriented microservices. In OSDI, 2020.&lt;/p>
&lt;p>[72] D. Raghavan, P. Levis, M. Zaharia, and I. Zhang. Breakfast of champi-ons: towards zero-copy serialization with nic scatter-gather. In HotOS, 2021.&lt;/p>
&lt;p>[73] F. Romero, Q. Li, N. J. Yadwadkar, and C. Kozyrakis. Infaas: Automated model-less inference serving. In ATC, 2021.&lt;/p>
&lt;p>[74] B. Sergey, G. Allan, K. Gabriel, L. James, P. Ravi, and T. Jorgen. Orleans: Cloud computing for everyong. In SOCC, 2011.&lt;/p>
&lt;p>[75] M. Waseem, P. Liang, G. Márquez, and A. D. Salle. Testing microser-vices architecture-based applications: A systematic mapping study. In APSEC, 2020.&lt;/p>
&lt;p>[76] Wikipedia contributors. Fallacies of distributed computing.&lt;/p>
&lt;p>[77] M. Zaharia, M. Chowdhury, T. Das, A. Dave, J. Ma, M. McCauly, M. J. Franklin, S. Shenker, and I. Stoica. Resilient distributed datasets: A fault-tolerant abstraction for in-memory cluster computing. In NSDI, 2012.&lt;/p>
&lt;p>[78] Y. Zhang, J. Yang, Z. Jin, U. Sethi, K. Rodrigues, S. Lu, and D. Yuan. Understanding and detecting software upgrade failures in distributed systems. In SOSP, 2021.&lt;/p></description></item><item><title>Blog: Apache Dubbo 开源之夏 2023,贡献社区赢取 12000 奖金</title><link>https://dubbo.apache.org/zh-cn/blog/2023/05/15/apache-dubbo-%E5%BC%80%E6%BA%90%E4%B9%8B%E5%A4%8F-2023%E8%B4%A1%E7%8C%AE%E7%A4%BE%E5%8C%BA%E8%B5%A2%E5%8F%96-12000-%E5%A5%96%E9%87%91/</link><pubDate>Mon, 15 May 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/05/15/apache-dubbo-%E5%BC%80%E6%BA%90%E4%B9%8B%E5%A4%8F-2023%E8%B4%A1%E7%8C%AE%E7%A4%BE%E5%8C%BA%E8%B5%A2%E5%8F%96-12000-%E5%A5%96%E9%87%91/</guid><description>
&lt;p>欢迎在校同学们参与Apache Dubbo开源之夏,社区导师手把手让&lt;strong>你的代码被社会广泛复用,来赚取最高12000奖金,可&lt;/strong>推荐入职/实习你心意公司,&lt;strong>又拿钱又成长又有价值&lt;/strong>,你还等什么呢?报名马上截止,快来参与Apache Dubbo开源之夏。&lt;/p>
&lt;h2 id="ospp-开源之夏是什么">OSPP 开源之夏是什么?&lt;/h2>
&lt;p>开源之夏是由“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励&lt;strong>在校学生&lt;/strong>积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展,培养和发掘更多优秀的开发者。
活动联合国内外各大开源社区,针对重要开源软件的开发与维护提供项目任务,并面向全球高校学生开放报名。
学生可在本活动中自主选择感兴趣的项目任务进行申请,并在中选后获得该开源项目资深维护者(社区导师)亲自指导的机会,完成项目并贡献给社区后,参与学生还将获得开源之夏&lt;strong>活动奖金&lt;/strong>和&lt;strong>结项证书&lt;/strong>。&lt;/p>
&lt;h1 id="apache-dubbo-项目简介">Apache Dubbo 项目简介&lt;/h1>
&lt;p>Apache Dubbo 是国内最具影响力的开源软件项目之一,由阿里巴巴贡献开源,是支撑阿里双十一百万集群、万亿次服务调用的核心框架,目前 Dubbo 已捐献给享誉世界的 Apache 软件基金会 (ASF)。&lt;/p>
&lt;h2 id="参与apache-dubbo开源之夏条件">参与Apache Dubbo开源之夏条件&lt;/h2>
&lt;ul>
&lt;li>本活动面向年满 18 周岁在校学生。&lt;/li>
&lt;li>暑期即将毕业的学生,只要在申请时学生证处在有效期内,就可以提交申请。&lt;/li>
&lt;li>海外学生可提供录取通知书、学生卡、在读证明等文件用于证明学生身份。&lt;/li>
&lt;/ul>
&lt;h2 id="参与apache-dubbo开源之夏你能获得什么">参与Apache Dubbo开源之夏,你能获得什么?&lt;/h2>
&lt;ol>
&lt;li>**【你的代码被社会广泛复用】**你的代码可能会运行在上万家企业核心业务逻辑中,帮助企业解决问题。&lt;/li>
&lt;li>**【赢得最高12000奖金】**奖金总额根据项目难度分为进阶 12000 元、基础 8000 元(注:奖金数额为税前人民币金额)&lt;/li>
&lt;li>**【社区核心人员辅导快速成长】**只要你报名被选中,每个题目的导师会精心手把手教你融入社区,帮助你完成题目的设计以及最终的落地。&lt;/li>
&lt;li>**【推荐入职/实习】**在本次编程之夏项目中表现优秀同学,可推荐入职/实习 你心意的公司工作。&lt;/li>
&lt;li>**【额外获得社区礼包】**所有参与本次编程之夏项目的同学,均可获得Apache Dubbo社区大礼包。&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>百分百有奖品拿哦&lt;/strong>,现在唯一的问题是时间不多了,赶紧上车报名,截止报名时间是6月4日,6个核心题目,快点来报名参与Apache Dubbo编程之夏吧。&lt;/p>
&lt;h2 id="课题报名方式">课题&amp;amp;报名方式&lt;/h2>
&lt;p>点开Apache Dubbo开源之夏的&lt;a href="https://summer-ospp.ac.cn/org/orgdetail/ab188e59-fab8-468f-bc89-bdc2bd8b5e64?lang=zh">链接&lt;/a> 选择你喜欢的题目:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0282?list=org&amp;amp;navpage=org">IDL 管理平台&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0286?list=org&amp;amp;navpage=org">API 管理平台&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0284?list=org&amp;amp;navpage=org">基于 Kubernetes 的集成测试体系建设&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0287?list=org&amp;amp;navpage=org">服务 JSON 序列化兼容性校验&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0289?list=org&amp;amp;navpage=org">将 Dubbo 工程结构重构为 Gradle 项目&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0292?list=org&amp;amp;navpage=org">自动化性能测试方案&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0294?list=org&amp;amp;navpage=org">移除对 jprotoc 的依赖&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0520?list=org&amp;amp;navpage=org">Node.js HTTP/2 协议实现&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/prodetail/23a7f0553?list=org&amp;amp;navpage=org">实现 Dubbo Rust 的路由模块&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>具体流程请参考 &lt;a href="https://summer-ospp.ac.cn/help/student/">学生指南&lt;/a>。&lt;/p>
&lt;p>请注意找导师沟通截止流程时间,优先更导师沟通,能帮助你更好的了解题目。&lt;/p>
&lt;p>为了方便大家报名,我们有以下咨询通道开放,如果对报名、题目、如何撰写 Proposal 有任何疑问,都可以前往咨询:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>扫码关注回复 &amp;ldquo;Apache Dubbo&amp;rdquo; 官网微信公众号,回复 “编程之夏” 加入微信沟通群&lt;/p>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/jpeg/54037/1684119089728-eae2eb7d-4098-430e-a69e-a3939265e22b.jpeg#clientId=ub09992a8-5524-4&amp;amp;from=paste&amp;amp;height=129&amp;amp;id=u938740a9&amp;amp;originHeight=258&amp;amp;originWidth=258&amp;amp;originalType=binary&amp;amp;ratio=2&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=27895&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=ud0f6af1b-fed8-4a07-aa5a-4e3f3bce229&amp;amp;title=&amp;amp;width=129" alt="qrcode_for_gh_a84d4cf4c871_258 (1).jpg">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>钉钉群:22895027434(Dubbo 2023 编程之夏群),搜索并加入咨询问题&lt;/p>
&lt;/li>
&lt;li>
&lt;p>发送邮件到 Apache Dubbo 邮件组:dev@dubbo.apache.org&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h1 id="参考资料">参考资料&lt;/h1>
&lt;ul>
&lt;li>Apache Dubbo Github 仓库:
&lt;ul>
&lt;li>&lt;a href="http://github.com/apache/dubbo">Java&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://github.com/apache/dubbo-go">Go&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://github.com/apache/dubbo-js">Node.js&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://github.com/apache/dubbo-rust">Rust&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://github.com/apache/dubbo-admin">Admin&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://github.com/dubbo/">更多生态&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://dubbo.apache.org/">Apache Dubbo 官网&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://summer-ospp.ac.cn/org/orgdetail/a7f6e2ad-4acc-47f8-9471-4e54b9a166a6?lang=zh">开源之夏官网&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>为了让同学们更好地了解Apache Dubbo,我们还在观望提供了电子书供大家阅读学习。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://cn.dubbo.apache.org/zh-cn/contact/books/">Apache Dubbo 微服务开发从入门到精通&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cn.dubbo.apache.org/zh-cn/contact/books/">Apache Dubbo3 源码深度解析&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>同时如果同学们对其他领域项目感兴趣,也可以尝试申请,例如:&lt;/p>
&lt;ul>
&lt;li>对于&lt;strong>微服务注册发现和配置管理&lt;/strong>有兴趣的同学,可以尝试填报 &lt;a href="https://summer-ospp.ac.cn/org/orgdetail/ab188e59-fab8-468f-bc89-bdc2bd8b5e64?lang=zh">Nacos 开源之夏&lt;/a>;&lt;/li>
&lt;li>对于&lt;strong>微服务分布式事务&lt;/strong>有兴趣的同学,可以尝试填报&lt;a href="https://summer-ospp.ac.cn/org/orgdetail/064c15df-705c-483a-8fc8-02831370db14?lang=zh">Seata&lt;/a>开源之夏;&lt;/li>
&lt;li>对于&lt;strong>微服务框架和RPC框架&lt;/strong>有兴趣的同学,可以尝试填报&lt;a href="https://summer-ospp.ac.cn/org/orgdetail/41d68399-ed48-4d6d-9d4d-3ff4128dc132?lang=zh">Spring Cloud Alibaba&lt;/a> 和 &lt;a href="https://summer-ospp.ac.cn/org/orgdetail/a7f6e2ad-4acc-47f8-9471-4e54b9a166a6?lang=zh">Dubbo&lt;/a> 开源之夏;&lt;/li>
&lt;li>对于&lt;strong>云原生网关&lt;/strong>有兴趣的同学,可以尝试填报&lt;a href="https://higress.io/zh-cn/blog/ospp-2023">Higress&lt;/a> 开源之夏;&lt;/li>
&lt;li>对于&lt;strong>分布式高可用防护&lt;/strong>有兴趣的同学,可以尝试填报&lt;a href="https://summer-ospp.ac.cn/org/orgdetail/5e879522-bd90-4a8b-bf8b-b11aea48626b?lang=zh">Sentinel&lt;/a> 开源之夏;&lt;/li>
&lt;li>对于&lt;strong>微服务治理&lt;/strong>有兴趣的同学,可以尝试填报&lt;a href="https://summer-ospp.ac.cn/org/orgdetail/aaff4eec-11b1-4375-997d-5eea8f51762b?lang=zh">OpenSergo&lt;/a> 开源之夏。&lt;/li>
&lt;/ul></description></item><item><title>Blog: 引言</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</guid><description>
&lt;h2 id="引言">引言&lt;/h2>
&lt;p>服务指标统计体系是 Dubbo 可观测能力的重要组成部分。
dubbo-metrics 指标模块旨在将 dubbo 内部零散的 Metrics 相关类综合到一个单独的模块中,提供一套更加完善、全面、可拓展、解耦合的指标采样-统计-导出解决方案。&lt;/p>
&lt;p>dubbo-metrics 模块包括:&lt;/p>
&lt;ul>
&lt;li>dubbo-metrics-api 公用接口包&lt;/li>
&lt;li>dubbo-metrics-prometheus 普罗米修斯适配包&lt;/li>
&lt;li>dubbo-metrics-metadata 元数据中心指标监控包&lt;/li>
&lt;li>dubbo-metrics-registry 注册中心指标监控包&lt;/li>
&lt;li>dubbo-metrics-config-center 配置中心指标监控包&lt;/li>
&lt;li>dubbo-metrics-default 接口默认实现包,提供dubbo内部核心指标的监控功能&lt;/li>
&lt;/ul>
&lt;p>在设计上,dubbo-metrics 深入应用事件驱动编程思想,总体体现出下图的事件处理链路:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metrics-event-struct.png" alt="metrics-event-struct">&lt;/p>
&lt;p>在拓展上,dubbo-metrics 抽象了一套指标导出接口与抽象实现,可实现兼容多种指标统计监控中心,默认提供了普罗米修斯实现。&lt;/p></description></item><item><title>Blog: 1-指标样本的收集与存储</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</guid><description>
&lt;h2 id="一指标样本的收集与存储">一、指标样本的收集与存储&lt;/h2>
&lt;h3 id="指标样本收集">指标样本收集&lt;/h3>
&lt;p>&lt;strong>指标收集器(Collector)是指标对外导出的入口&lt;/strong>。最终导出到指标统计中心的指标采样实际均直接来源于各个指标采样器。因此,我们将从各个收集器实现开始,分析 dubbo-metrics 模块是如何工作的。&lt;/p>
&lt;p>指标收集操作定义在 MetricsCollector (指标采集器,SPI)接口中,可以通过它的实现收集某一类的指标样本(MetricSample)。它主要有以下实现,对应着不同类型的指标:&lt;/p>
&lt;ul>
&lt;li>ConfigCenterMetricsCollector &lt;strong>配置中心操作相关指标收集器&lt;/strong> ,收集配置信息的变化次数&lt;/li>
&lt;li>MetadataMetricsCollector &lt;strong>元数据中心操作相关指标收集器&lt;/strong>,收集提供者、消费者对元数据中心操作(推送数据、拉取数据)情况的计数、耗时统计。&lt;/li>
&lt;li>RegistryMetricsCollector &lt;strong>注册中心相关操作指标收集器&lt;/strong>,收集应用级、接口级服务注册成功、失败、耗时的相关计数。&lt;/li>
&lt;li>DefaultMetricsCollector &lt;strong>默认指标收集器&lt;/strong>,内置多种采样器来完成不同类型的内部指标采样。&lt;/li>
&lt;li>HistogramMetricsCollector &lt;strong>直方图指标收集器&lt;/strong>,利用 micrometer API 处理直方图类型的指标,它的实现较为特殊。&lt;/li>
&lt;/ul>
&lt;p>配置中心 、元数据、服务注册及默认指标收集器均实现自混合指标收集器(CombMetricsCollector)。混合指标收集器实现了 ApplicationMetricsCollector 、ServiceMetricsCollector 、MethodMetricsCollector 三个接口(定义按应用名收集、按应用名-服务名收集和按应用-方法名收集指标的操作),因此它们可以进行应用、服务和方法三个层面的指标收集工作。&lt;/p>
&lt;p>默认指标收集器的特点是通过内部的指标采样器(MetricsSampler)完成指标事件的处理操作,而不是其它收集器的指标监听器(MetricsListener)&lt;/p>
&lt;p>直方图指标收集器则负责收集直方图类型的指标。它利用直方图度量寄存器(HistogramMetricRegister)借助 micrometer API 完成直方图样本的采集。直方图类型包括百分位数、服务水平目标、最小预期值、最大预期值、统计数据分布有效期等。&lt;/p>
&lt;p>&lt;strong>Collector的继承关系:&lt;/strong>&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/collectors.png" alt="collectors">&lt;/p>
&lt;p>可以看出,每个指标收集器都具有来自 MetricsListener 的监听指标事件的能力。为什么指标收集器本身需要监听指标事件? 在后文中,我们将讨论指标收集器是如何利用内置的子转发器(SubDispatcher)转发指标事件,并完成计数处理的。&lt;/p>
&lt;h3 id="指标样本存储">指标样本存储&lt;/h3>
&lt;p>前文中,我们了解了指标收集的入口是指标收集器(Collector)。那么各个收集器从哪里收集指标样本?&lt;/p>
&lt;p>&lt;strong>对于配置中心、元数据中心、 注册中心的指标收集器:&lt;/strong>&lt;/p>
&lt;p>它们分别负责采集三大中心模块的指标,均继承于&lt;strong>混合数据收集器(CombMetricsCollector)&lt;/strong>,而混合数据收集器中实现了 export 方法 。&lt;/p>
&lt;p>混合数据收集器内部有一个&lt;strong>基本数据聚合器(BaseStatComposite)&lt;/strong>,它实现了 MetricsExport 接口,该接口定义了指标导出操作,混合数据收集器则利用它的 export 方法导出指标。&lt;/p>
&lt;p>基本数据聚合器是一个抽象类,内有三个属性:ApplicationStatComposite 、ServiceStatComposite 和 RtStatComposite 。它们的作用:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ApplicationStatComposite 应用数据聚合器&lt;/strong>,应用程序级别相关事件的计数 ,根据指标Key( MetricsKey )和应用名统计指标,提供计数递增操作&lt;/li>
&lt;li>&lt;strong>ServiceStatComposite 服务数据聚合器&lt;/strong>,服务级别相关事件的计数,根据指标Key、应用名和服务名统计指标,提供计数递增操作&lt;/li>
&lt;li>&lt;strong>MethodStatComposite 方法数据聚合器&lt;/strong>,方法级别相关事件的计数,存储各方法RPC调用相关计数。&lt;/li>
&lt;li>&lt;strong>RtStatComposite,Rt(Response Time,响应时间)数据聚合器&lt;/strong>,包括应用级别和服务级别。根据应用名、服务名、注册的指标名及相应相应时间统计指标,提供添加操作。&lt;/li>
&lt;/ul>
&lt;p>对于以上四个聚合器,他们的职责就是存储某一类型的采样样本。&lt;/p>
&lt;p>** 基本数据聚合器 (BaseStatComposite)** 对这三个子聚合器的操作进行了简单整合,统一提供给外界。&lt;strong>而混合指标收集器(CombMetricsCollector)&lt;/strong> 也基本保留了内部基本数据聚合器的所有操作,将其封装为 &lt;code>increment&lt;/code>、&lt;code>setNum&lt;/code>、&lt;code>addRt &lt;/code>三个方法(及它们的重载,分别收集应用级数据和服务级数据)向上提供。外部组件可以直接调用这些收集器完成指标更新操作。&lt;/p>
&lt;p>&lt;strong>当调用元数据中心指标收集器、注册中心指标收集器的 collect 方法时,最终会调用&lt;code>BaseStatComposite.export(MetricsCategory category)&lt;/code> , 该方法会收集内部三个聚合器的指标并返回。&lt;/strong>&lt;/p>
&lt;p>需要注意的是, 配置中心指标收集器不依赖于基本数据聚合器 导出数据,它在创建时将基本数据聚合器置为null,而使用自己的 updatedMetrics 字段存储采样:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ConfigCenterMetricsCollector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>ConfigCenterMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> updatedMetrics &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ConfigCenterMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//BaseStatComposite = null
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>混合指标收集器和数据聚合器之间呈现如下的包含关系:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct">&lt;/p>
&lt;p>&lt;strong>DefaultMetricsCollector 默认指标采集器:&lt;/strong>&lt;/p>
&lt;p>它不直接存储采样数据,而是通过收集其下**指标采样器(MetricsSampler)**的样本来完成采样工作。这些采样器包括:&lt;/p>
&lt;ul>
&lt;li>方法采样器&lt;/li>
&lt;li>应用采样器&lt;/li>
&lt;li>线程池采样器&lt;/li>
&lt;/ul>
&lt;p>这些采样器完成采样后,还会利用采集器中的**事件多播器(Multicaster)**将指标事件发布出去,可以被其它监听器处理。详细流程将在后文中探讨。&lt;/p>
&lt;p>&lt;strong>HistogramMetricsCollector 直方图指标采集器:&lt;/strong>&lt;/p>
&lt;p>由于需要使用Timer完成直方图属性的统计,它使用自己的容器存储采样数据。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">HistogramMetricsCollector&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> MetricsListener &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//方法指标样本与对应的Timer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> Timer&lt;span style="color:#719e07">&amp;gt;&lt;/span> rt &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Timer(计时器)由 micrometer API 提供,常用于统计一分钟内的大量事件。&lt;/p></description></item><item><title>Blog: 2-指标收集器的指标采集流程</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</guid><description>
&lt;h2 id="二指标收集器的指标采集流程">二、指标收集器的指标采集流程&lt;/h2>
&lt;p>在前文中,我们了解了指标收集器(Collector)最终收集的数据只有三个来源:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>实现自混合指标收集器(CombMetricsCollector) 的元数据指标收集器(MetadataMetricsCollector)和注册中心指标收集器(RegistryMetricsCollector),它们的样本均存储在内置的基本数据聚合器中。具体来说,是基本数据聚合器下的四个子数据聚合器中:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>DefaultMetricsCollector 默认指标收集器&lt;/strong>,它的样本不仅来自于指标事件,还来自其下**采样器(Sampler)**中,用于Dubbo核心模块的采样。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>HistogramMetricsCollector 直方图指标收集器&lt;/strong>,由于采样数据的特殊性,它的样本直接以 Map 存储在内部。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>接下来,我们需要明确它们存储的指标是如何添加进去的。&lt;/p>
&lt;h3 id="1服务治理模块的指标采集流程">1,服务治理模块的指标采集流程&lt;/h3>
&lt;p>通过之前的分析,我们知道服务治理模块的指标采集器均实现自混合指标收集器(CombMetricsCollector)。它对基本数据聚合器(BaseStatComposite) 的大部分方法做了封装。基本数据聚合器又封装了四个负责存储不同类型指标采样的子聚合器。&lt;/p>
&lt;p>这四个子聚合器包括:&lt;/p>
&lt;ul>
&lt;li>ApplicationStatComposite&lt;/li>
&lt;li>ServiceStatComposite&lt;/li>
&lt;li>MethodStatComposite&lt;/li>
&lt;li>RtStatComposite&lt;/li>
&lt;/ul>
&lt;p>实际上,&lt;strong>元数据、注册中心指标收集器&lt;/strong>更新、添加指标的操作都是通过混合指标收集器暴露的方法进行。而具体的,是通过 &lt;code>setNum&lt;/code>、&lt;code>increment&lt;/code>、&lt;code>addRt&lt;/code> 这三个方法(及它们的重载)进行操作。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//CombMetricsCollector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> BaseStatComposite stats&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setNum&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> String applicationName&lt;span style="color:#719e07">,&lt;/span> String serviceKey&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> num&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>stats&lt;span style="color:#719e07">.&lt;/span>setServiceKey&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> applicationName&lt;span style="color:#719e07">,&lt;/span> serviceKey&lt;span style="color:#719e07">,&lt;/span> num&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">increment&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String applicationName&lt;span style="color:#719e07">,&lt;/span> MetricsKey metricsKey&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>stats&lt;span style="color:#719e07">.&lt;/span>incrementApp&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> applicationName&lt;span style="color:#719e07">,&lt;/span> SELF_INCREMENT_SIZE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">increment&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String applicationName&lt;span style="color:#719e07">,&lt;/span> String serviceKey&lt;span style="color:#719e07">,&lt;/span> MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> size&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>stats&lt;span style="color:#719e07">.&lt;/span>incrementServiceKey&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> applicationName&lt;span style="color:#719e07">,&lt;/span> serviceKey&lt;span style="color:#719e07">,&lt;/span> size&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addRt&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String applicationName&lt;span style="color:#719e07">,&lt;/span> String registryOpType&lt;span style="color:#719e07">,&lt;/span> Long responseTime&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stats&lt;span style="color:#719e07">.&lt;/span>calcApplicationRt&lt;span style="color:#719e07">(&lt;/span>applicationName&lt;span style="color:#719e07">,&lt;/span> registryOpType&lt;span style="color:#719e07">,&lt;/span> responseTime&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addRt&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String applicationName&lt;span style="color:#719e07">,&lt;/span> String serviceKey&lt;span style="color:#719e07">,&lt;/span> String registryOpType&lt;span style="color:#719e07">,&lt;/span> Long responseTime&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stats&lt;span style="color:#719e07">.&lt;/span>calcServiceKeyRt&lt;span style="color:#719e07">(&lt;/span>applicationName&lt;span style="color:#719e07">,&lt;/span> serviceKey&lt;span style="color:#719e07">,&lt;/span> registryOpType&lt;span style="color:#719e07">,&lt;/span> responseTime&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于几个方法实际上的调用链路类似,我们选择从其中的 setNum 方法开始分析。&lt;/p>
&lt;p>其在&lt;strong>数据聚合器层面的调用链路&lt;/strong>可以总结为:setNum 方法调用基本数据聚合器的 setServiceKey 方法,该方法又会调用服务数据聚合器(ServiceStatComposite)的同名 setServiceKey 方法(我们已经知道基本数据聚合器内封装了四个不同类型的子聚合器),这个方法实质上是&lt;strong>对应用层面的特定指标(由指标Key决定)进行注册并赋初始值(参数中的 num)。&lt;/strong>&lt;/p>
&lt;p>setNum 的用法均位于注册中心事件多播器(RegistryMetricsEventMulticaster)中声明的 MCat 接口中,在 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST 两个常量初始化时被调用。MCat 接口本身作为常量类使用,并在初始化时&lt;strong>注册真正的指标常量&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>MetricsCat APPLICATION_NOTIFY_FINISH &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetricsCat&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>NOTIFY_METRIC_NUM_LAST&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> placeType&lt;span style="color:#719e07">,&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onFinish&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>addRt&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> placeType&lt;span style="color:#719e07">.&lt;/span>getType&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getTimePair&lt;span style="color:#719e07">().&lt;/span>calc&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Integer&lt;span style="color:#719e07">&amp;gt;&lt;/span> lastNumMap &lt;span style="color:#719e07">=&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>unmodifiableMap&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>getAttachmentValue&lt;span style="color:#719e07">(&lt;/span>ATTACHMENT_KEY_LAST_NUM_MAP&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lastNumMap&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">.&lt;/span>setNum&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsCat APPLICATION_DIRECTORY_POST &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetricsCat&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>DIRECTORY_METRIC_NUM_VALID&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> placeType&lt;span style="color:#719e07">,&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onEvent&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsKey&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Integer&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> summaryMap &lt;span style="color:#719e07">=&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getAttachmentValue&lt;span style="color:#719e07">(&lt;/span>ATTACHMENT_DIRECTORY_MAP&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> summaryMap&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">((&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> map&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> map&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">.&lt;/span>setNum&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此处声明的指标常量都是 MetricsCat 类型的。其中部分常量在创建时还传入了该指标的收集逻辑,如Key 为 NOTIFY_METRIC_NUM_LAST 的常量。以下为 MetricsCat 的定义:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MetricsCat&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> MetricsPlaceType placeType&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Function&lt;span style="color:#719e07">&amp;lt;&lt;/span>CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;,&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> eventFunc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetricsCat&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> BiFunction&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsKey&lt;span style="color:#719e07">,&lt;/span> CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;,&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> biFunc&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>eventFunc &lt;span style="color:#719e07">=&lt;/span> collector &lt;span style="color:#719e07">-&amp;gt;&lt;/span> biFunc&lt;span style="color:#719e07">.&lt;/span>apply&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> collector&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetricsCat&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> TpFunction&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsKey&lt;span style="color:#719e07">,&lt;/span> MetricsPlaceType&lt;span style="color:#719e07">,&lt;/span> CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;,&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> tpFunc&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>eventFunc &lt;span style="color:#719e07">=&lt;/span> collector &lt;span style="color:#719e07">-&amp;gt;&lt;/span> tpFunc&lt;span style="color:#719e07">.&lt;/span>apply&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> placeType&lt;span style="color:#719e07">,&lt;/span> collector&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> MetricsCat &lt;span style="color:#268bd2">setPlaceType&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsPlaceType placeType&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>placeType &lt;span style="color:#719e07">=&lt;/span> placeType&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Function&lt;span style="color:#719e07">&amp;lt;&lt;/span>CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;,&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getEventFunc&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> eventFunc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一个接受三个入参,一个返回值的函数接口。通过构造函数我们可以知道这三个入参分别是MetricsKey, MetricsPlaceType, CombMetricsCollector&amp;lt;TimeCounterEvent&amp;gt;,返回值为AbstractMetricsListener。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">@FunctionalInterface&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">TpFunction&lt;/span>&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">,&lt;/span> U&lt;span style="color:#719e07">,&lt;/span> K&lt;span style="color:#719e07">,&lt;/span> R&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> R &lt;span style="color:#268bd2">apply&lt;/span>&lt;span style="color:#719e07">(&lt;/span>T t&lt;span style="color:#719e07">,&lt;/span> U u&lt;span style="color:#719e07">,&lt;/span> K k&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>MetricsCat 类除了构造器,只提供了两个public方法,都是获取其内部属性的。&lt;/p>
&lt;p>其实质上 eventFunc 字段的载体,提供了为特定指标生产监听器的逻辑,因此 &lt;code>MetricsCat &lt;/code> 可以看做&lt;strong>为特定指标生产指标监听器的工厂&lt;/strong>,用户在创建时传入这个监听器的处理逻辑。&lt;/p>
&lt;p>通过泛型,我们可以知道它构造时使用的两个参数分别为 MetricsKey(指标Key)和一个接受 &lt;code>MetricsKey, MetricsPlaceType, CombMetricsCollector&amp;lt;TimeCounterEvent&amp;gt;&lt;/code> 三个参数,返回一个 &lt;code>AbstractMetricsListener&lt;/code> 的函数。之所以要多封装一层函数,是因为 &lt;code>placeType&lt;/code> 字段在 &lt;code>MetricsKey&lt;/code> 实例构造之后才会提供,借此实现延迟初始化。&lt;/p>
&lt;p>回到之前两个&lt;strong>在 MCat 中定义了监听器生产方法&lt;/strong>的两个常量的初始化流程:它们在创建 MetricsCat 时传入的TpFunction中定义的操作为:返回通过 AbstractMetricsListener.onFinish获取的事件完成监听器。当指定MetricsKey 的指标统计事件完成时,这个监听器中的 onEventFinish 方法就会被调用。 而 MetricsCat 构造时传入的 MetricsKey 会被作为 AbstractMetricsListener 的构造参数,用于指定监听的指标。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">new&lt;/span> MetricsCat&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>NOTIFY_METRIC_NUM_LAST&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> placeType&lt;span style="color:#719e07">,&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onFinish&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>addRt&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> placeType&lt;span style="color:#719e07">.&lt;/span>getType&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getTimePair&lt;span style="color:#719e07">().&lt;/span>calc&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Integer&lt;span style="color:#719e07">&amp;gt;&lt;/span> lastNumMap &lt;span style="color:#719e07">=&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>unmodifiableMap&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>getAttachmentValue&lt;span style="color:#719e07">(&lt;/span>ATTACHMENT_KEY_LAST_NUM_MAP&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lastNumMap&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">.&lt;/span>setNum&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//AbstractMetricsListener
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> AbstractMetricsListener &lt;span style="color:#268bd2">onFinish&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> finishFunc&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onEventFinish&lt;/span>&lt;span style="color:#719e07">(&lt;/span>TimeCounterEvent event&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//此处是finishFunc就是之前 event -&amp;gt;{...} 中定义的lambda函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> finishFunc&lt;span style="color:#719e07">.&lt;/span>accept&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三个形参 (key, placeType, collector) 中的 collector 为 &lt;code>CombMetricsCollector&amp;lt;TimeCounterEvent&amp;gt;&lt;/code>,意味着它的三个实现(ConfigCenterMetricsCollector 、MetadataMetricsCollector、RegistryMetricsCollector)都可以作为参数。&lt;/p>
&lt;p>至此,我们可以总结,对于这两个参数, &lt;code>MetricsCat&lt;/code> 创建时嵌套的两层 lambda 函数最终是为了注册特定指标的监听器,并定义事件结束时的处理逻辑(内层的lambda)。在处理事件时,会调用混合指标收集器(CombMetricsCollector) 的 &lt;code>addRT&lt;/code> 方法添加响应时间计时,还会调用 &lt;code>setNum&lt;/code> 来添加指标计数。&lt;/p>
&lt;p>由于此处的 &lt;code>MetricsKey&lt;/code> 在 MetricsCat创建时就被传入,我们可以确定这两个字段存储了以下两个指标的统计逻辑:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>NOTIFY_METRIC_NUM_LAST:Last Notify Nums , 最后一个事件完成时的计数 。监听器中使用的是 &lt;code>setNum&lt;/code>,事件结束时直接更新指定key指标的计数为传入的值,同时使用 &lt;code>addRt &lt;/code> 来统计事件持续时长&lt;/p>
&lt;/li>
&lt;li>
&lt;p>DIRECTORY_METRIC_NUM_VALID:Valid Directory Urls,服务目录中注册成功的url数量。监听器中同样使用的是 &lt;code>setNum&lt;/code>,事件结束后直接更新为服务目录中的最新计数&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>之后,三个相关的 MetricsCat(指标类型) 实例会被绑定到一个 CategoryOverall(指标综合) 实例中,绑定的逻辑按一个事件进行的三个过程:&lt;strong>事件发生、事件结束、事件失败&lt;/strong>,分别对应 CategoryOverall 的第2、3、4个参数,其中事件发生时的逻辑不能为 null。而第一个参数为 &lt;code>MetricsPlaceType&lt;/code>,该参数封装了指标类型标识(如 register 服务注册、subscribe 服务订阅)和该指标的收集级别(应用还是服务)。&lt;/p>
&lt;p>还记得 &lt;code>MetricsCat&lt;/code> 中 &lt;code>TpFunction&lt;/code> 的三个入参吗?其中第二个 placeType 就是这个参数。 &lt;code>CategoryOverall&lt;/code> 在构造时会将它设置到其中的三个 MetricsCat 中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// CategorySet:常量接口,同样位于RegistryMetricsEventMulticaster中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">CategorySet&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CategoryOverall APPLICATION_NOTIFY &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CategoryOverall&lt;span style="color:#719e07">(&lt;/span>OP_TYPE_NOTIFY&lt;span style="color:#719e07">,&lt;/span> MCat&lt;span style="color:#719e07">.&lt;/span>APPLICATION_NOTIFY_POST&lt;span style="color:#719e07">,&lt;/span> MCat&lt;span style="color:#719e07">.&lt;/span>APPLICATION_NOTIFY_FINISH&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CategoryOverall SERVICE_DIRECTORY &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CategoryOverall&lt;span style="color:#719e07">(&lt;/span>OP_TYPE_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> MCat&lt;span style="color:#719e07">.&lt;/span>APPLICATION_DIRECTORY_POST&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CategoryOverall SERVICE_REGISTER &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CategoryOverall&lt;span style="color:#719e07">(&lt;/span>OP_TYPE_REGISTER_SERVICE&lt;span style="color:#719e07">,&lt;/span> MCat&lt;span style="color:#719e07">.&lt;/span>SERVICE_REGISTER_POST&lt;span style="color:#719e07">,&lt;/span> MCat&lt;span style="color:#719e07">.&lt;/span>SERVICE_REGISTER_FINISH&lt;span style="color:#719e07">,&lt;/span> MCat&lt;span style="color:#719e07">.&lt;/span>SERVICE_REGISTER_ERROR&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>CategoryOverall&lt;span style="color:#719e07">&amp;gt;&lt;/span> ALL &lt;span style="color:#719e07">=&lt;/span> Arrays&lt;span style="color:#719e07">.&lt;/span>asList&lt;span style="color:#719e07">(&lt;/span>APPLICATION_REGISTER&lt;span style="color:#719e07">,&lt;/span> APPLICATION_SUBSCRIBE&lt;span style="color:#719e07">,&lt;/span> APPLICATION_NOTIFY&lt;span style="color:#719e07">,&lt;/span> SERVICE_DIRECTORY&lt;span style="color:#719e07">,&lt;/span> SERVICE_REGISTER&lt;span style="color:#719e07">,&lt;/span> SERVICE_SUBSCRIBE&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>CategorySet&lt;/code> 中的常量都会被封装到List中,在 &lt;code>RegistryMetricsEventMulticaster&lt;/code> 创建时统一调用:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">RegistryMetricsCollector&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">RegistryMetricsEventMulticaster&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryMetricsCollector collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CategorySet&lt;span style="color:#719e07">.&lt;/span>ALL&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>categorySet &lt;span style="color:#719e07">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//通过 MetricsCat 实例中的定义的监听器创建逻辑,逐个注册监听器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>categorySet&lt;span style="color:#719e07">.&lt;/span>getPost&lt;span style="color:#719e07">().&lt;/span>getEventFunc&lt;span style="color:#719e07">().&lt;/span>apply&lt;span style="color:#719e07">(&lt;/span>collector&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>categorySet&lt;span style="color:#719e07">.&lt;/span>getFinish&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>categorySet&lt;span style="color:#719e07">.&lt;/span>getFinish&lt;span style="color:#719e07">().&lt;/span>getEventFunc&lt;span style="color:#719e07">().&lt;/span>apply&lt;span style="color:#719e07">(&lt;/span>collector&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>categorySet&lt;span style="color:#719e07">.&lt;/span>getError&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>categorySet&lt;span style="color:#719e07">.&lt;/span>getError&lt;span style="color:#719e07">().&lt;/span>getEventFunc&lt;span style="color:#719e07">().&lt;/span>apply&lt;span style="color:#719e07">(&lt;/span>collector&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>由此,我们也明确了 RegistryMetricsEventMulticaster (指标注册事件多播器)的作用&lt;/strong>:统一定义、管理事件,并在初始化时注册其中定义各种事件的&lt;strong>监听器&lt;/strong>。&lt;/p>
&lt;p>它继承了 SimpleMetricsEventMulticaster,其中的 publishEvent 方法在被调用时就会尝试调用所有监听器,判断其是否对当前事件类型感兴趣,选择是否进行调用。同时,这些监听器会对特定指标数据进行计算,更新到对应的收集器中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//SimpleMetricsEventMulticaster
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">publishEvent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsEvent event&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>event &lt;span style="color:#719e07">instanceof&lt;/span> EmptyEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>validateIfApplicationConfigExist&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetricsListener listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>listener&lt;span style="color:#719e07">.&lt;/span>isSupport&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listener&lt;span style="color:#719e07">.&lt;/span>onEvent&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们通过分析混合指标收集器(CombMetricsCollector) 中的 setNum 方法的用法,了解到了 Composite 中的数据来源之一是注册指标事件多播器(RegistryMetricsEventMulticaster)中为服务注册相关指标创建的指标监听器。实际上,increment、addRt方法都是由指标监听器的各个实现调用的。&lt;/p>
&lt;p>应用程序指标监听器(MetricsApplicationListener)中提供了 AbstractMetricsListener 的几个匿名实现,提供&lt;strong>应用层面事件发生、完成、抛出异常三种情况下对给定指标的计数或RT的计算&lt;/strong>,大多数用做处理应用层面指标事件的 MetricsListener 都是它提供的三个监听器实现:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MetricsApplicationListener&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> AbstractMetricsListener &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetricsApplicationListener&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//此处的Event均为TimeCounterEvent,在它被创建时就会自动开始计时
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> AbstractMetricsListener &lt;span style="color:#268bd2">onPostEventBuild&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onEvent&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">.&lt;/span>increment&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> metricsKey&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> AbstractMetricsListener &lt;span style="color:#268bd2">onFinishEventBuild&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> MetricsPlaceType placeType&lt;span style="color:#719e07">,&lt;/span> CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onFinish&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>increment&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> metricsKey&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>addRt&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> placeType&lt;span style="color:#719e07">.&lt;/span>getType&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getTimePair&lt;span style="color:#719e07">().&lt;/span>calc&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> AbstractMetricsListener &lt;span style="color:#268bd2">onErrorEventBuild&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsKey metricsKey&lt;span style="color:#719e07">,&lt;/span> MetricsPlaceType placeType&lt;span style="color:#719e07">,&lt;/span> CombMetricsCollector&lt;span style="color:#719e07">&amp;lt;&lt;/span>TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onError&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>increment&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> metricsKey&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>addRt&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> placeType&lt;span style="color:#719e07">.&lt;/span>getType&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getTimePair&lt;span style="color:#719e07">().&lt;/span>calc&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>还有 MetricsServiceListener(服务指标监听器),它和 MetricsApplicationListener 十分类似,提供的是服务层面的指标监听器的通用实现,不再重复分析。&lt;/p>
&lt;p>&lt;strong>可以用一句话简单的总结这三个 Collector 注册指标监听器的流程 : Collector 内部的 Mulicaster/Dispatcher 在被 Collector 创建时直接向自己注册已声明的指标监听器。&lt;/strong>&lt;/p>
&lt;p>至此,我们可以总结出 MetricsEvent 的部分消息转发路径 :&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/event-dispatch-simple.png" alt="event-dispatch-simple">&lt;/p>
&lt;h3 id="2dubbo-核心模块的指标采集流程">2,Dubbo 核心模块的指标采集流程&lt;/h3>
&lt;p>DefaultMetricsCollector(默认指标采集器) 作为指标采集器的默认实现,其主要通过采样器(Sampler)收集dubbo应用核心RPC功能的相关指标。 采样器包括以下几种:&lt;/p>
&lt;ul>
&lt;li>线程池线程状态(最大线程数、最小线程数、活跃线程数等),对应 &lt;strong>ThreadPoolMetricsSampler,线程池指标采样器&lt;/strong>&lt;/li>
&lt;li>线程池中线程耗尽事件的计数,对应 &lt;strong>ThreadRejectMetricsCountSampler, 线程耗尽次数采样器&lt;/strong>&lt;/li>
&lt;li>应用指标收集情况(收集次数),对应 DefaultMetricsCollector 中实现的 SimpleMetricsCountSampler 匿名子类&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>这些采样器内部会存储其负责采样类型指标的样本。由于默认指标采集器同样继承自 CombMetricsCollector,它也同时具有与前文中分析的三大中心指标收集器相似的指标转发流程。&lt;/strong>&lt;/p>
&lt;p>除了线程池指标采样器,其它两个采样器均实现自简单指标计数采样器(SimpleMetricsCountSampler)。它实现了通用的指标存取操作。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/sampler-struct.png" alt="sampler-struct">&lt;/p>
&lt;p>简单指标计数采样器内部的指标样本容器:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">abstract&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">SimpleMetricsCountSampler&lt;/span>&lt;span style="color:#719e07">&amp;lt;&lt;/span>S&lt;span style="color:#719e07">,&lt;/span> K&lt;span style="color:#719e07">,&lt;/span> M &lt;span style="color:#268bd2">extends&lt;/span> Metric&lt;span style="color:#719e07">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">implements&lt;/span> MetricsCountSampler&lt;span style="color:#719e07">&amp;lt;&lt;/span>S&lt;span style="color:#719e07">,&lt;/span> K&lt;span style="color:#719e07">,&lt;/span> M&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>K&lt;span style="color:#719e07">,&lt;/span> ConcurrentMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>M&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> metricCounter &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>其中:泛型 M 为指标类型,如方法指标 MethodMetric;泛型 K 为指标名称类型,如 String;泛型 S 为请求源类型,如 String 或 Invocation。请求源用于定位触发采样的请求来源,指标名称则用于对指标进行分组,便于按名称来分组检索指标数据。&lt;/strong>&lt;/p>
&lt;p>以及对特定指标的增减操作:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//SimpleMetricsCountSampler
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">inc&lt;/span>&lt;span style="color:#719e07">(&lt;/span>S source&lt;span style="color:#719e07">,&lt;/span> K metricName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExecute&lt;span style="color:#719e07">(&lt;/span>source&lt;span style="color:#719e07">,&lt;/span> metricName&lt;span style="color:#719e07">,&lt;/span> counter &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> counter&lt;span style="color:#719e07">.&lt;/span>incrementAndGet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">dec&lt;/span>&lt;span style="color:#719e07">(&lt;/span>S source&lt;span style="color:#719e07">,&lt;/span> K metricName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExecute&lt;span style="color:#719e07">(&lt;/span>source&lt;span style="color:#719e07">,&lt;/span> metricName&lt;span style="color:#719e07">,&lt;/span> counter &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> counter&lt;span style="color:#719e07">.&lt;/span>decrementAndGet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">incOnEvent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>S source&lt;span style="color:#719e07">,&lt;/span> K metricName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExecute&lt;span style="color:#719e07">(&lt;/span>source&lt;span style="color:#719e07">,&lt;/span> metricName&lt;span style="color:#719e07">,&lt;/span> counter &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> counter&lt;span style="color:#719e07">.&lt;/span>incrementAndGet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">decOnEvent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>S source&lt;span style="color:#719e07">,&lt;/span> K metricName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExecute&lt;span style="color:#719e07">(&lt;/span>source&lt;span style="color:#719e07">,&lt;/span> metricName&lt;span style="color:#719e07">,&lt;/span> counter &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> counter&lt;span style="color:#719e07">.&lt;/span>decrementAndGet&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对于四个增加、减少计数的方法,它们最终都会调用 &lt;code>doExecute&lt;/code> 方法来完成计数操作,其中 counter 函数定义了对计数器的操作(增加、减少)。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//SimpleMetricsCountSampler
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExecute&lt;/span>&lt;span style="color:#719e07">(&lt;/span>S source&lt;span style="color:#719e07">,&lt;/span> K metricsName&lt;span style="color:#719e07">,&lt;/span> Function&lt;span style="color:#719e07">&amp;lt;&lt;/span>AtomicLong&lt;span style="color:#719e07">,&lt;/span> Boolean&lt;span style="color:#719e07">&amp;gt;&lt;/span> counter&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsCountSampleConfigurer&lt;span style="color:#719e07">&amp;lt;&lt;/span>S&lt;span style="color:#719e07">,&lt;/span> K&lt;span style="color:#719e07">,&lt;/span> M&lt;span style="color:#719e07">&amp;gt;&lt;/span> sampleConfigure &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetricsCountSampleConfigurer&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sampleConfigure&lt;span style="color:#719e07">.&lt;/span>setSource&lt;span style="color:#719e07">(&lt;/span>source&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sampleConfigure&lt;span style="color:#719e07">.&lt;/span>setMetricsName&lt;span style="color:#719e07">(&lt;/span>metricsName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//利用子类重写的countConfigure为 sampleConfigure 设置事件发布函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>countConfigure&lt;span style="color:#719e07">(&lt;/span>sampleConfigure&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//通过指标名获取对应的指标计数器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>M&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> metricAtomic &lt;span style="color:#719e07">=&lt;/span> metricCounter&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>metricsName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metricAtomic &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metricAtomic &lt;span style="color:#719e07">=&lt;/span> metricCounter&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>metricsName&lt;span style="color:#719e07">,&lt;/span> k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Assert&lt;span style="color:#719e07">.&lt;/span>notNull&lt;span style="color:#719e07">(&lt;/span>sampleConfigure&lt;span style="color:#719e07">.&lt;/span>getMetric&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;metrics is null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AtomicLong atomicCounter &lt;span style="color:#719e07">=&lt;/span> metricAtomic&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>sampleConfigure&lt;span style="color:#719e07">.&lt;/span>getMetric&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>atomicCounter &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> atomicCounter &lt;span style="color:#719e07">=&lt;/span> metricAtomic&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>sampleConfigure&lt;span style="color:#719e07">.&lt;/span>getMetric&lt;span style="color:#719e07">(),&lt;/span> k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AtomicLong&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// counter函数定义了对atomicCounter的增减操作,如 inc方法定义的counter是对atomicCounter+1,dec方法定义的是对atomicCounter-1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Boolean isEvent &lt;span style="color:#719e07">=&lt;/span> counter&lt;span style="color:#719e07">.&lt;/span>apply&lt;span style="color:#719e07">(&lt;/span>atomicCounter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//如果本次计数操作应该触发事件...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取子类设置的事件发布函数,发布事件
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> sampleConfigure&lt;span style="color:#719e07">.&lt;/span>getFireEventHandler&lt;span style="color:#719e07">().&lt;/span>accept&lt;span style="color:#719e07">(&lt;/span>sampleConfigure&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>doExecute&lt;/code> 做了两件事:&lt;/p>
&lt;p>1,判断当前指标是否存在,如果不存在就放到容器中。&lt;/p>
&lt;p>2,调用提供的计数函数对指标进行修改,对应 counter 字段。&lt;/p>
&lt;p>以下为各采样器在 countConfigure 方法中提供的创建指标实例的逻辑:&lt;/p>
&lt;ul>
&lt;li>DefaultMetricsCollector 中 SimpleMetricsCountSampler 的匿名实现 (applicationSampler)提供的&lt;code>countConfigure&lt;/code> 方法:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">countConfigure&lt;/span>&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsCountSampleConfigurer&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> MetricsEvent&lt;span style="color:#719e07">.&lt;/span>Type&lt;span style="color:#719e07">,&lt;/span> ApplicationMetric&lt;span style="color:#719e07">&amp;gt;&lt;/span> sampleConfigure&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//提供根据 configure 创建指标实例的函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> sampleConfigure&lt;span style="color:#719e07">.&lt;/span>configureMetrics&lt;span style="color:#719e07">(&lt;/span>configure &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationMetric&lt;span style="color:#719e07">(&lt;/span>sampleConfigure&lt;span style="color:#719e07">.&lt;/span>getSource&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>ThreadRejectMetricsCountSampler 中提供的 &lt;code>countConfigure&lt;/code> 方法:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">countConfigure&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsCountSampleConfigurer&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">,&lt;/span> ThreadPoolRejectMetric&lt;span style="color:#719e07">&amp;gt;&lt;/span> sampleConfigure&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//提供根据 configure 创建指标实例的函数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> sampleConfigure&lt;span style="color:#719e07">.&lt;/span>configureMetrics&lt;span style="color:#719e07">(&lt;/span>configure &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ThreadPoolRejectMetric&lt;span style="color:#719e07">(&lt;/span>collector&lt;span style="color:#719e07">.&lt;/span>getApplicationName&lt;span style="color:#719e07">(),&lt;/span>configure&lt;span style="color:#719e07">.&lt;/span>getSource&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>默认指标收集器继承自 CombMetricsCollector,内部包含一个 DefaultSubDispatcher,因此它自身也可以作为指标事件的转发器,接受其它指标监听器的注册。&lt;/p>
&lt;p>在之前,我们发现了 AggregateMetricsCollector(聚合指标收集器)会将自己注册为 DefaultMetricsCollector 的监听器:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AggregateMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerListener&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">registerListener&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>DefaultMetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>还有 HistogramMetricsCollector (直方图指标收集器)也会将自己注册为它的监听器。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">HistogramMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registerListener&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">registerListener&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>DefaultMetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getEventMulticaster&lt;span style="color:#719e07">().&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>因此,聚合指标收集器和直方图指标收集器的指标事件来源于默认指标收集器转发的指标事件&lt;/strong>。通过默认指标转发器的 isSupport 方法,还可以发现这些指标事件的类型是 RequestEvent (RPC请求事件)或 RequestBeforeEvent(请求前失败事件:实际请求发送之前在 ClusterFilter 中产生的异常)。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isSupport&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsEvent event&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> event &lt;span style="color:#719e07">instanceof&lt;/span> RequestEvent &lt;span style="color:#719e07">||&lt;/span> event &lt;span style="color:#719e07">instanceof&lt;/span> RequestBeforeEvent&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/default-metrics-collector-struct.png" alt="default-metrics-collector-struct">&lt;/p>
&lt;p>&lt;strong>至此,我们也明确了 Dubbo 应用内部核心模块的相关指标是如何收集的:默认指标收集器除了接受上层指标转发器的指标事件之外,还会通过各种采样器对埋点采样,通过 SubDispatcher 统一转发指标事件,通知注册为它的监听器的其它 Collector 完成采样。&lt;/strong>&lt;/p>
&lt;h3 id="3-直方图相关指标的采集流程">3, 直方图相关指标的采集流程&lt;/h3>
&lt;p>直方图指标收集器(HistogramMetricsCollector)也是一个较为特殊的收集器,它主要负责RPC调用响应时间直方图指标这一种指标的收集。&lt;/p>
&lt;p>由于直方图指标收集器只需要采集单一类型的指标,它直接使用Map来存储采样数据,而非更复杂的数据聚合器(Composite)。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//HistogramMetricsCollector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> Timer&lt;span style="color:#719e07">&amp;gt;&lt;/span> rt &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>其中,key为方法指标,Timer则是该方法对应的RT计时器。该计时器由 micrometer 提供,在跟踪短时间内的大量事件时具有良好的性能。&lt;/strong>&lt;/p>
&lt;p>前文中已经提到,直方图指标收集器在初始化时会将自己注册为默认指标收集器(DefaultMetricsCollector)中的监听器,与聚合指标收集器相同(AggregateMetricsCollector)。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">registerListener&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>DefaultMetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getEventMulticaster&lt;span style="color:#719e07">().&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这意味着它接收的指标事件实际也来自于默认指标收集器中的采样器。之前的分析中,我们知道默认指标收集器目前实际只转发来自 MetricsDispatcher 的请求相关事件,因此直方图指标收集器也只会收集请求响应时间相关的指标采样。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//HistogramMetricsCollector
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onRTEvent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RequestEvent event&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metricRegister &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MethodMetric metric &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MethodMetric&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationName&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getAttachmentValue&lt;span style="color:#719e07">(&lt;/span>MetricsConstants&lt;span style="color:#719e07">.&lt;/span>INVOCATION&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> responseTime &lt;span style="color:#719e07">=&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getTimePair&lt;span style="color:#719e07">().&lt;/span>calc&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HistogramMetricSample sample &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HistogramMetricSample&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>METRIC_RT_HISTOGRAM&lt;span style="color:#719e07">.&lt;/span>getNameByType&lt;span style="color:#719e07">(&lt;/span>metric&lt;span style="color:#719e07">.&lt;/span>getSide&lt;span style="color:#719e07">()),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsKey&lt;span style="color:#719e07">.&lt;/span>METRIC_RT_HISTOGRAM&lt;span style="color:#719e07">.&lt;/span>getDescription&lt;span style="color:#719e07">(),&lt;/span> metric&lt;span style="color:#719e07">.&lt;/span>getTags&lt;span style="color:#719e07">(),&lt;/span> RT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Timer timer &lt;span style="color:#719e07">=&lt;/span> ConcurrentHashMapUtils&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>rt&lt;span style="color:#719e07">,&lt;/span> metric&lt;span style="color:#719e07">,&lt;/span> k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> metricRegister&lt;span style="color:#719e07">.&lt;/span>register&lt;span style="color:#719e07">(&lt;/span>sample&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timer&lt;span style="color:#719e07">.&lt;/span>record&lt;span style="color:#719e07">(&lt;/span>responseTime&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>MILLISECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当接收到事件时,直方图指标收集器会先计算当前调用花费的时间,然后为计时器(Time)添加一条响应时间记录。&lt;/p></description></item><item><title>Blog: 3-指标监听注册梳理</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</guid><description>
&lt;h2 id="三指标监听注册梳理">三、指标监听注册梳理&lt;/h2>
&lt;p>在前一章中,我们了解了不同收集器中的指标样本是如何被监听器添加进去的。接下来,我们将归纳指标监听器 的创建位置,及它们对应统计的指标。&lt;/p>
&lt;p>通过之前的分析,我们已经知道指标 注册事件多播器(RegistryMetricsEventMulticaster)中定义了并绑定了服务注册相关的指标。这种绑定操作同样存在于其它几个简单指标事件多播器(SimpleMetricsEventMulticaster)的几个实现中。&lt;/p>
&lt;h3 id="转发器注册">转发器注册&lt;/h3>
&lt;p>&lt;strong>RegistrySubDispatcher (服务注册指标转发器)注册了服务注册相关指标:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>应用级实例注册成功/失败/总数计数 (APPLICATION_REGISTER_&amp;hellip;)&lt;/li>
&lt;li>应用级服务接口订阅成功/失败/总数计数 (APPLICATION_SUBSCRIBE_&amp;hellip;)&lt;/li>
&lt;li>服务级注册成功/失败/总数计数 (SERVICE_REGISTER_&amp;hellip;)&lt;/li>
&lt;li>特殊的 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST (应用服务目录变化次数)&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>MetadataSubDispatcher(元数据指标转发器)注册应用元数据相关指标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>应用推送元数据相关计数 (APPLICATION_PUSH_&amp;hellip;)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>应用订阅元数据相关计数 (APPLICAITON_SUBSCRIBE_&amp;hellip;)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>服务订阅元数据相关计数 (SERVICE_SUBSCRIBE_&amp;hellip;)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>ConfigCenterSubDispatcher (配置中心指标转发器) 注册配置中心配置更新次数指标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>配置中心推送新配置次数 (CONFIGCENTER_METRIC_TOTAL)&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>DefaultSubDispatcher (默认转发器) 注册核心RPC调用次数指标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>请求次数 (METRIC_REQUESTS)&lt;/li>
&lt;li>请求成功次数(METRIC_REQUESTS_SUCCEED)&lt;/li>
&lt;li>请求失败次数(METRIC_REQUEST_BUSINESS_FAILED)&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>MetricsDispatcher&lt;/strong>&lt;/p>
&lt;p>MetricsDispatcher 较为特殊,它负责 ApplicationModel 下所有 MetricsCollector(前文中提到的指标收集器) 的初始化注册工作,并将它们添加到自己的监听器列表中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MetricsDispatcher&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> SimpleMetricsEventMulticaster &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@SuppressWarnings&lt;/span>&lt;span style="color:#719e07">({&lt;/span>&lt;span style="color:#2aa198">&amp;#34;rawtypes&amp;#34;&lt;/span>&lt;span style="color:#719e07">})&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetricsDispatcher&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ScopeBeanFactory beanFactory &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCollector&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>MetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>extensionLoader &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCollector&lt;span style="color:#719e07">&amp;gt;&lt;/span> customizeCollectors &lt;span style="color:#719e07">=&lt;/span> extensionLoader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getActivateExtensions&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetricsCollector customizeCollector &lt;span style="color:#719e07">:&lt;/span> customizeCollectors&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> beanFactory&lt;span style="color:#719e07">.&lt;/span>registerBean&lt;span style="color:#719e07">(&lt;/span>customizeCollector&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> customizeCollectors&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>addListener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要注意,以上几个实现均继承自 SimpleMetricsEventMulticaster,因此它们都具有注册监听、转发事件的能力。它们将自己注册到对应领域的指标 Collector 中,并在收到指标事件时转发到自己注册的监听器中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//SimpleMetricsEventMulticaster
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">addListener&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsListener&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> listener&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listeners&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>listener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">publishEvent&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetricsEvent event&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>event &lt;span style="color:#719e07">instanceof&lt;/span> EmptyEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>validateIfApplicationConfigExist&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetricsListener listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>listener&lt;span style="color:#719e07">.&lt;/span>isSupport&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listener&lt;span style="color:#719e07">.&lt;/span>onEvent&lt;span style="color:#719e07">(&lt;/span>event&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>SubDispatcher 和 Collector 之间的对应关系:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>MetadataSubDispatcher -&amp;gt; MetadataMetricsCollector 元数据指标事件&lt;/li>
&lt;li>RegistrySubDispatcher -&amp;gt; RegistryMetricsCollector 服务注册指标事件&lt;/li>
&lt;li>ConfigCenterSubDispatcher -&amp;gt; ConfigCenterMetricsCollector 配置中心指标事件&lt;/li>
&lt;li>MetricsDispatcher 由 MetricsEventBus 通过 BeanFactory 加载。它是所有事件转发的入口。&lt;/li>
&lt;/ul>
&lt;h3 id="事件触发">事件触发&lt;/h3>
&lt;p>剩下的问题就是这些监听器是如何被触发的。&lt;/p>
&lt;p>可以发现三大中心的指标转发器都是在它们对应的Collector中创建的:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">ConfigCenterMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setEventMulticaster&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ConfigCenterMetricsDispatcher&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetadataMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setEventMulticaster&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> MetadataMetricsEventMulticaster&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">RegistryMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">super&lt;/span>&lt;span style="color:#719e07">.&lt;/span>setEventMulticaster&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryMetricsEventMulticaster&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这意味这想要通过它们发布事件,需要通过它们对应的 &lt;code>Collector&lt;/code> 来访问。&lt;/p>
&lt;p>如前文所述, MetricsDispatcher 在初始化时会尝试获取并加载所有 MetricsCollector 的SPI拓展,&lt;/p>
&lt;p>三大中心的MetricsCollector (Metadata/Registry/ConfigCenter)也会在这里被初始化,并添加为 MetricsDispatcher 的监听器:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">MetricsDispatcher&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExtensionLoader&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCollector&lt;span style="color:#719e07">&amp;gt;&lt;/span> extensionLoader &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>MetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetricsCollector customizeCollector &lt;span style="color:#719e07">:&lt;/span> customizeCollectors&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> beanFactory&lt;span style="color:#719e07">.&lt;/span>registerBean&lt;span style="color:#719e07">(&lt;/span>customizeCollector&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> customizeCollectors&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">::&lt;/span>addListener&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对于 MetricsDispatcher,它由 MetricsEventBus 创建。而 MetricsEventBus 自身作为指标相关消息的总线,会接收所有指标消息,并将它们转发给监听者。&lt;/p>
&lt;p>MetricsEvenetBus 提供了三个方法来发布指标事件:&lt;/p>
&lt;ul>
&lt;li>&lt;code>publish(MetricsEvent event)&lt;/code> ,将事件发布给所有订阅者,只发布一次且不关心事件处理结果&lt;/li>
&lt;li>&lt;code>post(MetricsEvent event, Supplier&amp;lt;T&amp;gt; targetSupplier)&lt;/code> ,将事件发布给所有订阅者,并根据是否产生异常判断事件成功或失败,调用 MetricsDispatcher 发布对应的事件。&lt;/li>
&lt;li>&lt;code>post(MetricsEvent event, Supplier&amp;lt;T&amp;gt; targetSupplier, Function&amp;lt;T, Boolean&amp;gt; trFunction)&lt;/code> ,额外的 trFunction 可用于通过业务结果判断事件成功或失败。 &lt;code>targetSupplier&lt;/code> 为业务操作函数,泛型T为业务结果类型。&lt;/li>
&lt;/ul>
&lt;p>这三个方法均会通过 MetricsDispatcher 来转发事件。&lt;/p>
&lt;p>在之前的分析中,我们知道 MetricsDispatcher 创建了所有 MetricsCollector 拓展,并将它们注册为自己的监听者。&lt;/p>
&lt;p>因此,当 MetricsEventBus 接收到发布的信息时,它会将信息转发到所有 MetricsCollector 中。对于 CombMetricsCollector 的实现,它们又会调用自己创建的 MetricsEventMulticaster 再次转发消息,到具体指标的监听器。&lt;/p>
&lt;p>之后,这些监听器就会根据自己的逻辑修改Collector中的指标计数。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metris-event-dispatch-full.png" alt="image-20230629160012950">&lt;/p>
&lt;h3 id="事件发布">事件发布&lt;/h3>
&lt;p>接下来,我们将寻找指标事件发布的源头。&lt;/p>
&lt;p>通过前文的分析,我们知道 MetricsEventBus 是所有指标事件发布的入口。具体来说,它有以下的用法:&lt;/p>
&lt;ul>
&lt;li>AbstractDirectory&lt;/li>
&lt;li>ServiceConfig&lt;/li>
&lt;li>DefaultApplicationDeployer&lt;/li>
&lt;li>ApolloDynamicConfiguration&lt;/li>
&lt;li>NacosDynamicConfiguration&lt;/li>
&lt;li>ZookeeperDataListener&lt;/li>
&lt;li>AbstractMetadataReport&lt;/li>
&lt;/ul>
&lt;p>我们将逐个分析每个用法。&lt;/p>
&lt;p>&lt;strong>AbstractDirectory&lt;/strong>&lt;/p>
&lt;p>AbstractDirectory 在修改 Invoker 状态相关的操作完成后都会通过 MetricsEventBus 发布 refreshDirectoryEvent(服务目录更新事件,类型为 RegistryEvent ),将当前目录中各种状态 Invoker 实例的最新数量作为附件添加到 RegistryEvent 中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//AbstractDirectory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">recoverDisabledInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>RegistryEvent&lt;span style="color:#719e07">.&lt;/span>refreshDirectoryEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> getSummary&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setInvokers&lt;/span>&lt;span style="color:#719e07">(&lt;/span>BitList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> invokers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>RegistryEvent&lt;span style="color:#719e07">.&lt;/span>refreshDirectoryEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> getSummary&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">setInvokers&lt;/span>&lt;span style="color:#719e07">(&lt;/span>BitList&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> invokers&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>RegistryEvent&lt;span style="color:#719e07">.&lt;/span>refreshDirectoryEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> getSummary&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsKey&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Integer&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getSummary&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsKey&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Integer&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> summaryMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//目录中可用的Invoker数量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> summaryMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>DIRECTORY_METRIC_NUM_VALID&lt;span style="color:#719e07">,&lt;/span> groupByServiceKey&lt;span style="color:#719e07">(&lt;/span>getValidInvokers&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//目录中不可用的Invoker数量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> summaryMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>DIRECTORY_METRIC_NUM_DISABLE&lt;span style="color:#719e07">,&lt;/span> groupByServiceKey&lt;span style="color:#719e07">(&lt;/span>getDisabledInvokers&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//目录中等待重连的Invoker数量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> summaryMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>DIRECTORY_METRIC_NUM_TO_RECONNECT&lt;span style="color:#719e07">,&lt;/span> groupByServiceKey&lt;span style="color:#719e07">(&lt;/span>getInvokersToReconnect&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> summaryMap&lt;span style="color:#719e07">.&lt;/span>put&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>DIRECTORY_METRIC_NUM_ALL&lt;span style="color:#719e07">,&lt;/span> groupByServiceKey&lt;span style="color:#719e07">(&lt;/span>getInvokers&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> summaryMap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该事件最终会由 RegistryMetricsCollector 中的 RegistryMetricsDispatcher 转发到关系该事件的监听器中。&lt;strong>事件和监听器之间通过 MetricsKey匹配&lt;/strong> 。&lt;/p>
&lt;p>最终,MetricsKey 为 &lt;code>**DIRECTORY_METRIC_NUM_VALID**&lt;/code> 的监听器会处理这个事件,并更新 Collector中的计数。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//DIRECTORY_METRIC_NUM_VALID 对应的 Listener。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>MetricsCat APPLICATION_DIRECTORY_POST &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MetricsCat&lt;span style="color:#719e07">(&lt;/span>MetricsKey&lt;span style="color:#719e07">.&lt;/span>DIRECTORY_METRIC_NUM_VALID&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> placeType&lt;span style="color:#719e07">,&lt;/span> collector&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> AbstractMetricsListener&lt;span style="color:#719e07">.&lt;/span>onEvent&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> event &lt;span style="color:#719e07">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsKey&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Integer&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> summaryMap &lt;span style="color:#719e07">=&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getAttachmentValue&lt;span style="color:#719e07">(&lt;/span>ATTACHMENT_DIRECTORY_MAP&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> summaryMap&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">((&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> map&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> map&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> collector&lt;span style="color:#719e07">.&lt;/span>setNum&lt;span style="color:#719e07">(&lt;/span>metricsKey&lt;span style="color:#719e07">,&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>appName&lt;span style="color:#719e07">(),&lt;/span> k&lt;span style="color:#719e07">,&lt;/span> v&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样,服务目录中不同状态 Invoker 的计数就通过 RegistryMetricsCollector 更新到了 ServiceStatComposite 中。&lt;/p>
&lt;p>&lt;strong>ServiceConfig&lt;/strong>&lt;/p>
&lt;p>当通过 ServiceConfig 导出、注册一个服务时,它会发布一个服务导出事件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ServiceConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExport&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExportUrls&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exported&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doExportUrls&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>post&lt;span style="color:#719e07">(&lt;/span>RegistryEvent&lt;span style="color:#719e07">.&lt;/span>toRsEvent&lt;span style="color:#719e07">(&lt;/span>module&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">(),&lt;/span> getUniqueServiceName&lt;span style="color:#719e07">(),&lt;/span> protocols&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">*&lt;/span> registryURLs&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//该函数会被同步执行,如果抛出异常则触发 MetricsEvent 的 onError方法,否则触发 onFinish
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ProtocolConfig protocolConfig &lt;span style="color:#719e07">:&lt;/span> protocols&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String pathKey &lt;span style="color:#719e07">=&lt;/span> URL&lt;span style="color:#719e07">.&lt;/span>buildKey&lt;span style="color:#719e07">(&lt;/span>getContextPath&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>map&lt;span style="color:#719e07">(&lt;/span>p &lt;span style="color:#719e07">-&amp;gt;&lt;/span> p &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;/&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> path&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>orElse&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">),&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>serverService&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>pathKey&lt;span style="color:#719e07">,&lt;/span> interfaceClass&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doExportUrlsFor1Protocol&lt;span style="color:#719e07">(&lt;/span>protocolConfig&lt;span style="color:#719e07">,&lt;/span> registryURLs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> providerModel&lt;span style="color:#719e07">.&lt;/span>setServiceUrls&lt;span style="color:#719e07">(&lt;/span>urls&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>事件发布时,该事件会被转发到 RegistryMetricsCollector,触发对应的 Listener 增加 &lt;strong>SERVICE_REGISTER_METRIC_REQUESTS&lt;/strong> (当前服务级注册请求总数)的计数,然后执行定义的 provider 函数。根据是否抛出异常,之后执行 onError 方法 或 onFinish 方法,增加 &lt;strong>SERVICE_REGISTER_METRIC_REQUESTS_FAILED&lt;/strong> (当前服务级注册请求失败总数)或&lt;strong>SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED&lt;/strong> (当前服务级注册请求成功总数) 的计数。&lt;/p>
&lt;p>&lt;strong>DefaultApplicationDeployer&lt;/strong>&lt;/p>
&lt;p>它在应用部署过程中,初始化配置中心时,发布配置发生改变的事件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">startConfigCenter&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> compositeDynamicConfiguration&lt;span style="color:#719e07">.&lt;/span>addConfiguration&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prepareEnvironment&lt;span style="color:#719e07">(&lt;/span>configCenter&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> DynamicConfiguration &lt;span style="color:#268bd2">prepareEnvironment&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ConfigCenterConfig configCenter&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Add metrics
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>toChangeEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> configCenter&lt;span style="color:#719e07">.&lt;/span>getConfigFile&lt;span style="color:#719e07">(),&lt;/span> configCenter&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span>configCenter&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">(),&lt;/span> ConfigChangeType&lt;span style="color:#719e07">.&lt;/span>ADDED&lt;span style="color:#719e07">.&lt;/span>name&lt;span style="color:#719e07">(),&lt;/span> configMap&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>appGroup&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>toChangeEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> appConfigFile&lt;span style="color:#719e07">,&lt;/span> appGroup&lt;span style="color:#719e07">,&lt;/span> configCenter&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">(),&lt;/span> ConfigChangeType&lt;span style="color:#719e07">.&lt;/span>ADDED&lt;span style="color:#719e07">.&lt;/span>name&lt;span style="color:#719e07">(),&lt;/span> appConfigMap&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 &lt;code>prepareEnvironment&lt;/code> 方法中,会按配置中心设置的组(group)和当前应用程序的名称作为组名发布两次事件。该事件会被转发到 ConfigCenterMetricsCollector,增加 &lt;strong>CONFIGCENTER_METRIC_TOTAL&lt;/strong> (配置中心配置变化次数)的计数。&lt;/p>
&lt;p>&lt;strong>ApolloDynamicConfiguration&lt;/strong>&lt;/p>
&lt;p>动态配置功能的 Apollo 配置中心实现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ApolloDynamicConfiguration
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onChange&lt;/span>&lt;span style="color:#719e07">(&lt;/span>com&lt;span style="color:#719e07">.&lt;/span>ctrip&lt;span style="color:#719e07">.&lt;/span>framework&lt;span style="color:#719e07">.&lt;/span>apollo&lt;span style="color:#719e07">.&lt;/span>model&lt;span style="color:#719e07">.&lt;/span>ConfigChangeEvent changeEvent&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>toChangeEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span>ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>APOLLO_PROTOCOL&lt;span style="color:#719e07">,&lt;/span> ConfigChangeType&lt;span style="color:#719e07">.&lt;/span>ADDED&lt;span style="color:#719e07">.&lt;/span>name&lt;span style="color:#719e07">(),&lt;/span> SELF_INCREMENT_SIZE&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当Apollo配置中心的配置发生变化时,它的 &lt;code>onChange&lt;/code> 方法会被触发,并在最后发布一个 ConfigCenterEvent。该事件最终转发到ConfigCenterMetricsCollector 中,同样增加 **CONFIGCENTER_METRIC_TOTAL **的计数。&lt;/p>
&lt;p>&lt;strong>NacosDynamicConfiguration&lt;/strong>&lt;/p>
&lt;p>动态配置功能的 Nacos 配置中心实现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//NacosDynamicConfiguration
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">innerReceive&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String dataId&lt;span style="color:#719e07">,&lt;/span> String group&lt;span style="color:#719e07">,&lt;/span> String configInfo&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>toChangeEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">(),&lt;/span> event&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>NACOS_PROTOCOL&lt;span style="color:#719e07">,&lt;/span> ConfigChangeType&lt;span style="color:#719e07">.&lt;/span>ADDED&lt;span style="color:#719e07">.&lt;/span>name&lt;span style="color:#719e07">(),&lt;/span> SELF_INCREMENT_SIZE&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当Nacos配置中心的配置发生变化时,它的 &lt;code>innerReceive&lt;/code> 方法被触发,发布一个 ConfigCenterEvent。它的处理流程和 ApolloDynamicConfiguration 一致,最终增加 **CONFIGCENTER_METRIC_TOTAL **的计数。&lt;/p>
&lt;p>&lt;strong>ZookeeperDataListener&lt;/strong>&lt;/p>
&lt;p>动态配置功能的 Zookeeper 实现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//ZookeeperDataListener
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">dataChanged&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String path&lt;span style="color:#719e07">,&lt;/span> Object value&lt;span style="color:#719e07">,&lt;/span> EventType eventType&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>publish&lt;span style="color:#719e07">(&lt;/span>ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>toChangeEvent&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">,&lt;/span> configChangeEvent&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">(),&lt;/span> configChangeEvent&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigCenterEvent&lt;span style="color:#719e07">.&lt;/span>ZK_PROTOCOL&lt;span style="color:#719e07">,&lt;/span> ConfigChangeType&lt;span style="color:#719e07">.&lt;/span>ADDED&lt;span style="color:#719e07">.&lt;/span>name&lt;span style="color:#719e07">(),&lt;/span> SELF_INCREMENT_SIZE&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在指标收集层面,它的行为和前文中两个配置中心一致,此处不详细展开三个配置中心具体实现的异同。&lt;/p>
&lt;p>&lt;strong>AbstractMetadataReport&lt;/strong>&lt;/p>
&lt;p>元数据报告接口的抽象实现。它的三个实现 (NacosMetadataReport、RedisMetadataReport、ZookeeperMetadataReport)均使用它的指标事件逻辑。&lt;/p>
&lt;p>当订阅新服务并获取它的元数据时,它会发布一个 MetadataEvent 触发相关指标的修改。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">storeProviderMetadataTask&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MetadataIdentifier providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> ServiceDefinition serviceDefinition&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsEventBus&lt;span style="color:#719e07">.&lt;/span>post&lt;span style="color:#719e07">(&lt;/span>metadataEvent&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> doStoreProviderMetadata&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> saveProperties&lt;span style="color:#719e07">(&lt;/span>providerMetadataIdentifier&lt;span style="color:#719e07">,&lt;/span> data&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">!&lt;/span>syncReport&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span> aBoolean &lt;span style="color:#719e07">-&amp;gt;&lt;/span> aBoolean
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在前文中,我们提到 MetricsEventBus.post 的第二个参数是实际要进行的业务操作,第三个参数则是根据业务操作返回值判断操作是否成功的逻辑。&lt;/p>
&lt;p>此处的业务操作是尝试存储目标服务的元数据。执行操作之前,会先发布事件,最终增加 &lt;strong>STORE_PROVIDER_METADATA&lt;/strong> (尝试存储服务元数据次数)的计数。如果产生异常,会增加 &lt;strong>STORE_PROVIDER_METADATA_ERROR&lt;/strong> (存储服务元数据失败) 的计数,否则增加&lt;strong>STORE_PROVIDER_METADATA_SUCCEED&lt;/strong>(存储服务元数据成功) 的计数。&lt;/p></description></item><item><title>Blog: 4-指标转换与导出</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</guid><description>
&lt;h2 id="四指标转换与导出">四、指标转换与导出&lt;/h2>
&lt;p>本章主要梳理指标收集完成后,向外部收集器导出的流程。&lt;/p>
&lt;p>通过之前的分析,我们知道不同类型指标的收集分别由各个 Collector 实现进行。它们底层的 MetricsCollector 接口定义了指标导出的操作。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@SPI&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">MetricsCollector&lt;/span>&lt;span style="color:#719e07">&amp;lt;&lt;/span>E &lt;span style="color:#268bd2">extends&lt;/span> TimeCounterEvent&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">extends&lt;/span> MetricsLifeListener&lt;span style="color:#719e07">&amp;lt;&lt;/span>E&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">isCollectEnabled&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Collect metrics as {@link MetricSample}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return List of MetricSample
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricSample&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">collect&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>而指标报告器 (MetricsReporter) 的实现会定时调用Collector 的 &lt;code>collect&lt;/code> 方法,更新并导出指标数据。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">MetricsReporter&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新统计数据,定时调用collect()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshData&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取指标数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String &lt;span style="color:#268bd2">getResponse&lt;/span>&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取带指标名的指标样本(单个指标)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span> String &lt;span style="color:#268bd2">getResponseWithName&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String metricsName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>指标报告器有两个实现:DefaultMetricsReporter 和 PrometheusMetricsReporter,它们都实现自 AbstractMetricsRepoter,并使用它的指标刷新逻辑 (&lt;code>refreshData&lt;/code>方法)。&lt;/p>
&lt;p>AbstractMetricsRepoter 初始化时会获取并保存所有 Collector 的实现,每次刷新数据,调用&lt;code>refreshData&lt;/code>方法时都会遍历这些收集器,更新指标数据。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//AbstractMetricsRepoter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">initCollectors&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCollector&lt;span style="color:#719e07">&amp;gt;&lt;/span> otherCollectors &lt;span style="color:#719e07">=&lt;/span> beanFactory&lt;span style="color:#719e07">.&lt;/span>getBeansOfType&lt;span style="color:#719e07">(&lt;/span>MetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collectors&lt;span style="color:#719e07">.&lt;/span>addAll&lt;span style="color:#719e07">(&lt;/span>otherCollectors&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshData&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collectors&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>collector &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricSample&lt;span style="color:#719e07">&amp;gt;&lt;/span> samples &lt;span style="color:#719e07">=&lt;/span> collector&lt;span style="color:#719e07">.&lt;/span>collect&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>MetricSample sample &lt;span style="color:#719e07">:&lt;/span> samples&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将Dubbo的度量类型适配为micrometer的度量类型,并将其添加到CompositeMeterRegistry中,借此实现多监控系统的支持。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DefaultMetricsReporter 和 PrometheusMetricsReporter 各自实现了自己的指标采样逻辑 (&lt;code>getResponse&lt;/code>方法)。&lt;/p>
&lt;p>&lt;strong>PrometheusMetricsReporter&lt;/strong>&lt;/p>
&lt;p>它通过 PrometheusMeterRegistry 获取普罗米修斯支持格式的样本数据:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//PrometheusMetricsReporter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">getResponse&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> prometheusRegistry&lt;span style="color:#719e07">.&lt;/span>scrape&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在初始化时,它会开启一个定时任务,定时向普罗米修斯服务端推送采样数据:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//PrometheusMetricsReporter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SimpleMeterRegistry meterRegistry &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> SimpleMeterRegistry&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doInit&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addMeterRegistry&lt;span style="color:#719e07">(&lt;/span>prometheusRegistry&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> schedulePushJob&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">schedulePushJob&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里的URL是DefaultMetricsReporter中定义的指标报告URL,提供了指标服务的具体地址
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> pushEnabled &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_ENABLED_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>pushEnabled&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pushJobExecutor&lt;span style="color:#719e07">.&lt;/span>scheduleWithFixedDelay&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> push&lt;span style="color:#719e07">(&lt;/span>pushGateway&lt;span style="color:#719e07">,&lt;/span> job&lt;span style="color:#719e07">),&lt;/span> pushInterval&lt;span style="color:#719e07">,&lt;/span> pushInterval&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>SECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">push&lt;/span>&lt;span style="color:#719e07">(&lt;/span>PushGateway pushGateway&lt;span style="color:#719e07">,&lt;/span> String job&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> refreshData&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//将本次采样数据添加到pushGateway,等待下次抓取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> pushGateway&lt;span style="color:#719e07">.&lt;/span>pushAdd&lt;span style="color:#719e07">(&lt;/span>prometheusRegistry&lt;span style="color:#719e07">.&lt;/span>getPrometheusRegistry&lt;span style="color:#719e07">(),&lt;/span> job&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>PushGateway 和 PrometheusRegistry 均为 micrometer 提供的 API。&lt;/p>
&lt;p>&lt;strong>PrometheusRegistry 将度量数据转换为普罗米修斯支持的格式,而 PushGateway 存储样本,暴露一个HTTP端点供普罗米修斯服务端抓取。&lt;/strong>&lt;/p>
&lt;p>PushGateway 本身只是一个度量数据的缓存区,普罗米修斯服务端每从其中抓取一次数据,其内部的样本就会被清除。&lt;/p>
&lt;p>&lt;strong>DefaultMetricsReporter&lt;/strong>&lt;/p>
&lt;p>指标报告器的默认实现,提供了按指标名称导出特定指标的方法。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//DefaultMetricsReporter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">getResponse&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">getResponseWithName&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String metricsName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> meterRegistry&lt;span style="color:#719e07">.&lt;/span>getMeters&lt;span style="color:#719e07">().&lt;/span>stream&lt;span style="color:#719e07">().&lt;/span>filter&lt;span style="color:#719e07">(&lt;/span>meter &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//根据名称过滤样本
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metricsValue&lt;span style="color:#719e07">.&lt;/span>forEach&lt;span style="color:#719e07">((&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> value&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//按格式拼装结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> sb&lt;span style="color:#719e07">.&lt;/span>toString&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doInit&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> addMeterRegistry&lt;span style="color:#719e07">(&lt;/span>meterRegistry&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doDestroy&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该实现使用的 SimpleMeterRegistry 本身只会存储指标数据的功能,而不像 PrometheusMeterRegistry 那样提供发布数据的方法。&lt;/p>
&lt;p>&lt;strong>因此该指标报告器不会主动向外部发布数据,而是被动的通过 &lt;code>getResponseWithName&lt;/code> 提供数据。&lt;/strong>&lt;/p>
&lt;p>而且,该报告器在任何情况下都会被初始化:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//DefaultApplicationDeployer#initMetricsReporter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//If the protocol is not the default protocol, the default protocol is also initialized.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>PROTOCOL_DEFAULT&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>metricsConfig&lt;span style="color:#719e07">.&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DefaultMetricsReporterFactory defaultMetricsReporterFactory &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> DefaultMetricsReporterFactory&lt;span style="color:#719e07">(&lt;/span>applicationModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsReporter defaultMetricsReporter &lt;span style="color:#719e07">=&lt;/span> defaultMetricsReporterFactory&lt;span style="color:#719e07">.&lt;/span>createMetricsReporter&lt;span style="color:#719e07">(&lt;/span>metricsConfig&lt;span style="color:#719e07">.&lt;/span>toUrl&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defaultMetricsReporter&lt;span style="color:#719e07">.&lt;/span>init&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>registerBean&lt;span style="color:#719e07">(&lt;/span>defaultMetricsReporter&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>实际上,该类主要在用户使用Qos命令查询时提供指定指标数据,而非提供给某个特定的外部指标中心。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//Qos命令
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DefaultMetricsReporterCmd&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> BaseCommand &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> String &lt;span style="color:#268bd2">getResponseByApplication&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">,&lt;/span> String metricsName&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String response &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;DefaultMetricsReporter not init&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsReporter metricsReporter &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>DefaultMetricsReporter&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>metricsReporter &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metricsReporter&lt;span style="color:#719e07">.&lt;/span>refreshData&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取指定名称指标的数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> response &lt;span style="color:#719e07">=&lt;/span> metricsReporter&lt;span style="color:#719e07">.&lt;/span>getResponseWithName&lt;span style="color:#719e07">(&lt;/span>metricsName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> response&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>*需要注意的是,PrometheusMetricsReporter 也支持使用 Qos 命令查询内部指标数据,同样有对应的 PrometheusMetricsReporterCmd 实现,它们的工作原理相似。&lt;/p>
&lt;p>以上就是指标样本从收集完成到最终导出到外部指标中心的大致流程。&lt;/p></description></item><item><title>Blog: 精进云原生 - Dubbo 3.2 正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/15/%E7%B2%BE%E8%BF%9B%E4%BA%91%E5%8E%9F%E7%94%9F-dubbo-3.2-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Sat, 15 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/15/%E7%B2%BE%E8%BF%9B%E4%BA%91%E5%8E%9F%E7%94%9F-dubbo-3.2-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;h2 id="背景介绍">背景介绍&lt;/h2>
&lt;p>Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。&lt;/p>
&lt;h2 id="rest-协议支持">Rest 协议支持&lt;/h2>
&lt;h3 id="1-why-rest">1. Why Rest?&lt;/h3>
&lt;p>随着移动互联网的普及,越来越多的应用程序需要与不同的系统进行集成。而这些系统可能使用不同的通信协议,这就需要应用程序能够灵活地适应各种协议。Rest 协议正是一种非常灵活的协议,它使用 HTTP 进行通信,可以与几乎任何系统进行集成。&lt;/p>
&lt;p>在过去,RPC框架通常使用二进制协议进行通信,这种协议非常高效,但不够灵活。相比之下,Rest协议使用HTTP进行通信,更方便与其他系统集成,也更容易与现代化的Web和移动应用程序集成。&lt;/p>
&lt;p>除了灵活性,Rest协议还具有易读性和易用性。使用Rest协议,开发人员可以使用通用的HTTP工具(例如cURL或Postman)测试和调试服务,而不需要特定的工具。此外,由于Rest协议使用标准的HTTP方法(例如GET、POST、PUT和DELETE),因此开发人员可以更容易地理解和使用服务。&lt;/p>
&lt;h3 id="2-how-to">2. How To?&lt;/h3>
&lt;p>在之前的 Dubbo 版本中,也提供了 Rest 协议的支持,但存在以下问题:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>仅支持 JAX-RS 注解域,相较于采用度更高的 Spring Web 注解,复杂度更高&lt;/p>
&lt;/li>
&lt;li>
&lt;p>需要依赖众多外部组件,如 Resteasy、tomcat、jetty 等,才能正常工作,极大地增加了使用成本。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>因此,在 Dubbo 3.2 版本中,我们引入了 Spring Web 注解域的支持以及 Rest 协议的原生支持,无需依赖任何外部组件。&lt;/p>
&lt;p>最直观的区别是,如果你升级到了 Dubbo 3.2,通过 Spring Web 发布的服务也可以直接通过 Dubbo 来发布。这一切只需要将 @Controller 注解改成 @DubboService 注解即可。&lt;/p>
&lt;p>此外,对于原来使用 Spring Boot 或者 Spring Cloud 作为服务拆分的用户,也可以基于本功能平滑地迁移到 Dubbo 上来,以极低的成本获得 Dubbo 强大的能力。&lt;/p>
&lt;h3 id="3-whats-next">3. What&amp;rsquo;s next?&lt;/h3>
&lt;p>接下来 Dubbo 还将继续完善。除了现有的特性,我们还将加入以下新的特性,以更好地满足需求:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>HTTP/2、HTTP/3 协议的原生支持。这意味着,你可以更加方便地使用 Dubbo 与其他系统进行通信,无需担心协议的兼容性问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>参考 Spring Web 注解,Dubbo 原生提供 Web 注解的支持,使得用户无需依赖 Spring Web 也可以获得与使用 Spring Web 相同的体验。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持现有服务零改造以 Rest 协议发布。这个特性可以让你更加灵活地管理你的服务,而无需对现有的服务进行任何改动。你可以通过 Rest 协议来发布你的服务,这样你的服务就可以更加方便地被其他系统所使用了。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="可观测体系">可观测体系&lt;/h2>
&lt;p>在微服务架构下,业务系统由越来越多的服务组成,服务之间互相调用,随之而来的问题是如何快速地定位故障,并及时解决。为了解决这一问题,我们需要更多的工具和技术来确保整个系统的可靠性。其中一个解决方案是使用日志记录和分析,以便可以更好地跟踪应用程序的运行情况,找到潜在的问题并及时解决。另外,使用可视化的监控工具可以帮助我们更好地理解整个系统的状态,从而更好地预测和解决问题。最后,我们还可以使用自动化测试来确保每个服务的质量,以及整个系统的稳定性和可靠性,从而更好地满足客户的需求。&lt;/p>
&lt;p>一套完整的可观测体系应该包括以下功能:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Metrics 指标监控,用于收集和分析各种指标数据,包括系统的性能、资源消耗情况等等。通过指标监控,用户可以及时了解系统的运行情况,发现异常并做出相应的处理。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Tracing 分布式追踪,用于跟踪系统中各个服务之间的调用链路,帮助用户发现和定位潜在的性能问题、瓶颈等等。通过分布式追踪,用户可以深入了解系统的运作过程,识别出可能存在的问题并进行有效的优化和调整。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Logging 日志管理,用于记录系统中发生的各种事件和操作,包括错误日志、访问日志、事务日志等等。通过日志管理,用户可以了解系统的运行情况、故障信息等等,帮助用户快速定位问题并进行相应的处理。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>综上所述,以上三个功能不仅可以帮助用户快速定位故障,提高系统的可靠性和稳定性,还可以帮助用户深入了解系统的运行情况和性能状况,为用户提供全方位的监控和保障。&lt;/p>
&lt;p>在 Dubbo 3.2 版本中,我们主要就 Metrics 和 Tracing 两个方面进行了增强。&lt;/p>
&lt;h3 id="1-metrics">1. Metrics&lt;/h3>
&lt;p>在 Metrics 方面,我们使用 Micrometer 大幅增加了指标的埋点,包括但不限于 QPS、RT、调用总数、成功数、失败数、失败原因统计等核心服务指标。为了更好地监测服务运行状态,Dubbo 还提供了对核心组件状态的监控,例如线程池数量、服务健康状态等。此外,Dubbo 还支持标准 Prometheus 的 Pull 和 Push 模式,并提供了若干个官方原生的 Grafana 面板,实现面向生产的 Metrics 全天候观测。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/32-release/Untitled.png" alt="img">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/32-release/Untitled%201.png" alt="Untitled">&lt;/p>
&lt;p>对于所有的用户,只需要升级到 Dubbo 3.2 版本,并添加 dubbo-spring-boot-observability-starter 依赖即可获得 Metrics 能力。在应用启动后,将在 Dubbo QoS 的 metrics 命令下暴露相关的指标,本地可以通过 &lt;code>http://127.0.0.1:22222/metrics&lt;/code> 获取。此外对于使用了 Spring Actuator 的用户,Dubbo 也将默认将这些数据暴露出来。&lt;/p>
&lt;h3 id="tracing">Tracing&lt;/h3>
&lt;p>在 Tracing 方面,我们还基于 Micrometer 实现了请求运行时的埋点跟踪。我们通过 Filter 拦截器原生方式来实现这一功能。我们支持将跟踪数据导出到一些主流实现,例如 Zipkin、Skywalking、Jaeger 等。这样就可以实现全链路跟踪数据的分析和可视化展示。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/32-release/Untitled%202.png" alt="Untitled">&lt;/p>
&lt;h3 id="logging">Logging&lt;/h3>
&lt;p>此外,对于 Logging 方面,Dubbo 从 3.1 版本开始引入了错误码机制,实现了 WARN、ERROR 级别日志的全覆盖。在异常场景下,支持快速索引官网解决文档。&lt;/p>
&lt;h2 id="native-image-原生支持">Native Image 原生支持&lt;/h2>
&lt;p>在Native Image方面,Dubbo从3.2开始将正式基于GraalVM完成对Native Image 的支持,从Dubbo3.0开始,Dubbo已经有一些Native Image支持的探索,但是易用性和支持程度都不太理想,从3.2版本开始,Dubbo将会简化用户接入Native Image的使用方式。主要可以分为三个面:&lt;/p>
&lt;ol>
&lt;li>编译插件配置升级:从最初的 native-image-maven-plugin 改为 dubbo-maven-plugin +native-maven-plugin,区分了Graalvm官方提供的native image配置与Dubbo所需的native image配置,简化了用户所需要关心的native image配置&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.graalvm.nativeimage&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>native-image-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>21.0.0.2&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>native-image&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;phase&amp;gt;&lt;/span>package&lt;span style="color:#268bd2">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;skip&amp;gt;&lt;/span>false&lt;span style="color:#268bd2">&amp;lt;/skip&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;imageName&amp;gt;&lt;/span>demo-native-provider&lt;span style="color:#268bd2">&amp;lt;/imageName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;mainClass&amp;gt;&lt;/span>org.apache.dubbo.demo.graalvm.provider.Application&lt;span style="color:#268bd2">&amp;lt;/mainClass&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;buildArgs&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --no-fallback
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.slf4j.MDC
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.slf4j.LoggerFactory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.slf4j.impl.StaticLoggerBinder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.apache.log4j.helpers.Loader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.apache.log4j.Logger
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.apache.log4j.helpers.LogLog
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.apache.log4j.LogManager
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.apache.log4j.spi.LoggingEvent
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.slf4j.impl.Log4jLoggerFactory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.slf4j.impl.Log4jLoggerAdapter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-build-time=org.eclipse.collections.api.factory.Sets
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.epoll.Epoll
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.epoll.Native
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.epoll.EpollEventArray
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.DefaultFileRegion
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.kqueue.KQueueEventArray
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.kqueue.KQueueEventLoop
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.kqueue.Native
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.unix.Errors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.unix.IovArray
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.unix.Limits
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.util.internal.logging.Log4JLogger
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.unix.Socket
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --initialize-at-run-time=io.netty.channel.ChannelHandlerMask
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --report-unsupported-elements-at-runtime
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --allow-incomplete-classpath
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> --enable-url-protocols=http
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -H:+ReportExceptionStackTraces
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/buildArgs&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>变为:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.graalvm.buildtools&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>native-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>0.9.20&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;classesDirectory&amp;gt;&lt;/span>${project.build.outputDirectory}&lt;span style="color:#268bd2">&amp;lt;/classesDirectory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;metadataRepository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;enabled&amp;gt;&lt;/span>true&lt;span style="color:#268bd2">&amp;lt;/enabled&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/metadataRepository&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;requiredVersion&amp;gt;&lt;/span>22.3&lt;span style="color:#268bd2">&amp;lt;/requiredVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;mainClass&amp;gt;&lt;/span>org.apache.dubbo.demo.graalvm.provider.Application&lt;span style="color:#268bd2">&amp;lt;/mainClass&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo-maven-plugin&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>${project.version}&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;mainClass&amp;gt;&lt;/span>org.apache.dubbo.demo.graalvm.provider.Application&lt;span style="color:#268bd2">&amp;lt;/mainClass&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;phase&amp;gt;&lt;/span>process-sources&lt;span style="color:#268bd2">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;goal&amp;gt;&lt;/span>dubbo-process-aot&lt;span style="color:#268bd2">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>旧版本中需要用户手动生成和补全Dubbo内独有的Adaptive代码,新版本用户将不需要关心这些细节。&lt;/li>
&lt;li>旧版本中Dubbo框架生成的META-INF.native-image下的配置文件会直接生成在用户的工程目录中,3.2新版本将会被编译到target下,不影响用户的工程结构。除此之外,Dubbo框架也将不再采用手动补全native image的方式,而且采用自动探测和生成所需的配置文件的方式,简化了Dubbo开发者的体验。这也能够降低最后编译后的二进制包的大小和提高编译速度。&lt;/li>
&lt;/ol>
&lt;p>除了易用性提升以外,Dubbo将在3.2版本将在native image场景下支持API、注解以及XML配置方式,并支持与SpringBoot3中的native兼容。&lt;/p>
&lt;h2 id="其他">其他&lt;/h2>
&lt;h3 id="jdk-17--spring-boot-3-原生支持">JDK 17 &amp;amp; Spring Boot 3 原生支持&lt;/h3>
&lt;p>JDK 17 是继 JDK 11 之后目前 Java 的最新 LTS 版本,包括许多新功能和改进,例如 Sealed 类、垃圾收集器的改进等等。&lt;/p>
&lt;p>自从 JDK 16 开始限制 Java 内部类反射以后,Dubbo 的序列化以及动态代理都受到了一定的影响。在 Dubbo 3.2 中,我们通过 Fastjson2 以及 Javassist 的优化从底层解决了兼容性问题。目前 Dubbo 已经可以完美运行在 JDK17 之上,所有单元测试以及大多数集成测试也都在 JDK 17 平台上测试通过。&lt;/p>
&lt;p>针对即将发布的 JDK 21 LTS,Dubbo 正在紧锣密鼓地进行适配。我们将在 3.3 版本中加入对 JDK 21 和 Dubbo 协程(Project Loom)的支持。&lt;/p>
&lt;h3 id="rpc-性能大幅提升">RPC 性能大幅提升&lt;/h3>
&lt;p>在3.2版本中,我们对RPC调用性能做了调优,其中优化内容如下。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>消除了同步锁竞争以及会出现阻塞的代码(&lt;code>triple&lt;/code>)&lt;/p>
&lt;ol>
&lt;li>在3.1版本中创建HTTP/2 Stream Channel时采用了同步阻塞用户线程的方式等待Stream Channel创建完成,创建完成后才开始发起远程调用。而在3.2中我们将创建HTTP/2 Stream Channel的行为&lt;code>异步化&lt;/code>并保证创建完毕后才发起请求,以此&lt;code>减少了用户线程不必要的等待&lt;/code>。&lt;/li>
&lt;li>在3.1版本中用户线程与Netty的I/O线程出现了同步锁竞争,IO线程每次写请求都会检查Socket可用性,而用户线程中也使用了Socket可用性检查的方法,但JDK中Socket可用性检查的实现使用了 &lt;code>synchronized&lt;/code> 来保证并发安全,为了减少这部分的耗时我们将用户线程的检查移除,消除了该部分的耗时。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>减少了用同步阻塞调用方式的请求响应延迟(&lt;code>dubbo&lt;/code>、&lt;code>triple&lt;/code>)&lt;/p>
&lt;p>在3.1版本中SYNC模式下的RPC调用我们使用了阻塞队列的方式等待远程服务写回的响应,当响应写回后会添加到队列中并唤醒被阻塞的用户线程。而在3.2中我们将阻塞队列更换为并发队列,利用其CAS的机制大幅度减少线程进入阻塞的次数,提高CPU利用率并降低了响应处理延迟&lt;/p>
&lt;/li>
&lt;li>
&lt;p>减少了线程切换的次数(&lt;code>triple&lt;/code>)&lt;/p>
&lt;p>在3.1版本中SYNC模式下的RPC调用在接收响应时使用了一个消费者线程池进行处理,处理完毕后才唤醒用户线程接收响应。但通过分析SYNC模式下的消费者线程池是不必要的,多了一层消费者线程池处理不仅浪费服务器资源还降低了性能,因此我们在3.2版本中将SYNC模式下消费者线程池移除,交互模型由 &lt;code>NettyEventLoop → ConsumerThread → UserThread&lt;/code>变成了&lt;code>NettyEventLoop → UserThread&lt;/code>,以此减少服务器资源的浪费同时提高了性能&lt;/p>
&lt;/li>
&lt;li>
&lt;p>优化了I/O性能(&lt;code>dubbo&lt;/code>、&lt;code>triple&lt;/code>)&lt;/p>
&lt;p>在3.1版本中我们利用了Netty框架实现了网络通讯,但每次往对端写消息时都直接刷写到对端导致系统调用次数极高,降低了通讯性能。为此我们在3.2版本中对该进行了优化,每次发消息时是先将消息提交到一个写队列中,并在合适的时机将多个消息一次性写出,以此提高了I/O利用率,大幅度提高RPC通讯性能。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持在用户线程上序列化报文(&lt;code>dubbo&lt;/code>、&lt;code>triple&lt;/code>)&lt;/p>
&lt;p>在3.1版本中RPC通讯中的报文反序列化均是在单一I/O线程中串行执行的,导致无法利用多核CPU的优势。为此我们在3.2版本中支持了在用户线程上执行反序列化这类较为耗时的任务,将I/O线程的压力均分到多个CPU核心上,以此提高了&lt;code>较大报文&lt;/code>场景下的RPC性能。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>3.2对比3.1的性能提升如下:&lt;/p>
&lt;p>Triple协议:较小报文场景createUser、existUser、getUser下,提升率约在&lt;code>40-45%&lt;/code>,提升后的性能与gRPC同场景的性能基本持平。较大报文场景listUser下提升了约&lt;code>17%&lt;/code>,相较于同场景下的gRPC还低&lt;code>11%&lt;/code>。&lt;/p>
&lt;p>Dubbo协议:较小报文场景createUser、getUser下,提升率约&lt;code>180%&lt;/code>。极小报文existUser(仅一个boolean值)下提升率约&lt;code>24%&lt;/code>,而较大报文listUser提升率最高,达到了&lt;code>1000%&lt;/code>!&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/32-release/Untitled%203.png" alt="Untitled">&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/32-release/Untitled%204.png" alt="Untitled">&lt;/p>
&lt;h2 id="如何升级到-dubbo-32">如何升级到 Dubbo 3.2&lt;/h2>
&lt;h3 id="pom-升级">Pom 升级&lt;/h3>
&lt;p>最新的 Dubbo Maven 坐标为:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.dubbo&lt;span style="color:#268bd2">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;artifactId&amp;gt;&lt;/span>dubbo&lt;span style="color:#268bd2">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;version&amp;gt;&lt;/span>3.2.0&lt;span style="color:#268bd2">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="兼容性">兼容性&lt;/h3>
&lt;p>对于绝大多数的用户,升级到 Dubbo 3.2.0 是完全平滑的,仅需要修改依赖包版本即可。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>序列化校验逻辑的增强(&lt;strong>重要&lt;/strong>)&lt;/p>
&lt;p>如前文所述,在 Dubbo 3.2.0 版本中,Dubbo 将默认开启序列化白名单的强校验,以提升 Dubbo 的安全性,避免远程命令执行的问题。目前的机制通过包名递归机制自动信任了部分类,但对于一些使用了泛型等可能存在扫描不全的用户,我们建议您先升级到 Dubbo 3.1.9 版本或添加 &lt;code>-Ddubbo.application.serialize-check-status=WARN&lt;/code> 配置。观察一段时间后(通过日志、QoS 命令),如果没有触发安全告警,则可以配置强校验模式。&lt;/p>
&lt;p>关于自定义白名单的配置,可以参考官网的 &lt;code>文档 / SDK 手册 / Java SDK / 高级特性和用法 / 提升安全性 / 类检查机制&lt;/code> 一文进行配置。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>默认序列化的修改&lt;/p>
&lt;p>Dubbo 3.2.0 版本开始默认序列化方式从 &lt;code>hessian2&lt;/code> 切换为 &lt;code>fastjson2&lt;/code>,对于升级到 3.2.0 的应用,Dubbo 会自动尝试采用 &lt;code>fastjson2&lt;/code> 进行序列化。请注意,无论是客户端还是服务端,只要有一端还没有升级到 3.2.0,都将降级为 &lt;code>hessian2&lt;/code> 序列化,保证兼容性。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>默认关闭推空保护&lt;/p>
&lt;p>推空保护的目的是在注册中心出现故障并且主动推送空地址的时候,Dubbo 保留最后一批 provider 信息,以保证服务可用。但是,在大多数情况下,即使注册中心出现故障,也不会推送空地址,只有在一些特殊情况下才会出现。如果开启推空保护,则会对 Dubbo 的 Fallback 逻辑、心跳逻辑等造成较大的影响,给开发人员使用 Dubbo 带来困扰。&lt;/p>
&lt;p>如果在生产环境中需要开启推空保护以实现高可用性,可以将 &lt;code>dubbo.application.enable-empty-protection&lt;/code> 配置为 &lt;code>true&lt;/code>。但是请注意,已知开启推空保护会导致服务端应用从仅支持接口级服务发现的 &lt;code>2.6.x&lt;/code>、&lt;code>2.7.x&lt;/code> 版本升级到 &lt;code>3.x&lt;/code> 之后回滚到原来的版本时出现异常,极端情况下可能会导致服务调用失败。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>Dubbo 3.2 是一个非常重要的版本,它带来了众多新功能和改进,使得 Dubbo 更加强大和易用。我们非常感谢社区的支持和贡献,希望大家可以尽快体验 Dubbo 3.2,享受其中带来的便利和优势。&lt;/p></description></item><item><title>Blog: 如何通过 Higress 网关代理 Dubbo 服务</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/01/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-higress-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</link><pubDate>Sat, 01 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/01/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-higress-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/</guid><description>
&lt;h1 id="higress-对接-dubbo-服务">Higress 对接 Dubbo 服务&lt;/h1>
&lt;p>Higress提供了从HTTP协议到Dubbo协议进行转换的功能,用户通过配置协议转换,可以将一个Dubbo服务以HTTP接口暴露出来,从而用HTTP请求实现对Dubbo接口的调用。本文将通过一个示例来介绍如何用Higress配置HTTP到Dubbo的协议转换。该示例会引导您轻松地部署一个Nacos server和一个Dubbo服务,然后通过Ingress将HTTP请求转发到注册在Nacos上的Dubbo服务,并通过Higress的协议转换能力完成对Dubbo服务的HTTP调用。&lt;/p>
&lt;h2 id="前提条件">前提条件&lt;/h2>
&lt;ol>
&lt;li>Higress目前支持的Dubbo框架的版本为2.x。若您使用Dubbo3.0,要求使用dubbo协议(目前暂不支持Triple协议)。&lt;/li>
&lt;li>已安装Higress,并开启了对Istio CRD的支持,参考&lt;a href="https://higress.io/zh-cn/docs/ops/deploy-by-helm">Higress安装部署文档&lt;/a>。&lt;/li>
&lt;/ol>
&lt;h2 id="部署nacos和dubbo服务">部署Nacos和Dubbo服务&lt;/h2>
&lt;p>首先在K8s集群中apply以下资源,以部署一个Nacos注册中心,同时通过K8s service将这个Nacos server暴露出来。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Nacos Server配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">replicas&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">matchLabels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">template&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">containers&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">env&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: MODE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>: standalone
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: nacos/nacos-server:v2.2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">imagePullPolicy&lt;/span>: Always
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">containerPort&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">dnsPolicy&lt;/span>: ClusterFirst
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">restartPolicy&lt;/span>: Always
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"># Nacos Server Service配置&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">targetPort&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: ClusterIP
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在K8s集群中apply以下资源,以部署一个Dubbo服务,该Dubbo服务将注册到上述的Naocs中。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">replicas&lt;/span>: &lt;span style="color:#2aa198">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">matchLabels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">template&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">app&lt;/span>: nacos-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">containers&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: higress-registry.cn-hangzhou.cr.aliyuncs.com/samples/nacos-dubbo-provider:v1.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">imagePullPolicy&lt;/span>: IfNotPresent
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">containerPort&lt;/span>: &lt;span style="color:#2aa198">20880&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">env&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: DUBBO_REGISTRY_ADDRESS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>: nacos-server.default.svc.cluster.local
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该Dubbo服务的代码可以在Nacos的&lt;a href="https://github.com/nacos-group/nacos-examples/tree/master/nacos-dubbo-example">示例代码&lt;/a>仓库中找到,其接口定义为:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.nacos.example.dubbo.service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayName&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>接口实现如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> com.alibaba.nacos.example.dubbo.service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.config.annotation.Service&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> com.alibaba.dubbo.rpc.RpcContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.springframework.beans.factory.annotation.Value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Default {@link DemoService}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * https://nacos.io/zh-cn/docs/use-nacos-with-dubbo.html
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @since 2.6.5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Service&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.version}&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> group &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;${demo.service.group}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DefaultService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Value&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;${demo.service.name}&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String serviceName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayName&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RpcContext rpcContext &lt;span style="color:#719e07">=&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getContext&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> String&lt;span style="color:#719e07">.&lt;/span>format&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Service [name :%s , port : %d] %s(\&amp;#34;%s\&amp;#34;) : Hello,%s&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceName&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcContext&lt;span style="color:#719e07">.&lt;/span>getLocalPort&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rpcContext&lt;span style="color:#719e07">.&lt;/span>getMethodName&lt;span style="color:#719e07">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在本示例中,该Dubbo服务的服务名为com.alibaba.nacos.example.dubbo.service.DemoService,服务版本为1.0.0,服务分组为dev。&lt;/p>
&lt;p>为了测试方便,我们可以通过运行以下命令来将我们部署在K8s集群中的Naocs服务映射到本地端口:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>kubectl port-forward svc/nacos-server 8848:8848 --address&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#2aa198">&amp;#39;0.0.0.0&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后请求Nacos的服务发现接口,可以查看到我们Dubbo服务的元数据信息,从而对以上部署进行验证。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">$curl&lt;/span> -X GET &lt;span style="color:#2aa198">&amp;#39;http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT_GROUP@@providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;groupName&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT_GROUP&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;clusters&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;cacheMillis&amp;#34;&lt;/span>:10000,&lt;span style="color:#2aa198">&amp;#34;hosts&amp;#34;&lt;/span>:&lt;span style="color:#719e07">[{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;10.244.0.58&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;port&amp;#34;&lt;/span>:20880,&lt;span style="color:#2aa198">&amp;#34;weight&amp;#34;&lt;/span>:1.0,&lt;span style="color:#2aa198">&amp;#34;healthy&amp;#34;&lt;/span>:true,&lt;span style="color:#2aa198">&amp;#34;enabled&amp;#34;&lt;/span>:true,&lt;span style="color:#2aa198">&amp;#34;ephemeral&amp;#34;&lt;/span>:true,&lt;span style="color:#2aa198">&amp;#34;clusterName&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;serviceName&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;DEFAULT_GROUP@@providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;metadata&amp;#34;&lt;/span>:&lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;side&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;provider&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;release&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo_demo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;methods&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;sayName&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;deprecated&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;2.0.2&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;pid&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;3034042&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;interface&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;com.alibaba.nacos.example.dubbo.service.DemoService&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;service-name-mapping&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;version&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;generic&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;revision&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo_demo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;path&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;com.alibaba.nacos.example.dubbo.service.DemoService&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;protocol&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;metadata-type&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;remote&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;application&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dubbo-provider-demo&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;background&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;dynamic&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;category&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;providers&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;group&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;dev&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;anyhost&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;timestamp&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;1680176973875&amp;#34;&lt;/span>&lt;span style="color:#719e07">}&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;ipDeleteTimeout&amp;#34;&lt;/span>:30000,&lt;span style="color:#2aa198">&amp;#34;instanceHeartBeatInterval&amp;#34;&lt;/span>:5000,&lt;span style="color:#2aa198">&amp;#34;instanceHeartBeatTimeOut&amp;#34;&lt;/span>:15000&lt;span style="color:#719e07">}]&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;lastRefTime&amp;#34;&lt;/span>:1680178336936,&lt;span style="color:#2aa198">&amp;#34;checksum&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>,&lt;span style="color:#2aa198">&amp;#34;allIPs&amp;#34;&lt;/span>:false,&lt;span style="color:#2aa198">&amp;#34;reachProtectionThreshold&amp;#34;&lt;/span>:false,&lt;span style="color:#2aa198">&amp;#34;valid&amp;#34;&lt;/span>:true&lt;span style="color:#719e07">}&lt;/span>%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="通过ingress转发请求到dubbo服务">通过Ingress转发请求到Dubbo服务&lt;/h2>
&lt;p>Higress可以通过McpBridge来对接Nacos作为服务来源,在K8s集群中apply以下资源来配置McpBridge&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: networking.higress.io/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: McpBridge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: default
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">namespace&lt;/span>: higress-system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registries&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">domain&lt;/span>: nacos-server.default.svc.cluster.local
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">nacosGroups&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - DEFAULT_GROUP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: nacos-service-resource
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8848&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type&lt;/span>: nacos2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过McpBridge,我们可以直接从Nacos中发现Dubbo服务,并为其创建路由,而无需为每一个Dubbo服务创建service资源。&lt;/p>
&lt;p>接下来我们创建如下Ingress,从而创建一条指向Dubbo服务的HTTP路由:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: networking.k8s.io/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: Ingress
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">annotations&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">higress.io/destination&lt;/span>: providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">namespace&lt;/span>: higress-system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ingressClassName&lt;/span>: higress
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">rules&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">http&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">paths&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">backend&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">resource&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">apiGroup&lt;/span>: networking.higress.io
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">kind&lt;/span>: McpBridge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: default
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">path&lt;/span>: /dubbo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">pathType&lt;/span>: Prefix
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样,path前缀为/dubbo的请求就会被路由到我们刚刚创建的Dubbo服务上。&lt;/p>
&lt;h2 id="通过envoyfilter配置http到dubbo的协议转换规则">通过EnvoyFilter配置HTTP到Dubbo的协议转换规则&lt;/h2>
&lt;p>经过上述步骤,我们已经在K8s环境下部署了一套Naocs和Dubbo,并通过Ingress将path前缀为/dubbo的请求路由到我们配好的Dubbo服务上。但光是这样是无法正常通信的,因为Dubbo服务使用的是定制的Dubbo协议,无法天然与HTTP协议进行兼容。因此接下来我们将通过EnvoyFilter来配置HTTP到Dubbo的协议转换规则,从而实现用HTTP请求来调用Dubbo服务。&lt;/p>
&lt;p>在K8s集群中apply以下资源,要注意的是,EnvoyFilter是属于Istio的CRD,因此需要参照前提条件中的第2点来开启Higress对Istio CRD的支持。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">apiVersion&lt;/span>: networking.istio.io/v1alpha3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">kind&lt;/span>: EnvoyFilter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: http-dubbo-transcoder-test
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">namespace&lt;/span>: higress-system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">configPatches&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">applyTo&lt;/span>: HTTP_FILTER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context&lt;/span>: GATEWAY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">listener&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filterChain&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filter&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.filters.network.http_connection_manager
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">subFilter&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.filters.http.router
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">patch&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operation&lt;/span>: INSERT_BEFORE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.filters.http.http_dubbo_transcoder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">typed_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#39;@type&amp;#39;&lt;/span>: type.googleapis.com/udpa.type.v1.TypedStruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type_url&lt;/span>: type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">applyTo&lt;/span>: HTTP_ROUTE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context&lt;/span>: GATEWAY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">routeConfiguration&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">vhost&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">route&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">patch&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operation&lt;/span>: MERGE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">route&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">upgrade_configs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">connect_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">allow_post&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">upgrade_type&lt;/span>: CONNECT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">typed_per_filter_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">envoy.filters.http.http_dubbo_transcoder&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#39;@type&amp;#39;&lt;/span>: type.googleapis.com/udpa.type.v1.TypedStruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type_url&lt;/span>: type.googleapis.com/envoy.extensions.filters.http.http_dubbo_transcoder.v3.HttpDubboTranscoder
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">request_validation_options&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">reject_unknown_method&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">reject_unknown_query_parameters&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">services_mapping&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">group&lt;/span>: dev
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">method_mapping&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: sayName
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">parameter_mapping&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">extract_key&lt;/span>: p
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">extract_key_spec&lt;/span>: ALL_QUERY_PARAMETER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">mapping_type&lt;/span>: java.lang.String
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">passthrough_setting&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">passthrough_all_headers&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">path_matcher&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match_http_method_spec&lt;/span>: ALL_GET
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match_pattern&lt;/span>: /dubbo/hello
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: com.alibaba.nacos.example.dubbo.service.DemoService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">1.0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">url_unescape_spec&lt;/span>: ALL_CHARACTERS_EXCEPT_RESERVED
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">applyTo&lt;/span>: CLUSTER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">service&lt;/span>: providers:com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev.DEFAULT-GROUP.public.nacos
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">context&lt;/span>: GATEWAY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">patch&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">operation&lt;/span>: MERGE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">value&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">upstream_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: envoy.upstreams.http.dubbo_tcp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">typed_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;#39;@type&amp;#39;&lt;/span>: type.googleapis.com/udpa.type.v1.TypedStruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">type_url&lt;/span>: type.googleapis.com/envoy.extensions.upstreams.http.dubbo_tcp.v3.DubboTcpConnectionPoolProto
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在以上EnvoyFilter中,我们配置了将path为/dubbo/hello的HTTP请求转发到Dubbo服务com.alibaba.nacos.example.dubbo.service.DemoService:1.0.0:dev中,并调用其sayName方法,而该方法的参数则通过HTTP url中的的query参数p来指定。&lt;/p>
&lt;h2 id="请求验证">请求验证&lt;/h2>
&lt;p>通过以上配置,我们就可以执行以下curl命令来调用这个dubbo服务了:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">$curl&lt;/span> &lt;span style="color:#2aa198">&amp;#34;localhost/dubbo/hello?p=abc&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;result&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;Service [name :demoService , port : 20880] sayName(\&amp;#34;abc\&amp;#34;) : Hello,abc&amp;#34;&lt;/span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="配置参考">配置参考&lt;/h2>
&lt;p>EnvoyFilter的相关配置项参考&lt;a href="https://higress.io/zh-cn/docs/user/dubbo-envoyfilter">HTTP转Dubbo配置说明&lt;/a>&lt;/p></description></item><item><title>Blog: 2022 年度总结与 2023 新年规划</title><link>https://dubbo.apache.org/zh-cn/blog/2023/02/23/2022-%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93%E4%B8%8E-2023-%E6%96%B0%E5%B9%B4%E8%A7%84%E5%88%92/</link><pubDate>Thu, 23 Feb 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/02/23/2022-%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93%E4%B8%8E-2023-%E6%96%B0%E5%B9%B4%E8%A7%84%E5%88%92/</guid><description>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/roadmap/2023-roadmap.jpg" alt="dubbo-introduction">&lt;/p>
&lt;h2 id="2022-年度总结">2022 年度总结&lt;/h2>
&lt;h3 id="1-dubbo-开源-12-周年">1 Dubbo 开源 12 周年&lt;/h3>
&lt;p>50k+ star,30k fork,1000+ Contributor&lt;/p>
&lt;h3 id="2-dubbo3-里程碑">2 Dubbo3 里程碑&lt;/h3>
&lt;h3 id="实现规模化生产检验">实现规模化生产检验&lt;/h3>
&lt;p>阿里巴巴核心微服务集群实现从 HSF 到 Dubbo3 的全面升级,顺利支撑双十一、双十二万亿级服务调用&lt;/p>
&lt;h3 id="发力多语言生态">发力多语言生态&lt;/h3>
&lt;h4 id="具备生产可用条件">具备生产可用条件&lt;/h4>
&lt;p>Java、Go&lt;/p>
&lt;h4 id="从孵化走向成熟">从孵化走向成熟&lt;/h4>
&lt;p>Rust、Node.js、Python&lt;/p>
&lt;h3 id="3-核心技术">3 核心技术&lt;/h3>
&lt;p>应用级服务发现
HTTP/2 协议
流量管控升级
可观测性
自适应负载均衡
Proxyless Mesh
Spring Boot3 &amp;amp; Spring6&lt;/p>
&lt;h3 id="4-新晋-committerspmc">4 新晋 Committers&amp;amp;PMC&lt;/h3>
&lt;p>本年度社区共发展 Committer 13 位,PMC 4 位&lt;/p>
&lt;h2 id="2023-新年规划">2023 新年规划&lt;/h2>
&lt;ul>
&lt;li>官网与文档体验全面提升&lt;/li>
&lt;li>Go、Node.js、Rust 等多语言体系建设&lt;/li>
&lt;li>全面提升整体可观测性&lt;/li>
&lt;li>Dubbo Admin 一站式服务运维管控平台&lt;/li>
&lt;li>Dubbo Mesh 走向成熟&lt;/li>
&lt;li>提升 HTTP 开发体验,补全 Web 互通&lt;/li>
&lt;li>打造 gRPC over Dubbo 最佳实践&lt;/li>
&lt;li>完善的认证鉴权体系&lt;/li>
&lt;/ul></description></item><item><title>Blog: 一文帮你快速了解 Dubbo 核心能力</title><link>https://dubbo.apache.org/zh-cn/blog/2023/02/23/%E4%B8%80%E6%96%87%E5%B8%AE%E4%BD%A0%E5%BF%AB%E9%80%9F%E4%BA%86%E8%A7%A3-dubbo-%E6%A0%B8%E5%BF%83%E8%83%BD%E5%8A%9B/</link><pubDate>Thu, 23 Feb 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/02/23/%E4%B8%80%E6%96%87%E5%B8%AE%E4%BD%A0%E5%BF%AB%E9%80%9F%E4%BA%86%E8%A7%A3-dubbo-%E6%A0%B8%E5%BF%83%E8%83%BD%E5%8A%9B/</guid><description>
&lt;h2 id="dubbo-简介">Dubbo 简介&lt;/h2>
&lt;h3 id="一句话定义">一句话定义&lt;/h3>
&lt;p>Apache Dubbo 是一款微服务开发框架,它帮助解决微服务开发中的通信问题,同时为构建企业级微服务的提供服务治理能力,Dubbo 不绑定编程语言,我们的目标是为所有主流语言提供对等的微服务开发体验。
&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/1-overview.jpg" alt="overview">&lt;/p>
&lt;h3 id="基本架构">基本架构&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/2-arc.jpg" alt="overview">&lt;/p>
&lt;p>Dubbo 从架构图上分为数据面和控制面。在数据面,使用 Dubbo 开发的微服务进程间基于 RPC 协议通信。DubboAdmin 控制面作为服务治理的抽象入口,由一系列可选的服务治理组件构成,负责 Dubbo集群的服务发现、流量管控策略、可视化监测。&lt;/p>
&lt;h3 id="行业应用">行业应用&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/3-usecase.jpg" alt="overview">&lt;/p>
&lt;p>Dubbo 设计用于解决阿里巴巴内部大规模 微服务集群实践难题,当前已被广泛应用于几乎所有行业的微服务实践中。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/4-usecase-alibaba.jpg" alt="overview">&lt;/p>
&lt;p>以阿里巴巴为例,在 2021 年,阿里巴巴基于内部多年 HSF 框架实践积累,面向云原生架构设计了下一代微服务框架 Dubbo3,用于解决性能、治理升级、服务网格等一系列问题;截止目前,阿里巴巴已全面完成从 HSF到 Dubbo3 的迁移,核心业务都跑在开源 Dubbo3 之上。&lt;/p>
&lt;h2 id="dubbo-到底提供了哪些核心能力">Dubbo 到底提供了哪些核心能力?&lt;/h2>
&lt;h3 id="提供微服务抽象与框架">提供微服务抽象与框架&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/5-framework.jpg" alt="overview">&lt;/p>
&lt;p>首先,Dubbo 作为服务开发框架解决了业务应用中微服务定义、暴露、通信与治理的问题,为业务应用开发定义了一套微服务编程范式。
具体来说,Dubbo 为业务应用提供了微服务开发API、RPC 协议、服务治理三大核心能力,让开发者真正的专注业务逻辑开发。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/6-extensibility.jpg" alt="overview">&lt;/p>
&lt;p>Dubbo 不是应用框架的替代者,它可以很好的工作在每种语言的主流编程框架之上,以 Java 为例,Dubbo 可以很好的与 Spring 协作,并在此基础上提供服务定义、微服务编程、服务发现、负载均衡、流量管控等能力。&lt;/p>
&lt;h3 id="提供灵活的通信协议切换能力">提供灵活的通信协议切换能力&lt;/h3>
&lt;p>在通信方面,Dubbo 区别于其他 RPC 框架的是它不绑定特定协议,你可以在底层选用 HTTP./2、TCP、gRPC、REST、Hessian 等任意通信协议,同时享受统一的 API、以及对等的服务治理能力。&lt;/p>
&lt;h3 id="一切皆可扩展">一切皆可扩展&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/8-extensibility.jpg" alt="overview">&lt;/p>
&lt;p>Dubbo 的另一个优势在于其可扩展性设计,从流量管控、协议编码、诊断调优、再到服务治理,你都可以去扩展,满足企业级微服务开发与运维的所有诉求。&lt;/p>
&lt;h3 id="丰富的生态">丰富的生态&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/9-ecosystem.jpg" alt="overview">&lt;/p>
&lt;p>基于扩展能力 Dubbo 官方提供了丰富的生态适配,涵盖了所有主流的开源微服务组件。&lt;/p>
&lt;h3 id="服务网格">服务网格&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/10-mesh.jpg" alt="overview">&lt;/p>
&lt;p>对于服务网格架构,Dubbo也可以轻松接入原生 Istio 体系;
在数据面支持与 Envoy 部署的 Proxy 模式,也支持无 Envoy 的 Proxyless 模式,提供更灵活的数据面选择。&lt;/p>
&lt;h2 id="构建企业级dubbo-微服务有多简单你只需要-4-步">构建企业级Dubbo 微服务有多简单?你只需要 4 步&lt;/h2>
&lt;p>我们以 Java 微服务开发为例。&lt;/p>
&lt;h3 id="第一步">第一步&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/11-initializer.png" alt="overview">&lt;/p>
&lt;p>使用 &lt;a href="https://start.dubbo.apache.org/bootstrap.html">官方脚手架&lt;/a> 快速创建项目模板,只需要选择依赖的版本、组件,点击 “获取代码” 即可&lt;/p>
&lt;h3 id="第二步">第二步&lt;/h3>
&lt;p>将模板项目导入 IDE 开发环境。
定义 Java 接口作为 Dubbo 服务。
&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/12-interface.jpg" alt="overview">&lt;/p>
&lt;p>开发 Dubbo 服务端,实现接口并完成业务逻辑编码,通过一条简单的注解配置完成服务发布。
&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/13-impl.jpg" alt="overview">&lt;/p>
&lt;p>开发Dubbo 客户端,通过注解声明 Dubbo 服务,然后就可以发起远程方法调用了。至此,开发工作完成。
&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/14-reference.jpg" alt="overview">&lt;/p>
&lt;h3 id="第三步">第三步&lt;/h3>
&lt;p>进入部署环节,我们选择 Kubernetes 作为部署环境。&lt;/p>
&lt;p>首先,通过一条命令安装 dubbo-admin 等服务治理组件,安装成功之后,我们查看部署状态。接下来,开始部署业务应用,随后查看确认直到应用已经正常启动
&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/15-deploy.jpg" alt="overview">&lt;/p>
&lt;p>然后,我们就可以打开 Admin 控制台查看服务部署与调用情况了。这里是 Dubbo Admin 控制台的页面显示效果,可以看到刚才启动的 Dubbo 服务部署状态;除此之外,Admin 还提供了更详细的流量监控监测,点击服务统计,可进入监控页面&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/16-admin.jpg" alt="overview">&lt;/p>
&lt;p>你可以在此了解Dubbo 集群的详细运行状态,包括每个应用对外服务和调用服务的情况,QpS、成功率等,还可以查看每个实例的资源健康状况。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/17-grafana1.png" alt="overview">&lt;/p>
&lt;h3 id="第四步">第四步&lt;/h3>
&lt;p>进行流量管控。当应用已经平稳运行后,进一步控制流量的访问行为,包括实现金丝雀发布、全链路灰度、动态调整超时时间、调整权重、按比例流量分发、参数路由等。控制台提供了可视化的流量治理规则操作入口,在这里可以直接下发流量规则。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/19-gray.jpg" alt="overview">&lt;/p>
&lt;p>以一个线上环境的灰度隔离示例,通过 Dubbo 流量管控机制,我们可以给每个应用的一部分机器打上 gray 标签,接下来,对于入口为 gray 的流量,就可以控制确保它只在有 gray 标记的 Dubbo 实例内流转,实现了全链路的逻辑隔离效果,
对于隔离多套开发环境、线上灰度测试等场景都非常有用。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/20-region.jpg" alt="overview">&lt;/p>
&lt;p>对于同区域优先调用的场景,这里有两个应用做了多区域部署,紫色是杭州区域、蓝色是北京区域,部署在橙色区域的应用会优先访问同区域的应用,以此降低访问延迟,蓝色区域部署的服务亦是如此。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/introduction/21-region.jpg" alt="overview">&lt;/p>
&lt;p>当应用在同区域区域部署的实例不可用时,调用会自动跨区域切换到其他可用区,确保整体可用性。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>接下来,请开始你的Dubbo 之旅吧。&lt;/p></description></item><item><title>Blog: 指标埋点</title><link>https://dubbo.apache.org/zh-cn/blog/2023/02/20/%E6%8C%87%E6%A0%87%E5%9F%8B%E7%82%B9/</link><pubDate>Mon, 20 Feb 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/02/20/%E6%8C%87%E6%A0%87%E5%9F%8B%E7%82%B9/</guid><description>
&lt;h1 id="概述">概述&lt;/h1>
&lt;h2 id="1-指标接入说明">1. 指标接入说明&lt;/h2>
&lt;h2 id="2-指标体系设计">2. 指标体系设计&lt;/h2>
&lt;p>Dubbo的指标体系,总共涉及三块,指标收集、本地聚合、指标推送&lt;/p>
&lt;ul>
&lt;li>指标收集:将Dubbo内部需要监控的指标推送至统一的Collector中进行存储&lt;/li>
&lt;li>本地聚合:指标收集获取的均为基础指标,而一些分位数指标则需通过本地聚合计算得出&lt;/li>
&lt;li>指标推送:收集和聚合后的指标通过一定的方式推送至第三方服务器,目前只涉及Prometheus&lt;/li>
&lt;/ul>
&lt;h2 id="3-结构设计">3. 结构设计&lt;/h2>
&lt;ul>
&lt;li>移除原来与 Metrics 相关的类&lt;/li>
&lt;li>创建新模块 dubbo-metrics/dubbo-metrics-api、dubbo-metrics/dubbo-metrics-prometheus,MetricsConfig 作为该模块的配置类&lt;/li>
&lt;li>使用micrometer,在Collector中使用基本类型代表指标,如Long、Double等,并在dubbo-metrics-api中引入micrometer,由micrometer对内部指标进行转换&lt;/li>
&lt;/ul>
&lt;h2 id="4-数据流转">4. 数据流转&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/docs3-v2/java-sdk/observability/dataflow.png" alt="img.png">&lt;/p>
&lt;h2 id="5-目标">5. 目标&lt;/h2>
&lt;p>指标接口将提供一个 MetricsService,该 Service 不仅提供柔性服务所的接口级数据,也提供所有指标的查询方式,其中方法级指标的查询的接口可按如下方式声明&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">MetricsService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Default {@link MetricsService} extension name.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String DEFAULT_EXTENSION_NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;default&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * The contract version of {@link MetricsService}, the future update must make sure compatible.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String VERSION &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Get metrics by prefixes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param categories categories
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return metrics - key=MetricCategory value=MetricsEntityList
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCategory&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsEntity&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getMetricsByCategories&lt;/span>&lt;span style="color:#719e07">(&lt;/span>List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCategory&lt;span style="color:#719e07">&amp;gt;&lt;/span> categories&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Get metrics by interface and prefixes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param serviceUniqueName serviceUniqueName (eg.group/interfaceName:version)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param categories categories
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return metrics - key=MetricCategory value=MetricsEntityList
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCategory&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsEntity&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getMetricsByCategories&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String serviceUniqueName&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCategory&lt;span style="color:#719e07">&amp;gt;&lt;/span> categories&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * Get metrics by interface、method and prefixes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param serviceUniqueName serviceUniqueName (eg.group/interfaceName:version)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param methodName methodName
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param parameterTypes method parameter types
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param categories categories
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return metrics - key=MetricCategory value=MetricsEntityList
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCategory&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsEntity&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getMetricsByCategories&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String serviceUniqueName&lt;span style="color:#719e07">,&lt;/span> String methodName&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;[]&lt;/span> parameterTypes&lt;span style="color:#719e07">,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsCategory&lt;span style="color:#719e07">&amp;gt;&lt;/span> categories&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中 MetricsCategory 设计如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">enum&lt;/span> MetricsCategory &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RT&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> QPS&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> REQUESTS&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>MetricsEntity 设计如下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MetricsEntity&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String name&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> tags&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> MetricsCategory category&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> Object value&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="指标收集">指标收集&lt;/h1>
&lt;h2 id="1-嵌入位置">1. 嵌入位置&lt;/h2>
&lt;p>Dubbo 架构图如下
&lt;img src="https://dubbo.apache.org/imgs/docs3-v2/java-sdk/observability/dubbo.png" alt="img.png">&lt;/p>
&lt;p>在 provider 中添加一层 MetricsFilter 重写 invoke 方法嵌入调用链路用于收集指标,用 try-catch-finally 处理,核心代码如下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Activate&lt;/span>&lt;span style="color:#719e07">(&lt;/span>group &lt;span style="color:#719e07">=&lt;/span> PROVIDER&lt;span style="color:#719e07">,&lt;/span> order &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">-&lt;/span>1&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MetricsFilter&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Filter&lt;span style="color:#719e07">,&lt;/span> ScopeModelAware &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> Result &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> Invocation invocation&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>increaseTotalRequests&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>increaseProcessingRequests&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long startTime &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>currentTimeMillis&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Result invoke &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>increaseSucceedRequests&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoke&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RpcException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>increaseFailedRequests&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">finally&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long endTime &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>currentTimeMillis&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Long rt &lt;span style="color:#719e07">=&lt;/span> endTime &lt;span style="color:#719e07">-&lt;/span> startTime&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>addRT&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">,&lt;/span> rt&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collector&lt;span style="color:#719e07">.&lt;/span>decreaseProcessingRequests&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> methodName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-指标标识">2. 指标标识&lt;/h2>
&lt;p>用以下五个属性作为隔离级别区分标识不同方法,也是各个 ConcurrentHashMap 的 key&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">MethodMetric&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String applicationName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String interfaceName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String methodName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String group&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String version&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="3-基础指标">3. 基础指标&lt;/h2>
&lt;p>指标通过 common 模块下的 MetricsCollector 存储所有指标数据&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DefaultMetricsCollector&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> MetricsCollector &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> Boolean collectEnabled &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>MetricsListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ApplicationModel applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> String applicationName&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> totalRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> succeedRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> failedRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> processingRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> lastRT &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> LongAccumulator&lt;span style="color:#719e07">&amp;gt;&lt;/span> minRT &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> LongAccumulator&lt;span style="color:#719e07">&amp;gt;&lt;/span> maxRT &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> avgRT &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> totalRT &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> AtomicLong&lt;span style="color:#719e07">&amp;gt;&lt;/span> rtCount &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="本地聚合">本地聚合&lt;/h1>
&lt;p>本地聚合指将一些简单的指标通过计算获取各分位数指标的过程&lt;/p>
&lt;h2 id="1-参数设计">1. 参数设计&lt;/h2>
&lt;p>收集指标时,默认只收集基础指标,而一些单机聚合指标则需要开启服务柔性或者本地聚合后另起线程计算。此处若开启服务柔性,则本地聚合默认开启&lt;/p>
&lt;h3 id="11-本地聚合开启方式">1.1 本地聚合开启方式&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:metrics&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:aggregation&lt;/span> enable=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:metrics&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="12-指标聚合参数">1.2 指标聚合参数&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:metrics&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">&amp;lt;dubbo:aggregation&lt;/span> enable=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> bucket-num=&lt;span style="color:#2aa198">&amp;#34;5&amp;#34;&lt;/span> time-window-seconds=&lt;span style="color:#2aa198">&amp;#34;10&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;/dubbo:metrics&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-具体指标">2. 具体指标&lt;/h2>
&lt;p>Dubbo的指标模块帮助用户从外部观察正在运行的系统的内部服务状况 ,Dubbo参考 &lt;a href="https://sre.google/sre-book/monitoring-distributed-systems/">&amp;ldquo;四大黄金信号&amp;rdquo;&lt;/a>、&lt;em>RED方法&lt;/em>、&lt;em>USE方法&lt;/em>等理论并结合实际企业应用场景从不同维度统计了丰富的关键指标,关注这些核心指标对于提供可用性的服务是至关重要的。&lt;/p>
&lt;p>Dubbo的关键指标包含:&lt;strong>延迟(Latency)&lt;/strong>、&lt;strong>流量(Traffic)&lt;/strong>、 &lt;strong>错误(Errors)&lt;/strong> 和 &lt;strong>饱和度(Saturation)&lt;/strong> 等内容 。同时,为了更好的监测服务运行状态,Dubbo 还提供了对核心组件状态的监控,如Dubbo应用信息、线程池信息、三大中心交互的指标数据等。&lt;/p>
&lt;p>在Dubbo中主要包含如下监控指标:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">&lt;/th>
&lt;th style="text-align:left">基础设施&lt;/th>
&lt;th style="text-align:left">业务监控&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">延迟类&lt;/td>
&lt;td style="text-align:left">IO 等待; 网络延迟;&lt;/td>
&lt;td style="text-align:left">接口、服务的平均耗时、TP90、TP99、TP999 等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">流量类&lt;/td>
&lt;td style="text-align:left">网络和磁盘 IO;&lt;/td>
&lt;td style="text-align:left">服务层面的 QPS、&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">错误类&lt;/td>
&lt;td style="text-align:left">宕机; 磁盘(坏盘或文件系统错误); 进程或端口挂掉; 网络丢包;&lt;/td>
&lt;td style="text-align:left">错误日志;业务状态码、错误码走势;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">饱和度类&lt;/td>
&lt;td style="text-align:left">系统资源利用率: CPU、内存、磁盘、网络等; 饱和度:等待线程数,队列积压长度;&lt;/td>
&lt;td style="text-align:left">这里主要包含JVM、线程池等&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>qps: 基于滑动窗口获取动态qps&lt;/li>
&lt;li>rt: 基于滑动窗口获取动态rt&lt;/li>
&lt;li>失败请求数: 基于滑动窗口获取最近时间内的失败请求数&lt;/li>
&lt;li>成功请求数: 基于滑动窗口获取最近时间内的成功请求数&lt;/li>
&lt;li>处理中请求数: 前后增加Filter简单统计&lt;/li>
&lt;li>具体指标依赖滑动窗口,额外使用 AggregateMetricsCollector 收集&lt;/li>
&lt;/ul>
&lt;p>输出到普罗米修斯的相关指标可以参考的内容如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># HELP jvm_gc_live_data_size_bytes Size of long-lived heap memory pool after reclamation
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_gc_live_data_size_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_live_data_size_bytes 1.6086528E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP requests_succeed_aggregate Aggregated Succeed Requests
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE requests_succeed_aggregate gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>requests_succeed_aggregate{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 39.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_buffer_memory_used_bytes An estimate of the memory that the Java virtual machine is using for this buffer pool
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_buffer_memory_used_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_buffer_memory_used_bytes{id=&amp;#34;direct&amp;#34;,} 1.679975E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_buffer_memory_used_bytes{id=&amp;#34;mapped&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_gc_memory_allocated_bytes_total Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_gc_memory_allocated_bytes_total counter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_memory_allocated_bytes_total 2.9884416E9
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP requests_total_aggregate Aggregated Total Requests
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE requests_total_aggregate gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>requests_total_aggregate{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 39.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP system_load_average_1m The sum of the number of runnable entities queued to available processors and the number of runnable entities running on the available processors averaged over a period of time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE system_load_average_1m gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system_load_average_1m 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP system_cpu_usage The &amp;#34;recent cpu usage&amp;#34; for the whole system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE system_cpu_usage gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system_cpu_usage 0.015802269043760128
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_threads_peak_threads The peak live thread count since the Java virtual machine started or peak was reset
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_threads_peak_threads gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_peak_threads 40.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP requests_processing Processing Requests
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE requests_processing gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>requests_processing{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_memory_max_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;profiled nmethods&amp;#39;&amp;#34;,} 1.22912768E8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Survivor Space&amp;#34;,} -1.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Old Gen&amp;#34;,} 9.52107008E8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;Metaspace&amp;#34;,} -1.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Eden Space&amp;#34;,} -1.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;non-nmethods&amp;#39;&amp;#34;,} 5828608.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;Compressed Class Space&amp;#34;,} 1.073741824E9
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_max_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;non-profiled nmethods&amp;#39;&amp;#34;,} 1.22916864E8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_threads_states_threads The current number of threads having BLOCKED state
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_threads_states_threads gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_states_threads{state=&amp;#34;blocked&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_states_threads{state=&amp;#34;runnable&amp;#34;,} 10.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_states_threads{state=&amp;#34;waiting&amp;#34;,} 16.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_states_threads{state=&amp;#34;timed-waiting&amp;#34;,} 13.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_states_threads{state=&amp;#34;new&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_states_threads{state=&amp;#34;terminated&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_buffer_total_capacity_bytes An estimate of the total capacity of the buffers in this pool
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_buffer_total_capacity_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_buffer_total_capacity_bytes{id=&amp;#34;direct&amp;#34;,} 1.6799749E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_buffer_total_capacity_bytes{id=&amp;#34;mapped&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_p99 Response Time P99
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_p99 gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_p99{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 1.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_memory_used_bytes The amount of used memory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_memory_used_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Survivor Space&amp;#34;,} 1048576.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;profiled nmethods&amp;#39;&amp;#34;,} 1.462464E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Old Gen&amp;#34;,} 1.6098728E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;Metaspace&amp;#34;,} 4.0126952E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Eden Space&amp;#34;,} 8.2837504E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;non-nmethods&amp;#39;&amp;#34;,} 1372032.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;Compressed Class Space&amp;#34;,} 4519248.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_used_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;non-profiled nmethods&amp;#39;&amp;#34;,} 5697408.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP qps Query Per Seconds
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE qps gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qps{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 0.3333333333333333
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_min Min Response Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_min gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_min{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_buffer_count_buffers An estimate of the number of buffers in the pool
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_buffer_count_buffers gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_buffer_count_buffers{id=&amp;#34;mapped&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_buffer_count_buffers{id=&amp;#34;direct&amp;#34;,} 10.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP system_cpu_count The number of processors available to the Java virtual machine
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE system_cpu_count gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system_cpu_count 2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_classes_loaded_classes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_classes_loaded_classes 7325.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_total Total Response Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_total gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_total{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 2783.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_last Last Response Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_last gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_last{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_gc_memory_promoted_bytes_total Count of positive increases in the size of the old generation memory pool before GC to after GC
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_gc_memory_promoted_bytes_total counter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_memory_promoted_bytes_total 1.4450952E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_gc_pause_seconds Time spent in GC pause
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_gc_pause_seconds summary
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_pause_seconds_count{action=&amp;#34;end of minor GC&amp;#34;,cause=&amp;#34;Metadata GC Threshold&amp;#34;,} 2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_pause_seconds_sum{action=&amp;#34;end of minor GC&amp;#34;,cause=&amp;#34;Metadata GC Threshold&amp;#34;,} 0.026
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_pause_seconds_count{action=&amp;#34;end of minor GC&amp;#34;,cause=&amp;#34;G1 Evacuation Pause&amp;#34;,} 37.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_pause_seconds_sum{action=&amp;#34;end of minor GC&amp;#34;,cause=&amp;#34;G1 Evacuation Pause&amp;#34;,} 0.156
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_gc_pause_seconds_max Time spent in GC pause
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_gc_pause_seconds_max gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_pause_seconds_max{action=&amp;#34;end of minor GC&amp;#34;,cause=&amp;#34;Metadata GC Threshold&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_pause_seconds_max{action=&amp;#34;end of minor GC&amp;#34;,cause=&amp;#34;G1 Evacuation Pause&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_p95 Response Time P95
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_p95 gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_p95{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP requests_total Total Requests
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE requests_total gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>requests_total{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 27738.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP process_cpu_usage The &amp;#34;recent cpu usage&amp;#34; for the Java Virtual Machine process
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE process_cpu_usage gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>process_cpu_usage 8.103727714748784E-4
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_max Max Response Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_max gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_max{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 4.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_gc_max_data_size_bytes Max size of long-lived heap memory pool
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_gc_max_data_size_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_gc_max_data_size_bytes 9.52107008E8
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_threads_live_threads The current number of live threads including both daemon and non-daemon threads
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_threads_live_threads gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_live_threads 39.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_threads_daemon_threads The current number of live daemon threads
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_threads_daemon_threads gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_threads_daemon_threads 36.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_classes_unloaded_classes_total The total number of classes unloaded since the Java virtual machine has started execution
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_classes_unloaded_classes_total counter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_classes_unloaded_classes_total 0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE jvm_memory_committed_bytes gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;profiled nmethods&amp;#39;&amp;#34;,} 1.4680064E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Survivor Space&amp;#34;,} 1048576.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Old Gen&amp;#34;,} 5.24288E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;Metaspace&amp;#34;,} 4.1623552E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;heap&amp;#34;,id=&amp;#34;G1 Eden Space&amp;#34;,} 9.0177536E7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;non-nmethods&amp;#39;&amp;#34;,} 2555904.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;Compressed Class Space&amp;#34;,} 5111808.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jvm_memory_committed_bytes{area=&amp;#34;nonheap&amp;#34;,id=&amp;#34;CodeHeap &amp;#39;non-profiled nmethods&amp;#39;&amp;#34;,} 5701632.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP requests_succeed Succeed Requests
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE requests_succeed gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>requests_succeed{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 27738.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># HELP rt_avg Average Response Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># TYPE rt_avg gauge
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rt_avg{application_name=&amp;#34;metrics-provider&amp;#34;,group=&amp;#34;&amp;#34;,hostname=&amp;#34;iZ8lgm9icspkthZ&amp;#34;,interface=&amp;#34;org.apache.dubbo.samples.metrics.prometheus.api.DemoService&amp;#34;,ip=&amp;#34;172.28.236.104&amp;#34;,method=&amp;#34;sayHello&amp;#34;,version=&amp;#34;&amp;#34;,} 0.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="聚合收集器">聚合收集器&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AggregateMetricsCollector&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> MetricsCollector&lt;span style="color:#719e07">,&lt;/span> MetricsListener &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> bucketNum&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> timeWindowSeconds&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> TimeWindowCounter&lt;span style="color:#719e07">&amp;gt;&lt;/span> totalRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> TimeWindowCounter&lt;span style="color:#719e07">&amp;gt;&lt;/span> succeedRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> TimeWindowCounter&lt;span style="color:#719e07">&amp;gt;&lt;/span> failedRequests &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> TimeWindowCounter&lt;span style="color:#719e07">&amp;gt;&lt;/span> qps &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>MethodMetric&lt;span style="color:#719e07">,&lt;/span> TimeWindowQuantile&lt;span style="color:#719e07">&amp;gt;&lt;/span> rt &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> ApplicationModel applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Integer DEFAULT_COMPRESSION &lt;span style="color:#719e07">=&lt;/span> 100&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Integer DEFAULT_BUCKET_NUM &lt;span style="color:#719e07">=&lt;/span> 10&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Integer DEFAULT_TIME_WINDOW_SECONDS &lt;span style="color:#719e07">=&lt;/span> 120&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//在构造函数中解析配置信息
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">AggregateMetricsCollector&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ApplicationModel applicationModel&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>applicationModel &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ConfigManager configManager &lt;span style="color:#719e07">=&lt;/span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getApplicationConfigManager&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetricsConfig config &lt;span style="color:#719e07">=&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getMetrics&lt;span style="color:#719e07">().&lt;/span>orElse&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>config &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getAggregation&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">&amp;amp;&amp;amp;&lt;/span> Boolean&lt;span style="color:#719e07">.&lt;/span>TRUE&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>config&lt;span style="color:#719e07">.&lt;/span>getAggregation&lt;span style="color:#719e07">().&lt;/span>getEnabled&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// only registered when aggregation is enabled.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registerListener&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AggregationConfig aggregation &lt;span style="color:#719e07">=&lt;/span> config&lt;span style="color:#719e07">.&lt;/span>getAggregation&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>bucketNum &lt;span style="color:#719e07">=&lt;/span> aggregation&lt;span style="color:#719e07">.&lt;/span>getBucketNum&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> DEFAULT_BUCKET_NUM &lt;span style="color:#719e07">:&lt;/span> aggregation&lt;span style="color:#719e07">.&lt;/span>getBucketNum&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>timeWindowSeconds &lt;span style="color:#719e07">=&lt;/span> aggregation&lt;span style="color:#719e07">.&lt;/span>getTimeWindowSeconds&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> DEFAULT_TIME_WINDOW_SECONDS &lt;span style="color:#719e07">:&lt;/span> aggregation&lt;span style="color:#719e07">.&lt;/span>getTimeWindowSeconds&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果开启了本地聚合,则通过 spring 的 BeanFactory 添加监听,将 AggregateMetricsCollector 与 DefaultMetricsCollector 绑定,实现一种生产者消费者的模式,DefaultMetricsCollector 中使用监听器列表,方便扩展&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">registerListener&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationModel&lt;span style="color:#719e07">.&lt;/span>getBeanFactory&lt;span style="color:#719e07">().&lt;/span>getBean&lt;span style="color:#719e07">(&lt;/span>DefaultMetricsCollector&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>addListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="3-指标聚合">3. 指标聚合&lt;/h2>
&lt;p>滑动窗口
假设我们初始有6个bucket,每个窗口时间设置为2分钟
每次写入指标数据时,会将数据分别写入6个bucket内,每隔两分钟移动一个bucket并且清除原来bucket内的数据
读取指标时,读取当前current指向的bucket,以达到滑动窗口的效果
具体如下图所示,实现了当前 bucket 内存储了配置中设置的 bucket 生命周期内的数据,即近期数据
&lt;img src="https://dubbo.apache.org/imgs/docs3-v2/java-sdk/observability/aggre.png" alt="img_1.png">&lt;/p>
&lt;p>在每个bucket内,使用&lt;strong>TDigest 算法&lt;/strong>计算分位数指标&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>TDigest 算法&lt;/strong>(极端分位精确度高,如p1 p99,中间分位精确度低,如p50),相关资料如下&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://op8867555.github.io/posts/2018-04-09-tdigest.html">https://op8867555.github.io/posts/2018-04-09-tdigest.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.csdn.net/csdnnews/article/details/116246540">https://blog.csdn.net/csdnnews/article/details/116246540&lt;/a>&lt;/li>
&lt;li>开源实现:https://github.com/tdunning/t-digest&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>代码实现如下,除了 TimeWindowQuantile 用来计算分位数指标外,另外提供了 TimeWindowCounter 来收集时间区间内的指标数量&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">TimeWindowQuantile&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">double&lt;/span> compression&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> TDigest&lt;span style="color:#719e07">[]&lt;/span> ringBuffer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> currentBucket&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">long&lt;/span> lastRotateTimestampMillis&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">long&lt;/span> durationBetweenRotatesMillis&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">TimeWindowQuantile&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">double&lt;/span> compression&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> bucketNum&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> timeWindowSeconds&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>compression &lt;span style="color:#719e07">=&lt;/span> compression&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>ringBuffer &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> TDigest&lt;span style="color:#719e07">[&lt;/span>bucketNum&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0&lt;span style="color:#719e07">;&lt;/span> i &lt;span style="color:#719e07">&amp;lt;&lt;/span> bucketNum&lt;span style="color:#719e07">;&lt;/span> i&lt;span style="color:#719e07">++)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>ringBuffer&lt;span style="color:#719e07">[&lt;/span>i&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> TDigest&lt;span style="color:#719e07">.&lt;/span>createDigest&lt;span style="color:#719e07">(&lt;/span>compression&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>currentBucket &lt;span style="color:#719e07">=&lt;/span> 0&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>lastRotateTimestampMillis &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>currentTimeMillis&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>durationBetweenRotatesMillis &lt;span style="color:#719e07">=&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>SECONDS&lt;span style="color:#719e07">.&lt;/span>toMillis&lt;span style="color:#719e07">(&lt;/span>timeWindowSeconds&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">/&lt;/span> bucketNum&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">double&lt;/span> &lt;span style="color:#268bd2">quantile&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">double&lt;/span> q&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TDigest currentBucket &lt;span style="color:#719e07">=&lt;/span> rotate&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> currentBucket&lt;span style="color:#719e07">.&lt;/span>quantile&lt;span style="color:#719e07">(&lt;/span>q&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">add&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#dc322f">double&lt;/span> value&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rotate&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>TDigest bucket &lt;span style="color:#719e07">:&lt;/span> ringBuffer&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bucket&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> TDigest &lt;span style="color:#268bd2">rotate&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">long&lt;/span> timeSinceLastRotateMillis &lt;span style="color:#719e07">=&lt;/span> System&lt;span style="color:#719e07">.&lt;/span>currentTimeMillis&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&lt;/span> lastRotateTimestampMillis&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">while&lt;/span> &lt;span style="color:#719e07">(&lt;/span>timeSinceLastRotateMillis &lt;span style="color:#719e07">&amp;gt;&lt;/span> durationBetweenRotatesMillis&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ringBuffer&lt;span style="color:#719e07">[&lt;/span>currentBucket&lt;span style="color:#719e07">]&lt;/span> &lt;span style="color:#719e07">=&lt;/span> TDigest&lt;span style="color:#719e07">.&lt;/span>createDigest&lt;span style="color:#719e07">(&lt;/span>compression&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(++&lt;/span>currentBucket &lt;span style="color:#719e07">&amp;gt;=&lt;/span> ringBuffer&lt;span style="color:#719e07">.&lt;/span>length&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> currentBucket &lt;span style="color:#719e07">=&lt;/span> 0&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> timeSinceLastRotateMillis &lt;span style="color:#719e07">-=&lt;/span> durationBetweenRotatesMillis&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> lastRotateTimestampMillis &lt;span style="color:#719e07">+=&lt;/span> durationBetweenRotatesMillis&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ringBuffer&lt;span style="color:#719e07">[&lt;/span>currentBucket&lt;span style="color:#719e07">];&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="指标推送">指标推送&lt;/h1>
&lt;p>指标推送只有用户在设置了&amp;lt;dubbo:metrics /&amp;gt;配置且配置protocol参数后才开启,若只开启指标聚合,则默认不推送指标。&lt;/p>
&lt;h2 id="1-promehteus-pull-servicediscovery">1. Promehteus Pull ServiceDiscovery&lt;/h2>
&lt;p>使用dubbo-admin等类似的中间层,启动时根据配置将本机 IP、Port、MetricsURL 推送地址信息至dubbo-admin(或任意中间层)的方式,暴露HTTP ServiceDiscovery供prometheus读取,配置方式如&amp;lt;dubbo:metrics protocol=&amp;ldquo;prometheus&amp;rdquo; mode=&amp;ldquo;pull&amp;rdquo; address=&amp;quot;${dubbo-admin.address}&amp;quot; port=&amp;ldquo;20888&amp;rdquo; url=&amp;quot;/metrics&amp;quot;/&amp;gt;,其中在pull模式下address为可选参数,若不填则需用户手动在Prometheus配置文件中配置地址&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">exportHttpServer&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> exporterEnabled &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_EXPORTER_ENABLED_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exporterEnabled&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> port &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_EXPORTER_METRICS_PORT_KEY&lt;span style="color:#719e07">,&lt;/span> PROMETHEUS_DEFAULT_METRICS_PORT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String path &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_EXPORTER_METRICS_PATH_KEY&lt;span style="color:#719e07">,&lt;/span> PROMETHEUS_DEFAULT_METRICS_PATH&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>path&lt;span style="color:#719e07">.&lt;/span>startsWith&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;/&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;/&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> path&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prometheusExporterHttpServer &lt;span style="color:#719e07">=&lt;/span> HttpServer&lt;span style="color:#719e07">.&lt;/span>create&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> InetSocketAddress&lt;span style="color:#719e07">(&lt;/span>port&lt;span style="color:#719e07">),&lt;/span> 0&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prometheusExporterHttpServer&lt;span style="color:#719e07">.&lt;/span>createContext&lt;span style="color:#719e07">(&lt;/span>path&lt;span style="color:#719e07">,&lt;/span> httpExchange &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String response &lt;span style="color:#719e07">=&lt;/span> prometheusRegistry&lt;span style="color:#719e07">.&lt;/span>scrape&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpExchange&lt;span style="color:#719e07">.&lt;/span>sendResponseHeaders&lt;span style="color:#719e07">(&lt;/span>200&lt;span style="color:#719e07">,&lt;/span> response&lt;span style="color:#719e07">.&lt;/span>getBytes&lt;span style="color:#719e07">().&lt;/span>length&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">(&lt;/span>OutputStream os &lt;span style="color:#719e07">=&lt;/span> httpExchange&lt;span style="color:#719e07">.&lt;/span>getResponseBody&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os&lt;span style="color:#719e07">.&lt;/span>write&lt;span style="color:#719e07">(&lt;/span>response&lt;span style="color:#719e07">.&lt;/span>getBytes&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpServerThread &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> Thread&lt;span style="color:#719e07">(&lt;/span>prometheusExporterHttpServer&lt;span style="color:#719e07">::&lt;/span>start&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpServerThread&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>IOException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> RuntimeException&lt;span style="color:#719e07">(&lt;/span>e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-prometheus-push-pushgateway">2. Prometheus Push Pushgateway&lt;/h2>
&lt;p>用户直接在Dubbo配置文件中配置Prometheus Pushgateway的地址即可,如&amp;lt;dubbo:metrics protocol=&amp;ldquo;prometheus&amp;rdquo; mode=&amp;ldquo;push&amp;rdquo; address=&amp;quot;${prometheus.pushgateway-url}&amp;quot; interval=&amp;ldquo;5&amp;rdquo; /&amp;gt;,其中interval代表推送间隔&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">schedulePushJob&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> pushEnabled &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_ENABLED_KEY&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>pushEnabled&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String baseUrl &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String job &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_JOB_KEY&lt;span style="color:#719e07">,&lt;/span> PROMETHEUS_DEFAULT_JOB_NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">int&lt;/span> pushInterval &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY&lt;span style="color:#719e07">,&lt;/span> PROMETHEUS_DEFAULT_PUSH_INTERVAL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String username &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_USERNAME_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String password &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NamedThreadFactory threadFactory &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> NamedThreadFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;prometheus-push-job&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pushJobExecutor &lt;span style="color:#719e07">=&lt;/span> Executors&lt;span style="color:#719e07">.&lt;/span>newScheduledThreadPool&lt;span style="color:#719e07">(&lt;/span>1&lt;span style="color:#719e07">,&lt;/span> threadFactory&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PushGateway pushGateway &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> PushGateway&lt;span style="color:#719e07">(&lt;/span>baseUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isBlank&lt;span style="color:#719e07">(&lt;/span>username&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pushGateway&lt;span style="color:#719e07">.&lt;/span>setConnectionFactory&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> BasicAuthHttpConnectionFactory&lt;span style="color:#719e07">(&lt;/span>username&lt;span style="color:#719e07">,&lt;/span> password&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pushJobExecutor&lt;span style="color:#719e07">.&lt;/span>scheduleWithFixedDelay&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> push&lt;span style="color:#719e07">(&lt;/span>pushGateway&lt;span style="color:#719e07">,&lt;/span> job&lt;span style="color:#719e07">),&lt;/span> pushInterval&lt;span style="color:#719e07">,&lt;/span> pushInterval&lt;span style="color:#719e07">,&lt;/span> TimeUnit&lt;span style="color:#719e07">.&lt;/span>SECONDS&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">push&lt;/span>&lt;span style="color:#719e07">(&lt;/span>PushGateway pushGateway&lt;span style="color:#719e07">,&lt;/span> String job&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pushGateway&lt;span style="color:#719e07">.&lt;/span>pushAdd&lt;span style="color:#719e07">(&lt;/span>prometheusRegistry&lt;span style="color:#719e07">.&lt;/span>getPrometheusRegistry&lt;span style="color:#719e07">(),&lt;/span> job&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>IOException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Error occurred when pushing metrics to prometheus: &amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="可视化展示">可视化展示&lt;/h2>
&lt;p>目前推荐使用 Prometheus 来进行服务监控,Grafana 来展示指标数据。可以通过案例来快速入门 &lt;a href="https://dubbo.apache.org/zh-cn/overview/tasks/observability/grafana/">Dubbo 可视化监控&lt;/a>。&lt;/p></description></item><item><title>Blog: Dubbo 在 Proxyless Mesh 模式下的探索与改进</title><link>https://dubbo.apache.org/zh-cn/blog/2023/02/02/dubbo-%E5%9C%A8-proxyless-mesh-%E6%A8%A1%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%8E%A2%E7%B4%A2%E4%B8%8E%E6%94%B9%E8%BF%9B/</link><pubDate>Thu, 02 Feb 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/02/02/dubbo-%E5%9C%A8-proxyless-mesh-%E6%A8%A1%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%8E%A2%E7%B4%A2%E4%B8%8E%E6%94%B9%E8%BF%9B/</guid><description>
&lt;h2 id="一背景">一、背景&lt;/h2>
&lt;p>随着 Docker 和 Kubernetes 的出现,一个庞大的单体应用可以被拆分成多个独立部署的微服务,并被打包运行于对应的容器中。不同应用之间相互通信,以共同完成某一功能模块。微服务架构与容器化部署带来的好处是显而易见的,它降低了服务间的耦合性,利于开发和维护,能更有效地利用计算资源。当然,微服务架构也存在相应的缺点:&lt;/p>
&lt;ul>
&lt;li>强依赖于 SDK,业务模块与治理模块耦合较为严重。 除了相关依赖,往往还需要在业务代码中嵌入SDK代码或配置。&lt;/li>
&lt;li>统一治理难。每次框架升级都需要修改 SDK 版本,并重新进行回归测试,确认功能正常后再对每一台机器重新部署上线。不同服务引用的 SDK 版本不统一、能力参差不齐,增大了统一治理的难度。&lt;/li>
&lt;li>缺少一套统一解决方案。目前市场不存在一整套功能完善、无死角的微服务治理与解决方案。在实际生产环境往往还需要引入多个治理组件来完成像灰度发布、故障注入等功能。&lt;/li>
&lt;/ul>
&lt;p>为解决这些痛点,Service Mesh诞生了。以经典的 Sidecar 模式为例,它通过在业务 Pod 中注入 Sidecar 容器,对代理流量实施治理和管控,将框架的治理能力下层到 Sidecar 容器中,与业务系统解耦,从而轻松实现多语言、多协议的统一流量管控、监控等需求。通过剥离SDK能力并拆解为独立进程,从而解决了强依赖于SDK的问题,从而使开发人员可以更加专注于业务本身,实现了基础框架能力的下沉,如下图所示(源自dubbo官网):
&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/2/1.png" alt="image.png">&lt;/p>
&lt;p>经典的 Sidecar Mesh 部署架构有很多优势,如减少SDK耦合、业务侵入小等,但增加了一层代理,也带来了一些额外的问题,比如:&lt;/p>
&lt;ul>
&lt;li>Sidecar 代理会损耗一部分性能,当网络结构层级比较复杂时尤其明显,对性能要求很高的业务造成了一定的困扰。&lt;/li>
&lt;li>架构更加复杂,对运维人员要求高。&lt;/li>
&lt;li>对部署环境有一定的要求,需要其能支持 Sidecar 代理的运行。&lt;/li>
&lt;/ul>
&lt;p>为解决这些痛点,Proxyless Service Mesh 模式诞生了。传统服务网格通过代理的方式拦截所有的业务网络流量,代理需要感知到控制平面下发的配置资源,从而按照要求控制网络流量的走向。以istio为例,Proxyless 模式是指应用直接与负责控制平面的 istiod 进程通信,istiod 进程通过监听并获取 Kubernetes 的资源,例如 Service、Endpoint 等,并将这些资源统一通过 xDS 协议下发到不同的 RPC 框架,由 RPC 框架进行请求转发,从而实现服务发现和服务治理等能力。
Dubbo 社区是国内最早开始对 Proxyless Service Mesh 模式进行探索的社区,这是由于相比于 Service Mesh,Proxyless 模式落地成本较低,对于中小企业来说是一个较好的选择。Dubbo 在 3.1 版本中通过对 xDS 协议进行解析,新增了对 Proxyless 的支持。xDS 是一类发现服务的总称,应用通过 xDS API 可以动态获取 Listener(监听器)、Route(路由)、Cluster(集群)、Endpoint(集群成员) 以及 Secret(证书)配置。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/2/2.jpeg" alt="image.png">&lt;/p>
&lt;p>通过 Proxyless 模式,Dubbo 与 Control Plane 直接建立通信,进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控,从而规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="二dubbo-xds-推送机制详解">二、Dubbo xDS 推送机制详解&lt;/h2>
&lt;p>从整体上看,istio control plane 和 dubbo 的交互时序图如上。Dubbo 里 xDS 处理的主要逻辑在 PilotExchanger 和各个 DS (LDS、RDS、CDS、EDS) 的对应协议的具体实现里。PilotExchanger 统一负责串联逻辑,主要有三大逻辑:&lt;/p>
&lt;ul>
&lt;li>获取授信证书。&lt;/li>
&lt;li>调用不同 protocol 的 getResource 获取资源。&lt;/li>
&lt;li>调用不同 protocol 得 observeResource 方法监听资源变更。&lt;/li>
&lt;/ul>
&lt;p>例如对于 lds 和 rds,PilotExchanger 会调用 lds 的 getResource 方法与 istio 建立通信连接,发送数据并解析来自 istio 的响应,解析完成后的 resource 资源会作 为rds 调用 getResource 方法的入参,并由 rds 发送数据给 istio。当 lds 发生变更时,则由 lds 的 observeResource 方法去触发自身与 rds 的变更。上述关系对于 rds 和 eds 同样如此。现有交互如下,上述过程对应图里红线的流程:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/2/3.jpeg" alt="image.png">&lt;/p>
&lt;p>在第一次成功获取资源之后,各个DS会通过定时任务去不断发送请求给 istio,并解析响应结果和保持与 istio 之间的交互,进而实现控制面对流量管控、服务治理、可观测性方面的管控,其流程对应上图蓝线部分。&lt;/p>
&lt;h2 id="三当前dubbo-proxyless实现存在的不足">三、当前Dubbo Proxyless实现存在的不足&lt;/h2>
&lt;p>Dubbo Proxyless 模式经过验证之后,已经证明了其可靠性。现有 Dubbo proxyless 的实现方案存在以下问题:&lt;/p>
&lt;ul>
&lt;li>目前与 istio 交互的逻辑是推送模式。getResource 和 observeResource 是两条不同的 stream 流,每次发送新请求都需要重新建立连接。但我们建立的 stream 流是双向流动的,istio 在监听到资源变化后由主动推送即可,LDS、RDS、EDS 分别只需要维护一条 stream 流。&lt;/li>
&lt;li>Stream 流模式改为建立持久化连接之后,需要设计一个本地的缓存池,去存储已经存在的资源。当 istio 主动推送更新后,需要去刷新缓存池的数据。&lt;/li>
&lt;li>现有observeResource 逻辑是通过定时任务去轮询istio。现在 observeResource 不再需要定时去轮询,只需要将需要监听的资源加入到缓存池,等 istio 自动推送即可,且 istio 推送回来的数据需要按照 app 切分好,实现多点监听,后续 dubbo 支持其他 DS 模式,也可复用相应的逻辑。&lt;/li>
&lt;li>目前由 istio 托管的 dubbo 应用在 istio 掉线后会抛出异常,断线后无法重新连接,只能重新部署应用,增加了运维和管理的复杂度。我们需增加断线重连的功能,等 istio 恢复正常后无需重新部署即可重连。&lt;/li>
&lt;/ul>
&lt;p>改造完成后的交互逻辑:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/2/4.jpeg" alt="image.png">&lt;/p>
&lt;h2 id="四xds-监听模式实现方案">四、Xds 监听模式实现方案&lt;/h2>
&lt;h3 id="41-资源缓存池">4.1 资源缓存池&lt;/h3>
&lt;p>目前 Dubbo 的资源类型有LDS,RDS,EDS。对于同一个进程,三种资源监听的所有资源都与 istio 对该进程所缓存的资源监听列表一一对应。因此针对这三种资源,我们应该设计分别对应的本地的资源缓存池,dubbo 尝试资源的时候先去缓存池查询,若有结果则直接返回;否则将本地缓存池的资源列表与想要发送的资源聚合后,发送给 istio 让其更新自身的监听列表。缓存池如下,其中 key 代表单个资源,T 为不同 DS 的返回结果:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> resourcesMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>有了缓存池我们必须有一个监听缓存池的结构或者容器,在这里我们设计为 Map 的形式,如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> consumerObserveMap &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConcurrentHashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中key为想要监听的资源,value 为一个 List, 之所以设计为 List 是为了可以支持重复订阅。 List 存储的 item 为 jdk8 中的 Consumer 类型,它可以用于传递一个函数或者行为,其入参为 Map&amp;lt;String, T&amp;gt;,其 key 对应所要监听的单个资源,便于从缓存池中获取。如上文所述,PilotExchanger 负责串联整个流程,不同 DS 之间的更新关系可以用 Consumer 进行传递。以监听 LDS observeResource 为例, 大致代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 监听
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">observeResource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> resourceNames&lt;span style="color:#719e07">,&lt;/span> Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&lt;/span> consumer&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> isReConnect&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Observe LDS updated
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>ldsProtocol&lt;span style="color:#719e07">.&lt;/span>observeResource&lt;span style="color:#719e07">(&lt;/span>ldsResourcesName&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>newListener&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// LDS数据不一致
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>newListener&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>listenerResult&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//更新LDS数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>listenerResult &lt;span style="color:#719e07">=&lt;/span> newListener&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 触发RDS监听
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>isRdsObserve&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createRouteObserve&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">},&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Stream流模式改为建立持久化连接之后,我们也需要把这个 Consumer 的行为存储在本地的缓存池中。Istio 收到来自 dubbo 的推送请求后,刷新自身缓存的资源列表并返回响应。此时 istio 返回的响应内容是聚合后的结果,Dubbo 收到响应后,将响应资源拆分为更小的资源粒度,再推送给对应的 Dubbo应用通知其进行变更。&lt;/p>
&lt;p>踩坑点:&lt;/p>
&lt;ul>
&lt;li>istio推送的数据可能为空字符串,此时缓存池子无需存储,直接跳过即可。否则dubbo会绕过缓冲池,不断向istio发送请求。&lt;/li>
&lt;li>考虑以下场景,dubbo应用同时订阅了两个接口,分别由app1和app2提供。为避免监听之间的相互覆盖,因此向istio发送数据时,需要聚合所有监听的资源名一次性发起。&lt;/li>
&lt;/ul>
&lt;h3 id="42-多点独立监听">4.2 多点独立监听&lt;/h3>
&lt;p>在第一次向istio发送请求时会调用getResource方法先去cache查询,缺失了再聚合数据去istio请求数据,istio再返回相应的结果给dubbo。我们处理istio的响应有两种实现方案:&lt;/p>
&lt;ol>
&lt;li>由用户在getResource方案中new 一个completeFuture,由cache分析是否是需要的数据,若确认是新数据则由该future回调传递结果。&lt;/li>
&lt;li>getResource建立资源的监听器consumerObserveMap,定义一个consumer并把取到的数据同步到原来的线程,cache 收到来自istio的推送后会做两件事:将数据推送所有监听器和将数据发送给该资源的监听器。
以上两种方案都能实现,但最大的区别就是用户调用onNext发送数据给istio的时候需不需要感知getResource 的存在。综上,最终选择方案2进行实现。具体实现逻辑是让dubbo与istio建立连接后,istio会推送自身监听到资源列表给dubbo,dubbo解析响应,并根据监听的不同app切分数据,并刷新本地缓存池的数据,并发送ACK响应给istio,大致流程如下:&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/2/2/5.svg" alt="image.png">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ResponseObserver&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> XXX &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onNext&lt;/span>&lt;span style="color:#719e07">(&lt;/span>DiscoveryResponse value&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//接受来自istio的数据并切分
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> newResult &lt;span style="color:#719e07">=&lt;/span> decodeDiscoveryResponse&lt;span style="color:#719e07">(&lt;/span>value&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地缓存池数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> oldResource &lt;span style="color:#719e07">=&lt;/span> resourcesMap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新缓存池数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> discoveryResponseListener&lt;span style="color:#719e07">(&lt;/span>oldResource&lt;span style="color:#719e07">,&lt;/span> newResult&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resourcesMap &lt;span style="color:#719e07">=&lt;/span> newResult&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// for ACK
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> requestObserver&lt;span style="color:#719e07">.&lt;/span>onNext&lt;span style="color:#719e07">(&lt;/span>buildDiscoveryRequest&lt;span style="color:#719e07">(&lt;/span>Collections&lt;span style="color:#719e07">.&lt;/span>emptySet&lt;span style="color:#719e07">(),&lt;/span> value&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">discoveryResponseListener&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> oldResult&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> newResult&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">....&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">//具体实现交由LDS、RDS、EDS自身
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#268bd2">abstract&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">decodeDiscoveryResponse&lt;/span>&lt;span style="color:#719e07">(&lt;/span>DiscoveryResponse response&lt;span style="color:#719e07">){&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//比对新数据和缓存池的资源,并将不同时存在于两种池子的资源取出
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">.&lt;/span>Entry&lt;span style="color:#719e07">&amp;lt;&lt;/span>Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;,&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Consumer&lt;span style="color:#719e07">&amp;lt;&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> entry &lt;span style="color:#719e07">:&lt;/span> consumerObserveMap&lt;span style="color:#719e07">.&lt;/span>entrySet&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 本地缓存池不存在则跳过
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//聚合资源
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> T&lt;span style="color:#719e07">&amp;gt;&lt;/span> dsResultMap &lt;span style="color:#719e07">=&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getKey&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>collect&lt;span style="color:#719e07">(&lt;/span>Collectors&lt;span style="color:#719e07">.&lt;/span>toMap&lt;span style="color:#719e07">(&lt;/span>k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> k&lt;span style="color:#719e07">,&lt;/span> v &lt;span style="color:#719e07">-&amp;gt;&lt;/span> newResult&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>v&lt;span style="color:#719e07">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新缓存池数据
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> entry&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">().&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>o &lt;span style="color:#719e07">-&amp;gt;&lt;/span> o&lt;span style="color:#719e07">.&lt;/span>accept&lt;span style="color:#719e07">(&lt;/span>dsResultMap&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>踩坑点:&lt;/p>
&lt;ul>
&lt;li>原本多个stream流的情况下,会用递增的requestId来复用stream流,改成持久化连接之后,一种resource会有多个requestid,可能会相互覆盖,因此必须去掉这个机制。&lt;/li>
&lt;li>初始实现方案并没有对资源进行切分,而是一把梭,考虑到后续对其他DS的支持,对istio返回的数据进行切分,也导致consumerObserveMap有点奇形怪状。&lt;/li>
&lt;li>三种DS在发送数据时可以共享同一channel,但监听所用到的必须是同一channel,否则数据变更时istio不会进行推送。&lt;/li>
&lt;li>建立双向stream流之后,初始方案future为全局共享。但可能有这样的场景:相同的ds两次相邻时间的onNext事件,记为A事件和B事件,可能是A事件先发送,B随后;但可能是B事件的结果先返回,不确定istio推送的时间,因此future必须是局部变量而不是全局共享。&lt;/li>
&lt;/ul>
&lt;h3 id="43-采用读写锁避免并发冲突">4.3 采用读写锁避免并发冲突&lt;/h3>
&lt;p>监听器consumerObserveMap和缓存池resourcesMap均可能产生并发冲突。对于resourcemap,由于put操作都集中在getResource方法,因此可以采用悲观锁就能锁住相应的资源,避免资源的并发监听。对于consumerObserveMap,同时存在put、remove和遍历操作,从时序上,采用读写锁可规避冲突,对于遍历操作加读锁,对于put和remove操作加写锁,即可避免并发冲突。综上,resourcesMap加悲观锁即可,consumerObserveMap涉及的操作场景如下:&lt;/p>
&lt;ul>
&lt;li>远程请求istio时候会往consumerObserveMap 新增数据,加写锁。&lt;/li>
&lt;li>CompleteFuture跨线程返回数据后,去掉监听future,加写锁。&lt;/li>
&lt;li>监听缓存池时会往consumerObserveMap新增监听,加写锁。&lt;/li>
&lt;li>断线重连时会往consumerObserveMap新增监听,加写锁。&lt;/li>
&lt;li>解析istio返回的数据,遍历缓存池并刷新数据,加读锁。&lt;/li>
&lt;/ul>
&lt;p>踩坑点:&lt;/p>
&lt;ul>
&lt;li>由于dubbo和istio建立的是是双向stream流,相同的ds两次相邻时间的onNext事件,记为A事件和B事件,可能是A事件先发送,B随后;但可能是B事件的结果先返回,不确定istio推送的时间。因此需要加锁。&lt;/li>
&lt;/ul>
&lt;h3 id="44-断线重连">4.4 断线重连&lt;/h3>
&lt;p>断线重连只需要用定时任务去定时与istio交互,尝试获取授信证书,证书获取成功即可视为istio成功重新上线,dubbo会聚合本地的资源去istio请求数据,并解析响应和刷新本地缓存池数据,最后再关闭定时任务。
踩坑点:&lt;/p>
&lt;ul>
&lt;li>采用全局共享的定时任务池,不能进行关闭,否则会影响其他业务。&lt;/li>
&lt;li>&lt;/li>
&lt;/ul>
&lt;h2 id="五感想与总结">五、感想与总结&lt;/h2>
&lt;p>在这次功能的改造中,笔者着实掉了一波头发,怎么找bug也找不到的情形不在少数。除了上述提到的坑点之外,其他的坑点包括但不局限于:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>dubbo在某一次迭代里更改了获取k8s证书的方式,授权失败。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>原本的功能没问题,merge了下master代码,grpc版本与envoy版本不兼容,各种报错,最后靠降低版本成功解决。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>原本的功能没问题,merge了下master代码,最新分支代码里metadataservice发成了triple,然而在Proxyless模式下只支持dubbo协议,debug了三四天,最后发现需要增加配置。&lt;/p>
&lt;p>&amp;hellip;&amp;hellip;
但不得不承认,Proxyless Service Mesh确实有它自身的优势和广阔的市场前景。自dubbo3.1.0 release版本之后,dubbo已经实现了Proxyless Service Mesh能力,未来dubbo社区将深度联动业务,解决更多实际生产环境中的痛点,更好地完善service mesh能力。&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Blog: Dubbo 3.1.5、3.2.0-beta.4 正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/30/dubbo-3.1.53.2.0-beta.4-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/30/dubbo-3.1.53.2.0-beta.4-%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo 3.1.5 版本是目前 Dubbo 3 的最新稳定版本,我们建议所有的用户都升级到最新的稳定版本。Dubbo 3.2.0-beta.4 版本是目前 Dubbo 3 的最新特性版本,包括了如 Spring Boot 3、JDK 17、服务粒度的线程池隔离等新特性的支持,欢迎大家尝鲜使用。&lt;/p>
&lt;h1 id="dubbo-315">Dubbo 3.1.5&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/3-1-5.png" alt="image.png">&lt;/p>
&lt;h3 id="新特性">新特性&lt;/h3>
&lt;ul>
&lt;li>Dubbo QoS 支持记录请求的记录,便于进行审计&lt;/li>
&lt;li>支持在服务映射失败以后定时进行重试,降低由于元数据中心抖动带来的影响&lt;/li>
&lt;li>支持在初始化 Nacos Client 的时候进行健康检查,如果失败则在一定次数限制下进行重试,降低由于 Nacos 性能问题带来的稳定性影响&lt;/li>
&lt;li>支持序列化类检查机制,默认开启日志告警模式&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复资源加载器的日志级别&lt;/li>
&lt;li>修复 Dubbo 配置类对 Scope Model 进行懒加载,避免触发非预期的默认模块初始化&lt;/li>
&lt;li>修复 ReferenceConfig 中获取 ClassLoader 的逻辑&lt;/li>
&lt;li>修复 Metadata Service 在获取订阅服务列表时出现 NPE 的问题&lt;/li>
&lt;li>修复对接 Spring Cloud Rest 模式的时候 Metadata 配置覆盖的问题&lt;/li>
&lt;li>修复 Spring 懒加载时可能出现死锁的问题&lt;/li>
&lt;li>修复端口重复的无效日志&lt;/li>
&lt;li>修复 Active Limit Filter 不生效的问题&lt;/li>
&lt;li>修复服务映射时 Nacos CAS 检查写入无效的问题&lt;/li>
&lt;li>修复 Zookeeper 注册中心对接的应用级服务发现在服务发布的时候出现单节点服务找不到的问题&lt;/li>
&lt;li>修复服务映射在冲突以后未等待导致的冲突率高的问题&lt;/li>
&lt;li>修复应用级服务发现下节点更新失败的问题&lt;/li>
&lt;li>修复应用级配置覆盖不生效的问题&lt;/li>
&lt;li>修复在应用级地址刷新之后原 Revision 的元数据无法获取的问题&lt;/li>
&lt;li>修复 Zookeeper 注册中心在应用级服务发现下退订阅后无法重订阅的问题&lt;/li>
&lt;li>兼容 Nacos 在频繁刷新时最终一致性错误的问题&lt;/li>
&lt;li>关闭 Nacos 本地缓存获取的开关&lt;/li>
&lt;li>修复 Triple 传递大写 Attachment 无效的问题&lt;/li>
&lt;li>修复 Triple 处理特定类反序列化错误的问题&lt;/li>
&lt;li>修复 Protobuf 依赖不存在时抛出非预期异常的问题&lt;/li>
&lt;li>修复 CountDown 功能无效的问题&lt;/li>
&lt;li>修复 Triple 在反序列化时类加载器未切换的问题&lt;/li>
&lt;/ul>
&lt;h3 id="faq">FAQ&lt;/h3>
&lt;p>本次发布中有 5 个提交涉及异常日志 FAQ 的完善。关于错误码机制请参考官网&lt;a href="https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/faq/intro/">错误码机制介绍&lt;/a>一文。&lt;/p>
&lt;h3 id="代码优化">代码优化&lt;/h3>
&lt;p>本次发布中有 4 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="依赖升级">依赖升级&lt;/h3>
&lt;ul>
&lt;li>升级 protobuf-java: 3.16.3 -&amp;gt; 3.18.3&lt;/li>
&lt;li>升级 fastjson2: 2.0.21 -&amp;gt; 2.0.23&lt;/li>
&lt;/ul>
&lt;h3 id="贡献者">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献:@win120a, @wuwen5, @AlbumenJ, @CrazyHZM, @EarthChen, @xieshouyu, @wxbty&lt;/p>
&lt;h3 id="新贡献者">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@xieshouyu 在 PR #11177 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="dubbo-320-beta4">Dubbo 3.2.0-beta.4&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/3-2-0-beta-4.png" alt="image.png">&lt;/p>
&lt;p>&lt;strong>注:Dubbo 3.2.0-beta.4 的代码基础和 Dubbo 3.1.5 完全一致,因此在 Dubbo 3.1.5 中包括的所有修改内容,在 Dubbo 3.2.0-beta.4 中也同样存在,后续说明中对于重复的内容讲不再赘述。&lt;/strong>&lt;/p>
&lt;h3 id="新特性-1">新特性&lt;/h3>
&lt;ul>
&lt;li>支持 bytebuddy 作为动态代理的生成平台&lt;/li>
&lt;li>支持采集超时指标进行上报&lt;/li>
&lt;li>完善 Metrics 上报应用名&lt;/li>
&lt;li>完善 Metrics 上报指标的前缀&lt;/li>
&lt;li>支持上报请求失败的指标&lt;/li>
&lt;li>完善 xDS 的监听逻辑&lt;/li>
&lt;li>完善 xDS 对接路由规则的能力&lt;/li>
&lt;/ul>
&lt;h3 id="bugfix-1">Bugfix&lt;/h3>
&lt;ul>
&lt;li>修复 Token 请求在 Rest 协议下不生效的问题&lt;/li>
&lt;li>修复 GraalVM Native Image 的配置&lt;/li>
&lt;li>修复配置类无法进行序列化的问题&lt;/li>
&lt;li>修复 IPv6 地址段检查无效的问题&lt;/li>
&lt;li>修复路由链切换时地址可能存在异常的情况&lt;/li>
&lt;li>修复缓存文件锁被异常删除的问题&lt;/li>
&lt;li>修复 Deployer Listener 并发修改的问题&lt;/li>
&lt;/ul>
&lt;h3 id="性能优化">性能优化&lt;/h3>
&lt;ul>
&lt;li>优化 ConcurrentHashMap 的使用方式,避免在 JDK 1.8 下出现性能衰减&lt;/li>
&lt;/ul>
&lt;h3 id="代码优化-1">代码优化&lt;/h3>
&lt;p>本次发布中有 8 个提交涉及代码质量的优化。&lt;/p>
&lt;h3 id="依赖升级-1">依赖升级&lt;/h3>
&lt;ul>
&lt;li>升级 protobuf-java: 3.18.3 -&amp;gt; 3.19.6&lt;/li>
&lt;li>升级 cglib-nodep: 2.2 -&amp;gt; 2.2.2&lt;/li>
&lt;li>升级 byte-buddy: 1.12.19 -&amp;gt; 1.12.22&lt;/li>
&lt;li>升级 bouncycastle-bcprov_version: 1.69 -&amp;gt; 1.70&lt;/li>
&lt;li>升级 javax.ws.rs-api: 2.0 -&amp;gt; 2.1.1&lt;/li>
&lt;li>升级 curator_version: 4.2.0 -&amp;gt; 4.3.0&lt;/li>
&lt;li>升级 maven-plugin-plugin: 3.6.0 -&amp;gt; 3.7.1&lt;/li>
&lt;li>升级 javax.el: 3.0.1-b08 -&amp;gt; 3.0.1-b12&lt;/li>
&lt;li>升级 slf4j-api: 1.7.25 -&amp;gt; 1.7.36&lt;/li>
&lt;li>升级 spring-boot-dependencies: 2.3.1.RELEASE -&amp;gt; 2.7.7&lt;/li>
&lt;li>升级 maven-enforcer-plugin: 3.0.0-M3 -&amp;gt; 3.1.0&lt;/li>
&lt;li>升级 javassist: 3.28.0-GA -&amp;gt; 3.29.2-GA&lt;/li>
&lt;li>升级 spring-boot-maven-plugin: 2.1.4.RELEASE -&amp;gt; 2.7.7&lt;/li>
&lt;li>升级 javax.el-api: 2.2.4 -&amp;gt; 2.2.5&lt;/li>
&lt;li>升级 eureka.version: 1.9.12 -&amp;gt; 1.10.18&lt;/li>
&lt;li>升级 jetty-maven-plugin: 9.4.38.v20210224 -&amp;gt; 9.4.50.v20221201&lt;/li>
&lt;li>升级 jetty_version: 9.4.43.v20210629 -&amp;gt; 9.4.50.v20221201&lt;/li>
&lt;li>升级 resteasy_version: 3.0.20.Final -&amp;gt; 3.15.3.Final&lt;/li>
&lt;/ul>
&lt;h3 id="贡献者-1">贡献者&lt;/h3>
&lt;p>Dubbo 感谢以下贡献者对本次发布的贡献:@aamingaa, @AlbumenJ, @CrazyHZM, @fomeiherz, @HHoflittlefish777, @icodening, @jojocodeX, @LXPWing, @MentosL, @mxsm, @schneiderlin, @sconvent, @ShenFeng312, @songxiaosheng, @TigerTurbo, @yuluo-yx, @zeusbee&lt;/p>
&lt;h3 id="新贡献者-1">新贡献者&lt;/h3>
&lt;ul>
&lt;li>@LXPWing 在 PR #11258 提交了第一个贡献&lt;/li>
&lt;li>@HHoflittlefish777 在 PR #11266 提交了第一个贡献&lt;/li>
&lt;li>@sconvent 在 PR #11260 提交了第一个贡献&lt;/li>
&lt;li>@yuluo-yx 在 PR #11291 提交了第一个贡献&lt;/li>
&lt;li>@fomeiherz 在 PR #11295 提交了第一个贡献&lt;/li>
&lt;li>@schneiderlin 在 PR #11324 提交了第一个贡献&lt;/li>
&lt;li>@mxsm 在 PR #11326 提交了第一个贡献&lt;/li>
&lt;/ul>
&lt;h1 id="未来版本规划">未来版本规划&lt;/h1>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/release/release-roadmap.png" alt="image.png">&lt;/p>
&lt;p>Dubbo 版本的发布规划以及在《&lt;a href="https://mp.weixin.qq.com/s?__biz=MzIwODYwNTA4MA==&amp;amp;mid=2247484424&amp;amp;idx=1&amp;amp;sn=2f5ff4846f7dafad325f78fd8cf4d1fc&amp;amp;chksm=9701deffa07657e9a46eb97bb859770b4856599566b992724013a848a730f394702938e72404&amp;amp;token=1547029975&amp;amp;lang=zh_CN#rd">聚焦稳定性,Dubbo 发版规划公布&lt;/a>》一文中正式发布,欢迎查看。&lt;/p></description></item><item><title>Blog: Dubbo3 应用级服务发现设计</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/30/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E8%AE%BE%E8%AE%A1/</link><pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/30/dubbo3-%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E8%AE%BE%E8%AE%A1/</guid><description>
&lt;h2 id="objective">Objective&lt;/h2>
&lt;ul>
&lt;li>显著降低服务发现过程的资源消耗,包括提升注册中心容量上限、降低消费端地址解析资源占用等,使得 Dubbo3 框架能够支持更大规模集群的服务治理,实现无限水平扩容。&lt;/li>
&lt;li>适配底层基础设施服务发现模型,如 Kubernetes、Service Mesh 等。&lt;/li>
&lt;/ul>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/arc.png" alt="interface-arc">&lt;/p>
&lt;p>我们从 Dubbo 最经典的工作原理图说起,Dubbo 从设计之初就内置了服务地址发现的能力,Provider 注册地址到注册中心,Consumer 通过订阅实时获取注册中心的地址更新,在收到地址列表后,consumer 基于特定的负载均衡策略发起对 provider 的 RPC 调用。&lt;/p>
&lt;p>在这个过程中:&lt;/p>
&lt;ul>
&lt;li>每个 Provider 通过特定的 key 向注册中心注册本机可访问地址;&lt;/li>
&lt;li>注册中心通过这个 key 对 provider 实例地址进行聚合;&lt;/li>
&lt;li>Consumer 通过同样的 key 从注册中心订阅,以便及时收到聚合后的地址列表;&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/interface-data1.png" alt="interface-data1">&lt;/p>
&lt;p>这里,我们对接口级地址发现的内部数据结构进行详细分析。&lt;/p>
&lt;p>首先,看右下角 provider 实例内部的数据与行为。Provider 部署的应用中通常会有多个 Service,也就是 Dubbo2 中的服务,每个 service 都可能会有其独有的配置,我们所讲的 service 服务发布的过程,其实就是基于这个服务配置生成地址 URL 的过程,生成的地址数据如图所示;同样的,其他服务也都会生成地址。&lt;/p>
&lt;p>然后,看一下注册中心的地址数据存储结构,注册中心以 service 服务名为数据划分依据,将一个服务下的所有地址数据都作为子节点进行聚合,子节点的内容就是实际可访问的ip地址,也就是我们 Dubbo 中 URL,格式就是刚才 provider 实例生成的。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/interface-data2.png" alt="interface-data2">&lt;/p>
&lt;p>这里把 URL 地址数据划分成了几份:&lt;/p>
&lt;ul>
&lt;li>首先是实例可访问地址,主要信息包含 ip port,是消费端将基于这条数据生成 tcp 网络链接,作为后续 RPC 数据的传输载体&lt;/li>
&lt;li>其次是 RPC 元数据,元数据用于定义和描述一次 RPC 请求,一方面表明这条地址数据是与某条具体的 RPC 服务有关的,它的版本号、分组以及方法相关信息,另一方面表明&lt;/li>
&lt;li>下一部分是 RPC 配置数据,部分配置用于控制 RPC 调用的行为,还有一部分配置用于同步 Provider 进程实例的状态,典型的如超时时间、数据编码的序列化方式等。&lt;/li>
&lt;li>最后一部分是自定义的元数据,这部分内容区别于以上框架预定义的各项配置,给了用户更大的灵活性,用户可任意扩展并添加自定义元数据,以进一步丰富实例状态。&lt;/li>
&lt;/ul>
&lt;p>结合以上两页对于 Dubbo2 接口级地址模型的分析,以及最开始的 Dubbo 基本原理图,我们可以得出这么几条结论:&lt;/p>
&lt;ul>
&lt;li>第一,地址发现聚合的 key 就是 RPC 粒度的服务&lt;/li>
&lt;li>第二,注册中心同步的数据不止包含地址,还包含了各种元数据以及配置&lt;/li>
&lt;li>得益于 1 与 2,Dubbo 实现了支持应用、RPC 服务、方法粒度的服务治理能力&lt;/li>
&lt;/ul>
&lt;p>这就是一直以来 Dubbo2 在易用性、服务治理功能性、可扩展性上强于很多服务框架的真正原因。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/interface-defect.png" alt="interface-defect">&lt;/p>
&lt;p>一个事物总是有其两面性,Dubbo2 地址模型带来易用性和强大功能的同时,也给整个架构的水平可扩展性带来了一些限制。这个问题在普通规模的微服务集群下是完全感知不到的,而随着集群规模的增长,当整个集群内应用、机器达到一定数量时,整个集群内的各个组件才开始遇到规模瓶颈。在总结包括阿里巴巴、工商银行等多个典型的用户在生产环境特点后,我们总结出以下两点突出问题(如图中红色所示):&lt;/p>
&lt;ul>
&lt;li>首先,注册中心集群容量达到上限阈值。由于所有的 URL 地址数据都被发送到注册中心,注册中心的存储容量达到上限,推送效率也随之下降。&lt;/li>
&lt;li>而在消费端这一侧,Dubbo2 框架常驻内存已超 40%,每次地址推送带来的 cpu 等资源消耗率也非常高,影响正常的业务调用。&lt;/li>
&lt;/ul>
&lt;p>为什么会出现这个问题?我们以一个具体 provider 示例进行展开,来尝试说明为何应用在接口级地址模型下容易遇到容量问题。
青蓝色部分,假设这里有一个普通的 Dubbo Provider 应用,该应用内部定义有 10 个 RPC Service,应用被部署在 100 个机器实例上。这个应用在集群中产生的数据量将会是 “Service 数 * 机器实例数”,也就是 10 * 100 = 1000 条。数据被从两个维度放大:&lt;/p>
&lt;ul>
&lt;li>从地址角度。100 条唯一的实例地址,被放大 10 倍&lt;/li>
&lt;li>从服务角度。10 条唯一的服务元数据,被放大 100 倍&lt;/li>
&lt;/ul>
&lt;h2 id="proposal">Proposal&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/app-principle.png" alt="app-principle">&lt;/p>
&lt;p>面对这个问题,在 Dubbo3 架构下,我们不得不重新思考两个问题:&lt;/p>
&lt;ul>
&lt;li>如何在保留易用性、功能性的同时,重新组织 URL 地址数据,避免冗余数据的出现,让 Dubbo3 能支撑更大规模集群水平扩容?&lt;/li>
&lt;li>如何在地址发现层面与其他的微服务体系如 Kubernetes、Spring Cloud 打通?&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/app-data1.png" alt="app-data1">&lt;/p>
&lt;p>Dubbo3 的应用级服务发现方案设计本质上就是围绕以上两个问题展开。其基本思路是:地址发现链路上的聚合元素也就是我们之前提到的 Key 由服务调整为应用,这也是其名称叫做应用级服务发现的由来;另外,通过注册中心同步的数据内容上做了大幅精简,只保留最核心的 ip、port 地址数据。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/app-data2.png" alt="app-data2">&lt;/p>
&lt;p>这是升级之后应用级地址发现的内部数据结构进行详细分析。
对比之前接口级的地址发现模型,我们主要关注橙色部分的变化。首先,在 provider 实例这一侧,相比于之前每个 RPC Service 注册一条地址数据,一个 provider 实例只会注册一条地址到注册中心;而在注册中心这一侧,地址以应用名为粒度做聚合,应用名节点下是精简过后的 provider 实例地址;&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/app-metadataservice.png" alt="app-metadataservice">&lt;/p>
&lt;p>应用级服务发现的上述调整,同时实现了地址单条数据大小和总数量的下降,但同时也带来了新的挑战:我们之前 Dubbo2 强调的易用性和功能性的基础损失了,因为元数据的传输被精简掉了,如何精细的控制单个服务的行为变得无法实现。&lt;/p>
&lt;p>针对这个问题,Dubbo3 的解法是引入一个内置的 MetadataService 元数据服务,由中心化推送转为 Consumer 到 Provider 的点对点拉取,在这个模式下,元数据传输的数据量将不在是一个问题,因此可以在元数据中扩展出更多的参数、暴露更多的治理数据。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/proposals/discovery/app-workflow.png" alt="app-metadataservice">&lt;/p>
&lt;p>这里我们个重点看消费端 Consumer 的地址订阅行为,消费端从分两步读取地址数据,首先是从注册中心收到精简后的地址,随后通过调用 MetadataService 元数据服务,读取对端的元数据信息。在收到这两部分数据之后,消费端会完成地址数据的聚合,最终在运行态还原出类似 Dubbo2 的 URL 地址格式。因此从最终结果而言,应用级地址模型同时兼顾了地址传输层面的性能与运行层面的功能性。&lt;/p>
&lt;p>以上就是的应用级服务发现背景、工作原理部分的所有内容,接下来我们看一下饿了么升级到 Dubbo3 尤其是应用级服务发现的过程。&lt;/p></description></item><item><title>Blog: 启发式流控制</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/30/%E5%90%AF%E5%8F%91%E5%BC%8F%E6%B5%81%E6%8E%A7%E5%88%B6/</link><pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/30/%E5%90%AF%E5%8F%91%E5%BC%8F%E6%B5%81%E6%8E%A7%E5%88%B6/</guid><description>
&lt;h1 id="整体介绍">整体介绍&lt;/h1>
&lt;p>本文所说的柔性服务主要是指&lt;strong>consumer端的负载均衡&lt;/strong>和&lt;strong>provider端的限流&lt;/strong>两个功能。在之前的dubbo版本中,&lt;/p>
&lt;ul>
&lt;li>负载均衡部分更多的考虑的是公平性原则,即consumer端尽可能平等的从provider中作出选择,在某些情况下表现并不够理想。&lt;/li>
&lt;li>限流部分只提供了静态的限流方案,需要用户对provider端设置静态的最大并发值,然而该值的合理选取对用户来讲并不容易。&lt;/li>
&lt;/ul>
&lt;p>我们针对这些存在的问题进行了改进。&lt;/p>
&lt;h2 id="负载均衡">负载均衡&lt;/h2>
&lt;h3 id="使用介绍">使用介绍&lt;/h3>
&lt;p>在原本的dubbo版本中,有五种负载均衡的方案供选择,他们分别是 &lt;code>Random&lt;/code>、&lt;code>ShortestResponse&lt;/code>、&lt;code>RoundRobin&lt;/code>、&lt;code>LeastActive&lt;/code> 和 &lt;code>ConsistentHash&lt;/code>。其中除 &lt;code>ShortestResponse&lt;/code> 和 &lt;code>LeastActive&lt;/code> 外,其他的几种方案主要是考虑选择时的公平性和稳定性。&lt;/p>
&lt;p>对于 &lt;code>ShortestResponse&lt;/code> 来说,其设计目的是从所有备选的 provider 中选择 response 时间最短的以提高系统整体的吞吐量。然而存在两个问题:&lt;/p>
&lt;ol>
&lt;li>在大多数的场景下,不同provider的response时长没有非常明显的区别,此时该算法会退化为随机选择。&lt;/li>
&lt;li>response的时间长短有时也并不能代表机器的吞吐能力。对于 &lt;code>LeastActive&lt;/code> 来说,其认为应该将流量尽可能分配到当前并发处理任务较少的机器上。但是其同样存在和 &lt;code>ShortestResponse&lt;/code> 类似的问题,即这并不能单独代表机器的吞吐能力。&lt;/li>
&lt;/ol>
&lt;p>基于以上分析,我们提出了两种新的负载均衡算法。一种是同样基于公平性考虑的单纯 &lt;code>P2C&lt;/code> 算法,另一种是基于自适应的方法 &lt;code>adaptive&lt;/code>,其试图自适应的衡量 provider 端机器的吞吐能力,然后将流量尽可能分配到吞吐能力高的机器上,以提高系统整体的性能。&lt;/p>
&lt;h4 id="总体效果">总体效果&lt;/h4>
&lt;p>对于负载均衡部分的有效性实验在两个不同的情况下进行的,分别是提供端机器配置比较均衡和提供端机器配置差距较大的情况。&lt;/p>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/67956773/1675265258687-c3df68a8-80e0-4311-816c-63480494850c.png#clientId=u54f9e7eb-38a1-4&amp;amp;from=paste&amp;amp;height=226&amp;amp;id=ud2d81be9&amp;amp;name=image.png&amp;amp;originHeight=890&amp;amp;originWidth=1798&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=63793&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u9adb8df7-315a-4800-ac9f-888ba0d1c11&amp;amp;title=&amp;amp;width=457" alt="image.png">&lt;/p>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/67956773/1675265271198-5b045ced-8524-42a2-8b34-d7edbbd1f232.png#clientId=u54f9e7eb-38a1-4&amp;amp;from=paste&amp;amp;height=246&amp;amp;id=u4652443c&amp;amp;name=image.png&amp;amp;originHeight=890&amp;amp;originWidth=1798&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=57908&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u624f980f-f1de-43ed-a068-9f1c38ab171&amp;amp;title=&amp;amp;width=497" alt="image.png">&lt;/p>
&lt;h4 id="使用方法">使用方法&lt;/h4>
&lt;p>使用方法与原本的负载均衡方法相同。只需要在consumer端将&amp;quot;loadbalance&amp;quot;设置为&amp;quot;p2c&amp;quot;或者&amp;quot;adaptive&amp;quot;即可。&lt;/p>
&lt;h4 id="代码结构">代码结构&lt;/h4>
&lt;p>负载均衡部分的算法实现只需要在原本负载均衡框架内继承 LoadBalance接口即可。&lt;/p>
&lt;h3 id="原理介绍">原理介绍&lt;/h3>
&lt;h4 id="p2c算法">P2C算法&lt;/h4>
&lt;p>Power of Two Choice算法简单但是经典,主要思路如下:&lt;/p>
&lt;ol>
&lt;li>对于每次调用,从可用的provider列表中做两次随机选择,选出两个节点providerA和providerB。&lt;/li>
&lt;li>比较providerA和providerB两个节点,选择其“当前正在处理的连接数”较小的那个节点。&lt;/li>
&lt;/ol>
&lt;h4 id="adaptive算法">adaptive算法&lt;/h4>
&lt;p>&lt;a href="https://github.com/apache/dubbo/pull/10745">代码的github地址&lt;/a>&lt;/p>
&lt;h5 id="相关指标">相关指标&lt;/h5>
&lt;ol>
&lt;li>
&lt;p>cpuLoad
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/26808016bc7f1ee83ab425e308074f17.svg#card=math&amp;amp;code=cpuLoad%20%3D%20cpu%E4%B8%80%E5%88%86%E9%92%9F%E5%B9%B3%E5%9D%87%E8%B4%9F%E8%BD%BD%20%2A%20100%20%2F%20%E5%8F%AF%E7%94%A8cpu%E6%95%B0%E9%87%8F&amp;amp;id=DLuwW" alt="img">。该指标在provider端机器获得,并通过invocation的attachment传递给consumer端。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>rt
rt为一次rpc调用所用的时间,单位为毫秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>timeout
timeout为本次rpc调用超时剩余的时间,单位为毫秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>weight
weight是设置的服务权重。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>currentProviderTime
provider端在计算cpuLoad时的时间,单位是毫秒&lt;/p>
&lt;/li>
&lt;li>
&lt;p>currentTime
currentTime为最后一次计算load时的时间,初始化为currentProviderTime,单位是毫秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>multiple
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/b60f036bd026b92129df8a6476922cc8.svg#card=math&amp;amp;code=multiple%3D%28%E5%BD%93%E5%89%8D%E6%97%B6%E9%97%B4%20-%20currentTime%29%2Ftimeout%20%2B%201&amp;amp;id=VpE3k" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>lastLatency
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/f2abbc771049cf4f3e492e93a258d699.svg#card=math&amp;amp;code=%5Cbegin%7Balign%2A%7D%0A%5Cend%7Balign%2A%7D%0A%0A&amp;amp;id=ynJBf" alt="img">&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/8fb1af970b995232ebed2764a5706aab.svg#card=math&amp;amp;code=%5Cbegin%7Balign%2A%7D%0A%5Cbegin%7Bsplit%7D%0A%20%0AlastLatency%3D%20%5Cleft%20%5C%7B%0A%20%0A%5Cbegin%7Barray%7D%7Bll%7D%0A%20%0A%20%20%20%202%2Atimeout%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20currentTime%3D%3DcurrentProviderTime%5C%5C%0A%20%0A%20%20%20%20lastLatency%20%3E%3E%20multiple%2C%20%20%20%20%20%26%20otherwise%5C%5C%0A%20%0A%0A%5Cend%7Barray%7D%0A%20%0A%5Cright.%0A%20%0A%5Cend%7Bsplit%7D%0A%20%0A%5Cend%7Balign%2A%7D&amp;amp;id=xAu3F" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>beta
平滑参数,默认为0.5&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ewma
lastLatency的平滑值&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/c26fdbae56f3a06c46434ae91185a3d6.svg#card=math&amp;amp;code=lastLatency%3Dbeta%20%2A%20lastLatency%20%2B%20%281%20-%20beta%29%20%2A%20lastLatency&amp;amp;id=absgN" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>inflight
inflight为consumer端还未返回的请求的数量。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/f429c4726dec484e70ee73e6a37c88dd.svg#card=math&amp;amp;code=inflight%3DconsumerReq%20-%20consumerSuccess%20-%20errorReq&amp;amp;id=UZIcf" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>load
对于备选后端机器x来说,若距离上次被调用的时间大于2*timeout,则其load值为0。
否则,&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/0f56746b3643dc3ed0e019c24ad5f377.svg#card=math&amp;amp;code=load%20%3D%20CpuLoad%20%2A%20%28sqrt%28ewma%29%20%2B%201%29%20%2A%20%28inflight%20%2B%201%29%2F%28%28%28consumerSuccess%20%2F%20%28consumerReq%20%2B1%29%20%29%20%2A%20weight%29%2B1%29&amp;amp;id=TCYWX" alt="img">&lt;/p>
&lt;h5 id="算法实现">算法实现&lt;/h5>
&lt;p>依然是基于P2C算法。&lt;/p>
&lt;ol>
&lt;li>从备选列表中做两次随机选择,得到providerA和providerB&lt;/li>
&lt;li>比较providerA和providerB的load值,选择较小的那个。&lt;/li>
&lt;/ol>
&lt;h2 id="自适应限流">自适应限流&lt;/h2>
&lt;p>与负载均衡运行在consumer端不同的是,限流功能运行在provider端。其作用是限制provider端处理并发任务时的最大数量。从理论上讲,服务端机器的处理能力是存在上限的,对于一台服务端机器,当短时间内出现大量的请求调用时,会导致处理不及时的请求积压,使机器过载。在这种情况下可能导致两个问题:1.由于请求积压,最终所有的请求都必须等待较长时间才能被处理,从而使整个服务瘫痪。2.服务端机器长时间的过载可能有宕机的风险。因此,在可能存在过载风险时,拒绝掉一部分请求反而是更好的选择。在之前的dubbo版本中,限流是通过在provider端设置静态的最大并发值实现的。但是在服务数量多,拓扑复杂且处理能力会动态变化的局面下,该值难以通过计算静态设置。
基于以上原因,我们需要一种自适应的算法,其可以动态调整服务端机器的最大并发值,使其可以在保证机器不过载的前提下,尽可能多的处理接收到的请求。因此,我们参考brpc等其他框架的基础上,在dubbo的框架内实现了两种自适应限流算法,分别是基于启发式平滑的&amp;quot;HeuristicSmoothingFlowControl&amp;quot;和基于窗口的&amp;quot;AutoConcurrencyLimier&amp;quot;。&lt;/p>
&lt;p>&lt;a href="https://github.com/apache/dubbo/pull/10642">代码的github地址&lt;/a>&lt;/p>
&lt;h3 id="使用介绍-1">使用介绍&lt;/h3>
&lt;h4 id="总体效果-1">总体效果&lt;/h4>
&lt;p>自适应限流部分的有效性实验我们在提供端机器配置尽可能大的情况下进行,并且为了凸显效果,在实验中我们将单次请求的复杂度提高,将超时时间尽可能设置的大,并且开启消费端的重试功能。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/0/2023/png/67956773/1675267798831-3da99681-577f-4e5a-b122-b87c8aba7299.png#clientId=u54f9e7eb-38a1-4&amp;amp;from=paste&amp;amp;height=239&amp;amp;id=u4de83107&amp;amp;name=image.png&amp;amp;originHeight=800&amp;amp;originWidth=1680&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;size=53641&amp;amp;status=done&amp;amp;style=none&amp;amp;taskId=u948ff148-1ec8-42ec-9712-0655b1e0336&amp;amp;title=&amp;amp;width=502" alt="image.png">&lt;/p>
&lt;h4 id="使用方法-1">使用方法&lt;/h4>
&lt;p>要确保服务端存在多个节点,并且消费端开启重试策略的前提下,限流功能才能更好的发挥作用。&lt;/p>
&lt;p>设置方法与静态的最大并发值设置类似,只需在provider端将&amp;quot;flowcontrol&amp;quot;设置为&amp;quot;autoConcurrencyLimier&amp;quot;或者&amp;quot;heuristicSmoothingFlowControl&amp;quot;即可。&lt;/p>
&lt;h4 id="代码结构-1">代码结构&lt;/h4>
&lt;ol>
&lt;li>FlowControlFilter:在provider端的filter负责根据限流算法的结果来对provider端进行限流功能。&lt;/li>
&lt;li>FlowControl:根据dubbo的spi实现的限流算法的接口。限流的具体实现算法需要继承自该接口并可以通过dubbo的spi方式使用。&lt;/li>
&lt;li>CpuUsage:周期性获取cpu的相关指标&lt;/li>
&lt;li>HardwareMetricsCollector:获取硬件指标的相关方法&lt;/li>
&lt;li>ServerMetricsCollector:基于滑动窗口的获取限流需要的指标的相关方法。比如qps等。&lt;/li>
&lt;li>AutoConcurrencyLimier:自适应限流的具体实现算法。&lt;/li>
&lt;li>HeuristicSmoothingFlowControl:自适应限流的具体实现方法。&lt;/li>
&lt;/ol>
&lt;h3 id="原理介绍-1">原理介绍&lt;/h3>
&lt;h4 id="heuristicsmoothingflowcontrol">HeuristicSmoothingFlowControl&lt;/h4>
&lt;h5 id="相关指标-1">相关指标&lt;/h5>
&lt;ol>
&lt;li>
&lt;p>alpha
alpha为可接受的延时的上升幅度,默认为0.3&lt;/p>
&lt;/li>
&lt;li>
&lt;p>minLatency
在一个时间窗口内的最小的Latency值。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>noLoadLatency
noLoadLatency是单纯处理任务的延时,不包括排队时间。这是服务端机器的固有属性,但是并不是一成不变的。在HeuristicSmoothingFlowControl算法中,我们根据机器CPU的使用率来确定机器当前的noLoadLatency。当机器的CPU使用率较低时,我们认为minLatency便是noLoadLatency。当CPU使用率适中时,我们平滑的用minLatency来更新noLoadLatency的值。当CPU使用率较高时,noLoadLatency的值不再改变。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>maxQPS
一个时间窗口周期内的QPS的最大值。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>avgLatency
一个时间窗口周期内的Latency的平均值,单位为毫秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>maxConcurrency
计算得到的当前服务提供端的最大并发值。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/f40e48ebdb49648cf942714609808c52.svg#card=math&amp;amp;code=maxConcurrency%3Dceil%28maxQPS%20%2A%20%28%282%20%2B%20alpha%29%20%2A%20noLoadLatency%20-%20avgLatency%29%29&amp;amp;id=xO1h8" alt="img">&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h5 id="算法实现-1">算法实现&lt;/h5>
&lt;p>当服务端收到一个请求时,首先判断CPU的使用率是否超过50%。如果没有超过50%,则接受这个请求进行处理。如果超过50%,说明当前的负载较高,便从HeuristicSmoothingFlowControl算法中获得当前的maxConcurrency值。如果当前正在处理的请求数量超过了maxConcurrency,则拒绝该请求。&lt;/p>
&lt;h4 id="autoconcurrencylimier">AutoConcurrencyLimier&lt;/h4>
&lt;h5 id="相关指标-2">相关指标&lt;/h5>
&lt;ol>
&lt;li>
&lt;p>MaxExploreRatio
默认设置为0.3&lt;/p>
&lt;/li>
&lt;li>
&lt;p>MinExploreRatio
默认设置为0.06&lt;/p>
&lt;/li>
&lt;li>
&lt;p>SampleWindowSizeMs
采样窗口的时长。默认为1000毫秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>MinSampleCount
采样窗口的最小请求数量。默认为40。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>MaxSampleCount
采样窗口的最大请求数量。默认为500。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>emaFactor
平滑处理参数。默认为0.1。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>exploreRatio
探索率。初始设置为MaxExploreRatio。
若avgLatency&amp;lt;=noLoadLatency*(1.0 + MinExploreRatio)或者qps&amp;gt;=maxQPS*(1.0 + MinExploreRatio)
则exploreRatio=min(MaxExploreRatio,exploreRatio+0.02)
否则
exploreRatio=max(MinExploreRatio,exploreRatio-0.02)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>maxQPS
窗口周期内QPS的最大值。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/d5cf045bc17267befc176f3d76273267.svg#card=math&amp;amp;code=%5Cbegin%7Balign%2A%7D%0A%5Cbegin%7Bsplit%7D%0A%20%0AmaxQPS%3D%20%5Cleft%20%5C%7B%0A%20%0A%5Cbegin%7Barray%7D%7Bll%7D%0A%20%0A%20%20%20%20qps%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20qps%20%3E%20maxQPS%5C%5C%0A%20%0A%20%20%20%20qps%2AemaFactor%20%2B%20maxQPS%2A%281-emaFactor%29%2C%20%20%20%20%20%26%20otherwise%5C%5C%0A%20%0A%0A%5Cend%7Barray%7D%0A%20%0A%5Cright.%0A%20%0A%5Cend%7Bsplit%7D%0A%20%0A%5Cend%7Balign%2A%7D&amp;amp;id=VbdUd" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>noLoadLatency
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/8c700211f5c7a13403e3088df9cd9f43.svg#card=math&amp;amp;code=%5Cbegin%7Balign%2A%7D%0A%5Cbegin%7Bsplit%7D%0A%20%0AnoLoadLatency%3D%20%5Cleft%20%5C%7B%0A%20%0A%5Cbegin%7Barray%7D%7Bll%7D%0A%20%0A%20%20%20%20avgLatency%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20noLoadLatency%20%3C%3D%200%5C%5C%0A%20%0A%20%20%20%20avgLatency%2AemaFactor%20%2B%20noLoadLatency%2A%281%20-%20emaFactor%29%2C%20%20%20%20%20%26%20otherwise%5C%5C%0A%20%0A%0A%5Cend%7Barray%7D%0A%20%0A%5Cright.%0A%20%0A%5Cend%7Bsplit%7D%0A%20%0A%5Cend%7Balign%2A%7D&amp;amp;id=hB7ED" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>halfSampleIntervalMs
半采样区间。默认为25000毫秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>resetLatencyUs
下一次重置所有值的时间戳,这里的重置包括窗口内值和noLoadLatency。单位是微秒。初始为0.
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/1af4a6134ede96985302ee8a27f93df7.svg#card=math&amp;amp;code=resetLatencyUs%3DsamplingTimeUs%2B2%2AavgLatency%2C%0Aif%28remeasureStartUs%3C%3DsamplingTimeUs%29&amp;amp;id=vJHLa" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>remeasureStartUs
下一次重置窗口的开始时间。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/c7da904b9a4c890456499b09d01938d3.svg#card=math&amp;amp;code=remeasureStartUs%3DsamplingTimeUs%2B%28halfSampleIntervalMS%20%2B%20%E9%9A%8F%E6%9C%BA%E5%80%BC%29%2A1000&amp;amp;id=ket08" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>startSampleTimeUs
开始采样的时间。单位为微秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>sampleCount
当前采样窗口内请求的数量。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>totalSampleUs
采样窗口内所有请求的latency的和。单位为微秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>totalReqCount
采样窗口时间内所有请求的数量和。注意区别sampleCount。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>samplingTimeUs
采样当前请求的时间戳。单位为微秒。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>latency
当前请求的latency。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>qps
在该时间窗口内的qps值。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/c0e8b30fc1ecf9438bc2d574fb3da8b6.svg#card=math&amp;amp;code=qps%3DtotalReqCount%2A1000000%2F%28samplingTimeUs%20-%20startSampleTimeUs%29&amp;amp;id=tzGm6" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>avgLatency
窗口内的平均latency。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/3a3acfdb05be7d3985835d43e492d3b9.svg#card=math&amp;amp;code=avgLatency%3DtotalSampleUs%20%2F%20sampleCount&amp;amp;id=gTnsb" alt="img">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>maxConcurrency
上一个窗口计算得到当前周期的最大并发值。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>nextMaxConcurrency
当前窗口计算出的下一个周期的最大并发值。
&lt;img src="https://intranetproxy.alipay.com/skylark/lark/__latex/09852cc0ef125b43a37719796cb8baae.svg#card=math&amp;amp;code=%5Cbegin%7Balign%2A%7D%0A%5Cbegin%7Bsplit%7D%0A%20%0AnextMaxConcurrency%3D%20%5Cleft%20%5C%7B%0A%20%0A%5Cbegin%7Barray%7D%7Bll%7D%0A%20%0A%20%20%20%20ceil%28maxQPS%2AnoLoadLatency%2A0.9%2F1000000%29%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20remeasureStartUs%20%3C%3D%20samplingTimeUs%5C%5C%0A%20%0A%20%20%20%20ceil%28noLoadLatency%2AmaxQPS%2A%281%2BexploreRatio%29%2F1000000%29%2C%20%20%20%20%20%20otherwise%5C%5C%0A%20%0A%0A%5Cend%7Barray%7D%0A%20%0A%5Cright.%0A%20%0A%5Cend%7Bsplit%7D%0A%20%0A%5Cend%7Balign%2A%7D&amp;amp;id=kLKle" alt="img">&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h5 id="littles-law">Little&amp;rsquo;s Law&lt;/h5>
&lt;ul>
&lt;li>当服务处于稳定状态时:concurrency=latency*qps。这是自适应限流理论的基础。&lt;/li>
&lt;li>当请求没有导致机器超载时,latency基本稳定,qps和concurrency处于线性关系。&lt;/li>
&lt;li>当短时间内请求数量过多,导致服务超载的时候,concurrency会和latency一起上升,qps则会趋于稳定。&lt;/li>
&lt;/ul>
&lt;h5 id="算法实现-2">算法实现&lt;/h5>
&lt;p>AutoConcurrencyLimier的算法使用过程和HeuristicSmoothingFlowControl类似。与HeuristicSmoothingFlowControl的最大区别是:&lt;/p>
&lt;p>AutoConcurrencyLimier是基于窗口的。每当窗口内积累了一定量的采样数据时,才利用窗口内的数据来更新得到maxConcurrency。
其次,利用exploreRatio来对剩余的容量进行探索。&lt;/p>
&lt;p>另外,每隔一段时间都会自动缩小max_concurrency并持续一段时间,以处理noLoadLatency上涨的情况。因为估计noLoadLatency时必须先让服务处于低负载的状态,因此对maxConcurrency的缩小是难以避免的。&lt;/p>
&lt;p>由于 max_concurrency &amp;lt; concurrency 时,服务会拒绝掉所有的请求,限流算法将 &amp;ldquo;排空所有的经历过排队的等待请求的时间&amp;rdquo; 设置为 2*latency,以确保 minLatency 的样本绝大部分时没有经过排队等待的。&lt;/p></description></item><item><title>Blog: Dubbo 连接异构微服务体系 - 多协议&amp;多注册中心</title><link>https://dubbo.apache.org/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/01/05/dubbo-%E8%BF%9E%E6%8E%A5%E5%BC%82%E6%9E%84%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BD%93%E7%B3%BB-%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/</guid><description>
&lt;p>从编程开发的角度来说,Dubbo 首先是一款 RPC 服务框架,它最大的优势在于提供了面向接口代理的服务编程模型,对开发者屏蔽了底层的远程通信细节。同时 Dubbo 也是一款服务治理框架,它为分布式部署的微服务提供了服务发现、流量调度等服务治理解决方案。&lt;/p>
&lt;p>在这篇文章中,我们将以以上基础能力为背景,尝试突破 Dubbo 体系自身,探索如何利用 Dubbo 对多协议、多服务发现模型的支持,来实现异构微服务体系间的互联互通。在实际业务场景中,这可以用来解决异构技术体系共存场景下的通信问题,帮助公司实现在异构技术体系间作平滑迁移,解决大规模跨区域、多集群部署场景的地址发现及流量调度等问题。&lt;/p>
&lt;h2 id="面向接口代理的透明服务开发框架">面向接口代理的透明服务开发框架&lt;/h2>
&lt;p>我们还是从 &lt;strong>Dubbo 是一个微服务开发框架&lt;/strong> 这个大家熟知的概念开始。就像 Spring 是开发 Java 应用的基础框架一样,我们经常会选用 Dubbo 作为开发微服务业的基础框架。 Dubbo 框架的最大优势我认为就在其面向接口的编程模型,使得开发远程服务调用就像开发本地服务一样(以 Java 语言为例):&lt;/p>
&lt;ol>
&lt;li>服务定义&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingsService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHi&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费方调用服务&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 和调用本地服务一样,完全透明。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">@Reference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> GreetingService greetingService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doSayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greetingService&lt;span style="color:#719e07">.&lt;/span>sayHi&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello world!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>下图是 Dubbo 的基本工作原理图,服务提供者与服务消费者之间通过注册中心协调地址,通过约定的协议实现数据交换。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img.png" alt="Dubbo basic work flow">&lt;/p>
&lt;h2 id="同构异构微服务体系面临的问题">同构/异构微服务体系面临的问题&lt;/h2>
&lt;p>关于 Dubbo 协议本身及其服务治理相关功能细节并不是本文的重点,我们今天将从一个更高的层次,来看看公司内部构建微服务体系所面的挑战,以及 Dubbo 能为架构选型和迁移等提供哪些解决思路。&lt;/p>
&lt;p>一个公司内部的微服务可能都是基于某一个相同的服务框架开发的,比如说 Dubbo,对于这样的架构,我们称之为是&lt;strong>同构的微服务体系&lt;/strong>;而有些公司的微服务可能是使用多个不同的服务框架所建设,我们称之为&lt;strong>异构的微服务体系&lt;/strong>,多个不同技术栈微服务体系的共存在大型组织内还是非常普遍的,造成这种局面可能有很多原因。比如,可能是遗留系统带来的,也可能是公司正在做技术栈迁移,或者就是不同业务部门为了满足各自特殊需求而做的独立选型(这也意味着异构微服务体系的长期共存)。&lt;/p>
&lt;p>&lt;strong>1. 异构微服务体系共存&lt;/strong>&lt;/p>
&lt;p>我们很容易想到的一个挑战是:**不同的体系间通常是使用不同的 RPC 通信协议、部署独立的注册中心集群,面对这种多协议、多注册中心集群的场景,要如何实现相互之间透明的地址发现和透明的 RPC 调用?**如果我们什么都不做,那么每个微服务体系就只能感知到自己体系内的服务状态,流量也在各自的体系内封闭。而要做到从体系 A 平滑的迁移到体系 B,或者想长期的保持公司内部多个体系的共存,则解决不同体系间的互联互通,实现流量的透明调度将是非常重要的环节。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_1.png" alt="2">&lt;/p>
&lt;p>&lt;strong>2. Dubbo 体系内部&lt;/strong>&lt;/p>
&lt;p>&lt;strong>多协议、多注册中心集群的问题在同构的微服务体系中也可能存在,尤其是当一个组织内部的微服务规模增长到一定量级的时候。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>我们可能要在不同的服务之间采用不同的通信协议,因为不同的服务面临不同的业务场景,而这也进一步导致了数据传输特点的不同,我们需要分别采用更适合各类业务特点的协议。比如典型的场景:我们可能对于普通的业务服务采用 Dubbo 协议,对于和 FrontEnd 交互的服务需要 HTTP 协议,而对于需要流式数据传输的业务则采用 gRPC 协议等等。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Dubbo 体系内部另一个常出现的问题是,在大规模分布式部署的场景下,微服务系统会做跨区域、跨注册中心的部署,这个时候就会出现多集群间地址同步和流量调度的问题。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>总结起来,**不论是同构体系还是异构体系,都面临对多协议通信、多注册中心集群地址发现的问题。**Dubbo 目前是支持多协议、多注册中心的,可以说就是为解决我们上面分析的 Dubbo 同构体系内的场景而设计的,因此下面我们从同构体系的多协议、多注册中心场景讲起,先了解 Dubbo 多协议、多注册中心的基本支持情况以及它们是如何工作的。而在后面的一章再进一步探索怎么扩展这个能力来支持异构微服务体系的互联互通。&lt;/p>
&lt;h2 id="dubbo-体系内的多协议多注册中心机制">Dubbo 体系内的多协议、多注册中心机制&lt;/h2>
&lt;p>我们将通过两个场景示例,来分别具体的讲一下 Dubbo 的多协议、多注册中心机制的使用方式和工作原理。&lt;/p>
&lt;h3 id="多协议">多协议&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_2.png" alt="undefined">&lt;/p>
&lt;p>以上是使用 Dubbo 开发的一套微服务,服务间通信使用到了不同的协议,根据我们的调研发现,公司内部启用多协议其实是非常普遍需求,具体场景在此我们暂不做解释。&lt;/p>
&lt;p>应用 B 作为服务提供者,发布了 5 个服务,其中:&lt;/p>
&lt;ul>
&lt;li>&lt;code>DemoService1&lt;/code> &lt;code>DemoService2&lt;/code> 通过 &lt;code>dubbo&lt;/code> 协议发布&lt;/li>
&lt;li>&lt;code>DemoService3&lt;/code> &lt;code>DemoService4&lt;/code> 通过 &lt;code>gRPC&lt;/code> 协议发布&lt;/li>
&lt;li>&lt;code>DemoService0&lt;/code> 通过 &lt;code>dubbo&lt;/code> 、&lt;code>gRPC&lt;/code> 双协议发布&lt;/li>
&lt;/ul>
&lt;p>应用 A 作为消费者,使用 dubbo 协议消费 &lt;code>DemoService1&lt;/code> &lt;code>DemoService2&lt;/code>,使用 gRPC 协议消费 &lt;code>DemoService0&lt;/code>。&lt;/p>
&lt;p>应用 B 作为消费者,使用 gRPC 协议消费 &lt;code>DemoService2&lt;/code> &lt;code>DemoService4&lt;/code>,使用 dubbo 协议消费 &lt;code>DemoService0&lt;/code>。&lt;/p>
&lt;p>以下是具体的代码配置:&lt;/p>
&lt;ol>
&lt;li>提供端应用 B&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService1&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService2&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService3&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService4&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService0&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo, grpc&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>消费端应用 A&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService1&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService2&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService0&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>消费端应用 C&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService3&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;grpc&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService4&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.basic.api.DemoService0&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo-多协议支持现状">Dubbo 多协议支持现状&lt;/h4>
&lt;p>Dubbo 目前所支持的协议包括 Dubbo、REST、Thrift、gRPC、JsonRPC、Hessian 等,基本涵盖了业界大多数主流的 RPC 通信协议。需要注意的是,这些协议的支持都是以直接集成官方 Release 实现的形式来做的,我认为这是一个很好的选择,既保证了协议解析自身的稳定性,又能使 Dubbo 社区更专注的将更多的精力放在 Dubbo 外围服务治理能力的改善上。试想如果 Dubbo 社区自己为每个协议提供实现,那是要花费多少精力和时间才能使每种协议达到稳定的生产可用。&lt;/p>
&lt;p>除了以上官方提供支持的协议之外,得益于 Dubbo 灵活的扩展机制,想要为 Dubbo 扩展协议非常容易,开发者可以随时为 Dubbo 增加更多的协议支持,包括自有协议扩展。&lt;/p>
&lt;p>关于对 gRPC (HTTP/2) 协议的支持,请参阅上期文档&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_3.png" alt="3">&lt;/p>
&lt;h4 id="多协议能解决的问题">多协议能解决的问题&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>将 RPC 框架无缝地接入 Dubbo 的服务治理体系。&lt;/p>
&lt;p>通过协议扩展将 RPC 协议纳入 Dubbo 服务开发体系,从而复用 Dubbo 的编程模型和服务发现、流量管控等能力。比如 gRPC,其服务治理体系相对比较弱、编程 API 不够友好,很难直接用于微服务开发。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>满足不同场景的调用需求。&lt;/p>
&lt;p>各个服务可能是为了满足不同业务需求而开发,同时外围消费端应用的技术栈也可能多种多样,通过启用不同的通信协议,可以最优化不同场景的通信需求。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>实现协议间的迁移。&lt;/p>
&lt;p>通过支持多种协议,借助注册中心的协调,可以快速满足公司内协议迁移的需求。如从自有协议升级到 Dubbo 协议,Dubbo 协议自身升级,从 Dubbo 协议迁移到 gRPC,从 REST 迁移到 Dubbo 协议等。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="多注册中心">多注册中心&lt;/h3>
&lt;p>当服务集群规模小的时候,一个中心化的集群部署方案能很好的解决我们的业务问题。但是随着应用规模的增长、用户流量的增加,我们就不得不考虑要为业务系统引入跨区域、多集群的部署方案,而此时同业务系统密切相关的注册中心集群也面临部署方案的选型:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>继续维持全局共享的注册中心集群。这种架构方案的优点是简单;缺点是注册中心集群由于要保存全量的地址数据,存储和推送压力会变得很大,另外对于一些注册中心产品(如 Zookeeper 等)在跨集群网络部署的场景下稳定性和性能可能都会面临挑战。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>每个业务集群部署独立的注册中心集群。多注册中心集群的优点是能解决跨集群网络可用性的问题,同时也能够减轻注册中心的存储和推送压力;缺点则是要求服务框架(如 Dubbo 等)能有同时发布/监听多个注册中心集群的能力。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>下面我们具体看一下,Dubbo 为多注册中心集群场景提供的解决方案。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_4.png" alt="4">&lt;/p>
&lt;p>上图有两个业务集群,分别部署在北京和上海,每个业务集群有自己独立的注册中心集群,要解决两个业务集群间服务的透明 RPC 通信问题。&lt;/p>
&lt;ol>
&lt;li>服务提供端,双注册中心发布&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> default=&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address2}&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.HelloService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;helloService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry,beijingRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.DemoService&amp;#34;&lt;/span> ref=&lt;span style="color:#2aa198">&amp;#34;demoService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry,beijingRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>服务消费端,根据消费需求做单/双注册中心订阅&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> default=&lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span> preferred=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">&amp;#34;100&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span> &lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address2}&amp;#34;&lt;/span> default=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">&amp;#34;20&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.DemoService&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.DemoService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry, shanghaiRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.HelloService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;beijingRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.multi.registry.api.HelloService&amp;#34;&lt;/span> registry=&lt;span style="color:#2aa198">&amp;#34;shanghaiRegistry,shanghaiRegistry&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="dubbo-对异构注册中心集群的支持">Dubbo 对异构注册中心集群的支持&lt;/h4>
&lt;p>虽然我们会做多注册中心集群部署,但通常情况下,我们部署的都是相同的注册中心产品,如都是 Zookeeper、Nacos;而对于注册中心迁移的场景,则要求 Dubbo 能提供对更多的注册中心产品的支持,或者最重要的要有很好的扩展能力。Dubbo 官方目前支持的注册中心实现有:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_5.png" alt="5">&lt;/p>
&lt;p>这里需要特别提到的一点是,当前 Dubbo 的服务注册/发现模型是以接口为粒度的,而从 2.7.5 版本开始,Dubbo 新引入了应用粒度的服务注册/发现模型。这一方面有助于优化 Dubbo 当前服务发现机制、提升服务容量,另一方面对于联通以 SpringCloud 为代表的微服务体系也非常重要(关于这点在下一章中有进一步提及)。更多关于《应用粒度服务发现:服务自省》的介绍,我们将在接下来的文章或文档中予以补充,请持续关注。&lt;/p>
&lt;h4 id="多订阅带来的流量调度问题">多订阅带来的流量调度问题&lt;/h4>
&lt;p>在引入多注册中心集群后,Dubbo 在流量选址时的多了一层注册中心集群间的负载均衡:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_6.png" alt="6">&lt;/p>
&lt;p>在 Cluster Invoker 这一级,我们支持的选址策略有(2.7.5+ 版本,具体使用请参见文档):&lt;/p>
&lt;ul>
&lt;li>
&lt;p>指定优先级&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 来自 preferred=“true” 注册中心的地址将被优先选择,只有该中心无可用地址时才 Fallback 到其他注册中心 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> preferred=&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>同 zone 优先&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 选址时会和流量中的 zone key 做匹配,流量会优先派发到相同 zone 的地址 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> zone=&lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>权重轮询&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&amp;lt;!-- 来自北京和上海集群的地址,将以 10:1 的比例来分配流量 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;beijing&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address1}&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">”100“&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:registry&lt;/span> id=&lt;span style="color:#2aa198">&amp;#34;shanghai&amp;#34;&lt;/span> address=&lt;span style="color:#2aa198">&amp;#34;zookeeper://${zookeeper.address2}&amp;#34;&lt;/span> weight=&lt;span style="color:#2aa198">”10“&lt;/span> &lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>默认,stick to 任意可用&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="多注册中心适用的场景">多注册中心适用的场景&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>同区域流量优先调度&lt;/p>
&lt;p>出于容灾或者服务伸缩性需求,服务/应用往往需要部署在多个独立的机房/区域,在每个区域有独立注册中心集群的场景下,实现同区域的流量优先调度就能很好的解决延迟和可用性问题。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>注册中心迁移&lt;/p>
&lt;p>公司的服务一直以来可能是存储在某一个注册中心,如 Zookeeper,但到了某个时间节点,因为各种各样的原因,当我们要迁移到另外的注册中心时,多注册中心模型能够保证平滑的迁移。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>异构系统互通&lt;/p>
&lt;p>不同微服务体系开发的服务,都封闭在各自的服务发现体系中,而通过统一的多注册中心模型,可以实现不同体系的服务互相发现。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="借助-dubbo-联通异构的微服务体系">借助 Dubbo 联通异构的微服务体系&lt;/h2>
&lt;p>上文我们提到了在组织内存在异构微服务体系的各种合理可能性,现在我们来具体看一下异构微服务体系的实际场景,以及使用 Dubbo 实现互联互通的解决方法。首先我们先通过一张图来看一下,联通异构的微服务体系具体是一个什么样的场景。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_7.png" alt="7">&lt;/p>
&lt;p>如上图所示,我们有部分微服务可以是基于 SpringCloud、gRPC、K8S 或者是自建体系构建的,他们各自之间默认是相互隔离无法联通的。当我们再构建一套基于 Dubbo 的微服务体系时,则利用 Dubbo 的多协议、多服务发现模型,我们就可以做到和各个微服务体系间的两两之间的互联互通。进一步的,如图中橙色箭头所示,依赖 Dubbo 体系作为桥接层,我们还可以实现两个异构微服务体系间的打通。&lt;/p>
&lt;p>对于以下几个示例场景,由于在地址发现层面目前没有统一的标准,我们暂且假设地址发现层面不同的体系建是没有障碍的,我们将重点关注迁移的基本流程以及通信协议环节。(关于地址发现部分,我们将在后续《服务自省:基于应用粒度的服务发现》之后再深入探讨)&lt;/p>
&lt;h3 id="dubbo-体系内的协议迁移共存">Dubbo 体系内的协议迁移(共存)&lt;/h3>
&lt;p>绝大多数开发者对 Dubbo 有这么一个固有认知:使用 Dubbo 开发微服务系统,则就要用 Dubbo 协议来作为服务间的通信协议才是最优方案。实际上,我们完全没有必要只束缚在 Dubbo RPC 协议上。Dubbo 作为微服务开发框架和 Dubbo 作为 RPC 协议这是两个概念,其实是完全可以分开来看待的,比如我们用 Dubbo 框架开发的业务系统,选用 rest、gRPC 通信是完全没有问题的(参加 Dubbo 支持的协议列表),具体用什么协议根据业务特点和技术规划才是最适合的。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_8.png" alt="8">&lt;/p>
&lt;p>当前在云原生、Mesh 的大背景下, HTTP1/2、gRPC 协议开始受到越来越多的关注,一方面原因自然是因为它们在标准化方面做的更好,得到的更多的网络设备和基础设施的支持,具备更好的通用性和穿透性。对于很多有云原生迁移意愿的企业来说,往此类协议迁移无疑将对之后的架构升级有更多的帮助。&lt;/p>
&lt;p>下图演示了在 Dubbo 体系内,从 Dubbo 协议向 gRPC 协议迁移的一个中间状态。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_9.png" alt="9">&lt;/p>
&lt;ul>
&lt;li>最左边的代表尚未迁移的老应用,这类应用在迁移过程中仍然要消费和提供 Dubbo 协议的服务。&lt;/li>
&lt;li>中间的代表处于迁移中的应用,他们中间可能有些是服务提供者,既要为左边的老系统提供提供 Dubbo 协议服务;又要为右边的新系统提供 gRPC 服务;因此他们都是双协议暴露服务。&lt;/li>
&lt;li>最右边则代表是新开发的或者已经迁移完成的应用,这个体系内已能完全用 gRPC 协议通信。&lt;/li>
&lt;li>最终度过中间态后,我们期望所有的应用都达到最左边应用的状态,实现完全的 gRPC 协议通信。&lt;/li>
&lt;/ul>
&lt;h3 id="spring-cloud-体系迁移到-dubbo-体系共存">Spring Cloud 体系迁移到 Dubbo 体系(共存)&lt;/h3>
&lt;p>如前文所述,由于 SpringCloud 和 Dubbo 间服务发现模型的问题,要两个体系间的地址互通需要 Dubbo 侧作相应的适配,关于这部分内容将在接下来的 2.7.5 版本《服务自省》部分发布,在此我们暂且认为已经打通。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2023/01/protocols/img_10.png" alt="10">&lt;/p>
&lt;p>Dubbo 体系内的部分应用作为透明的联通两个体系的关键节点,部分服务提供者应用要双协议发布、部分消费者应用要做到选定协议消费。由于老的 Spring Cloud 体系不允许做任何改动,因此联通两套体系的关键是 REST 协议,对 Dubbo 侧的应用来说:&lt;/p>
&lt;ul>
&lt;li>部分应用可能要以 REST 协议消费 SpringCloud 的服务;&lt;/li>
&lt;li>部分应用可能要暴露 REST 协议共 SpringCloud 消费;&lt;/li>
&lt;li>Dubbo 自有体系内则通过自己选定的协议通信,这里就比较灵活了,可以是 Dubbo、REST、gRPC 等其中的任一种。而如果选定 REST 协议则对于与 SpringCloud 体系的联通就变得更加自然了,因为两端的协议都是统一的。&lt;/li>
&lt;/ul>
&lt;p>对于消费 Spring Cloud 服务的应用,要配置服务 :&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:reference&lt;/span> interface =&lt;span style="color:#2aa198">&amp;#34;xxx.SpringService&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;rest&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对于提供服务给 Spring Cloud 侧消费的应用,则指定服务暴露为 rest 协议,或者双协议暴露(因如果这个服务还要被新体系内的应用调用到):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">&amp;lt;dubbo:service&lt;/span> interface=&lt;span style="color:#2aa198">&amp;#34;xxx.NewService&amp;#34;&lt;/span> protocol=&lt;span style="color:#2aa198">&amp;#34;rest,dubbo&amp;#34;&lt;/span>&lt;span style="color:#268bd2">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>作为 Dubbo 的维护者,虽然我们这里有明显的偏向性,讲的是从如何从 SpringCloud 体系迁移到 Dubbo 体系。但是反过来考虑,如果你已经或者即将选型 Dubbo 来开发微服务,则未来从 Dubbo 迁移到 SpringCloud 也是同样的思路,Dubbo 的多协议、多注册模型为双向迁移都提供了同样的灵活性。&lt;/p>
&lt;h3 id="自建体系迁移到-dubbo-体系共存">自建体系迁移到 Dubbo 体系(共存)&lt;/h3>
&lt;p>这个场景和上一节中讲到的的 SpringCloud 迁移有些类似,最大的区别在于 rest 协议是 Dubbo 官方默认提供支持的,而对于已有的微服务体系内的私有通信协议,则需要先要自己去扩展 Dubbo Protocol 来提供协议层面的支持。&lt;/p>
&lt;h2 id="总结与展望">总结与展望&lt;/h2>
&lt;p>要实现异构微服务体系间的共存或迁移,关键点在打通异构体系间的&lt;code>协议&lt;/code>与&lt;code>服务发现&lt;/code>,得益于 Dubbo 自身对多协议、多注册模型的支持,我们可以很容易的使 Dubbo 成为桥接异构微服务体系的中间层。熟悉 Dubbo 多协议实现细节的同学,可能会担心在服务数量较多的场景下,多协议注册会导致地址数量翻倍从而影响地址推送性能;另外在文中《借助 Dubbo 联通异构的微服务体系》一节,关于如何实现异构体系间的透明服务发现部分我们没有做详细的说明。关于涉及服务发现的这部分,我们将在接下来的文章中做具体阐述,看看 Dubbo 2.7.5 版本引入新的服务发现机制是如何解决这个问题的,请持续关注后续文章及 Dubbo 官方文档。&lt;/p></description></item><item><title>Blog: Dubbo 3 之 Triple 流控反压原理解析</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 28 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/28/dubbo-3-%E4%B9%8B-triple-%E6%B5%81%E6%8E%A7%E5%8F%8D%E5%8E%8B%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</guid><description>
&lt;p>Triple 是 Dubbo 3 提出的基于 HTTP2 的开放协议,
旨在解决 Dubbo 2 私有协议带来的互通性问题。
Triple 基于 HTTP/2 定制自己的流控,支持通过特定的异常通知客户端业务层服务端负载高情况,
保护了服务端被大流量击垮,提高系统高可用能力。&lt;/p>
&lt;h2 id="一流控反压现状">一、流控反压现状&lt;/h2>
&lt;p>客户端和服务器端在接收数据的时候有一个缓冲区来临时存储数据,
但是缓冲区的大小是有限制的,所以有可能会出现缓冲区溢出的情况,
HTTP 通过流控保护数据溢出丢失风险。&lt;/p>
&lt;h3 id="1http1-流控">1、HTTP/1 流控&lt;/h3>
&lt;p>在 HTTP/1.1 中,流量的控制依赖的是底层TCP协议,在客户端和服务器端建立连接的时候,
会使用系统默认的设置来建立缓冲区。在数据进行通信的时候,会告诉对方它的接收窗口的大小,
这个接收窗口就是缓冲区中剩余的可用空间。如果接收窗口大小为零,则说明接收方缓冲区已满,
则发送方将不再发送数据,直到客户端清除其内部缓冲区,然后请求恢复数据传输。&lt;/p>
&lt;h3 id="2http2-流控">2、HTTP/2 流控&lt;/h3>
&lt;p>HTTP/2 使用了多路复用机制,一个TCP连接可以有多个 HTTP/2 连接,
故在 HTTP/2 中,有更加精细的流控制机制,允许服务端实现自己数据流和连接级的流控制。
服务端与客户端第一次连接时,会通过发送 &lt;code>HTTP/2 SettingsFrame&lt;/code>设置初始化的流控窗口大小,
用于 &lt;code>Stream&lt;/code> 级别流控,默认为 65,535 字节。
定好流控窗口后,每次客户端发送数据就会减少流控窗口的大小,
服务端收到数据后会发送窗口更新包(&lt;code>WINDOW_UPDATE frame&lt;/code>)通知客户端更新窗口。
客户端收到窗口更新包后就会增加对应值的流控窗口,从而达到动态控制的目的。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/1.png" alt="1.png">&lt;/p>
&lt;h2 id="二triple流控反压">二、Triple流控反压&lt;/h2>
&lt;p>Netty 基于 HTTP/2 实现了基础的流控,当服务端负载过高,客户端发送窗口为 0 时,
新增请求就无法被发送出去,会在缓存到客户端待发送请求队列中,缓存数据过大,
就会造成客户端内存溢出,影响业务程序。&lt;/p>
&lt;p>Triple 基于 Netty 实现了 HTTP/2 协议,通过 &lt;code>HTTP/2 FlowController&lt;/code>接口统一封装,
在实现分为进站(inbound)和出站(outbound)两个维度的实现。
Triple 在 inbound 流量上使用了 Netty 的默认流控实现,
在 outbound 上实现了自己流控,基于服务端负载,
将服务端流量压力透传到客户端业务层,实现客户端的业务反压,暂停业务继续发送请求,
保护服务端不被大流量击垮。&lt;/p>
&lt;h3 id="1连接初始化">1、连接初始化&lt;/h3>
&lt;p>Triple在初次建立连接时,通过 &lt;code>TripleHttpProtocol&lt;/code> 初始化 HTTP/2 配置,
默认流控窗口 &lt;code>DEFAULT_WINDOW_INIT_SIZE = MIB_8&lt;/code>,
并在服务端和客户端加入自己的 outbound 流控接口。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/2.png" alt="2.png">&lt;/p>
&lt;h3 id="2inbound流控">2、Inbound流控&lt;/h3>
&lt;p>Inbound 流量会通过 &lt;code>DefaultHttpLocalFlowController&lt;/code> 的 &lt;code>consumeBytes&lt;/code> 方法实现流控窗口更新与发送。&lt;/p>
&lt;h4 id="1-入口传入http-流与更新数据大小">1) 入口传入HTTP 流与更新数据大小&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/3.png" alt="3.png">&lt;/p>
&lt;h4 id="2-找到对应连接实现数据消费">2) 找到对应连接实现数据消费&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/4.png" alt="4.png">&lt;/p>
&lt;h4 id="3-更新流控窗口">3) 更新流控窗口&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/5.png" alt="5.png">&lt;/p>
&lt;h4 id="4-发送流控更新数据包window_update">4) 发送流控更新数据包(window_update)&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/6.png" alt="6.png">&lt;/p>
&lt;h3 id="3outbound流控">3、Outbound流控&lt;/h3>
&lt;p>Outbound 通过 Triple 自己的流控实现 &lt;code>TriHttpRemoteFlowController&lt;/code>,
将服务端压力反馈到业务层,保护服务端被大流量击垮。&lt;/p>
&lt;h4 id="1-发送数据时判断是否还有窗口">1) 发送数据时判断是否还有窗口&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/7.png" alt="7.png">&lt;/p>
&lt;h4 id="2-窗口为0时抛出特定异常">2) 窗口为0时抛出特定异常&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/8.png" alt="8.png">&lt;/p>
&lt;h4 id="3-反馈客户端流控异常">3) 反馈客户端流控异常&lt;/h4>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/28/triple/9.png" alt="9.png">&lt;/p>
&lt;h3 id="4总结">4、总结&lt;/h3>
&lt;p>Triple 通过将底层客户端发送窗口为 0 场景封装为特定流控异常,
透传至客户端上层业务,阻止客户端业务继续数据发送,
有效的保护了服务端被大流量击垮和客户端的内存溢出的问题。&lt;/p>
&lt;h2 id="三未来展望">三、未来展望&lt;/h2>
&lt;p>目前 Triple 已经基本实现了流控反压能力,未来我们将深度联动业务,
基于业务负载自适应调整反压流控,
一是在 inbound 上将流控窗口包发送时机调整到服务端业务处理完成后,
二是在 outbound 流量上关联客户端业务层,动态调整客户端发送速率。
从而实现基于服务端业务负载动态反压流控机制。&lt;/p></description></item><item><title>Blog: Triple 协议支持 Java 异常回传的设计与实现</title><link>https://dubbo.apache.org/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><pubDate>Mon, 19 Dec 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/12/19/triple-%E5%8D%8F%E8%AE%AE%E6%94%AF%E6%8C%81-java-%E5%BC%82%E5%B8%B8%E5%9B%9E%E4%BC%A0%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">deal&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span>&lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//doSomething
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span>&lt;span style="color:#719e07">(&lt;/span>IGreeterException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> e&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或者通过ExceptionBuilder,把相关的异常对象返回给consumer:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>provider&lt;span style="color:#719e07">.&lt;/span>send&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ExceptionBuilders&lt;span style="color:#719e07">.&lt;/span>IGreeterExceptionBuilder&lt;span style="color:#719e07">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>setDescription&lt;span style="color:#719e07">(&lt;/span>&amp;#39;异常描述信息&amp;#39;&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在抛出异常后, 通过捕获和instanceof来判断特定的异常, 然后做相应的业务处理,例如:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greeterProxy&lt;span style="color:#719e07">.&lt;/span>echo&lt;span style="color:#719e07">(&lt;/span>REQUEST_MSG&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>IGreeterException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//做相应的处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 Dubbo 2.x 版本,可以通过上述方法来捕获 Provider 端的异常。
而随着云原生时代的到来, Dubbo 也开启了 3.0 的里程碑。&lt;/p>
&lt;p>Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生,
在 3.0 的许多特性中,很重要的一个改动就是支持&lt;strong>新的一代Rpc协议Triple&lt;/strong>。&lt;/p>
&lt;p>Triple 协议基于 HTTP 2.0 进行构建,对网关的穿透性强,&lt;strong>兼容 gRPC&lt;/strong>,
提供 Request Response、Request Streaming、Response Streaming、
Bi-directional Streaming 等通信模型;
从 Triple 协议开始,Dubbo 还支持基于 IDL 的服务定义。&lt;/p>
&lt;p>采用 Triple 协议的用户可以在 provider 端生成用户定义的异常信息,
记录异常产生的堆栈,triple 协议可保证将用户在客户端获取到异常的message。&lt;/p>
&lt;p>Triple 的回传异常会在 &lt;code>AbstractInvoker&lt;/code> 的 &lt;code>waitForResultIfSync&lt;/code>
中把异常信息堆栈统一封装成 &lt;code>RpcException&lt;/code>,
所有来自 Provider 端的异常都会被封装成 &lt;code>RpcException&lt;/code> 类型并抛出,
这会导致用户无法根据特定的异常类型捕获来自 Provider 的异常,
只能通过捕获 RpcException 异常来返回信息,
且 Provider 携带的异常 message 也无法回传,只能获取打印的堆栈信息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> greeterProxy&lt;span style="color:#719e07">.&lt;/span>echo&lt;span style="color:#719e07">(&lt;/span>REQUEST_MSG&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RpcException e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> e&lt;span style="color:#719e07">.&lt;/span>printStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>自定义异常信息在社区中的呼声也比较高,
因此本次改动将支持自定义异常的功能, 使得服务端能抛出自定义异常后被客户端捕获到。&lt;/p>
&lt;h2 id="dubbo异常处理简介">Dubbo异常处理简介&lt;/h2>
&lt;p>我们从Consumer的角度看一下一次Triple协议 Unary请求的大致流程:&lt;/p>
&lt;p>Dubbo Consumer 从 Spring 容器中获取 bean 时获取到的是一个代理接口,
在调用接口的方法时会通过代理类远程调用接口并返回结果。&lt;/p>
&lt;p>Dubbo提供的代理工厂类是 &lt;code>ProxyFactory&lt;/code>,通过 SPI 机制默认实现的是 &lt;code>JavassistProxyFactory&lt;/code>,
&lt;code>JavassistProxyFactory&lt;/code> 创建了一个继承自 &lt;code>AbstractProxyInvoker&lt;/code> 类的匿名对象,
并重写了抽象方法 &lt;code>doInvoke&lt;/code>。
重写后的 &lt;code>doInvoke&lt;/code> 只是将调用请求转发给了 &lt;code>Wrapper&lt;/code> 类的 &lt;code>invokeMethod&lt;/code> 方法,
并生成 &lt;code>invokeMethod&lt;/code> 方法代码和其他一些方法代码。&lt;/p>
&lt;p>代码生成完毕后,通过 &lt;code>Javassist&lt;/code> 生成 &lt;code>Class&lt;/code> 对象,
最后再通过反射创建 &lt;code>Wrapper&lt;/code> 实例,随后通过 &lt;code>InvokerInvocationHandler&lt;/code> -&amp;gt; &lt;code>InvocationUtil&lt;/code> -&amp;gt; &lt;code>AbstractInvoker&lt;/code> -&amp;gt; 具体实现类发送请求到Provider端。&lt;/p>
&lt;p>Provider 进行相应的业务处理后返回相应的结果给 Consumer 端,来自 Provider 端的结果会被封装成 &lt;code>AsyncResult&lt;/code> ,在 &lt;code>AbstractInvoker&lt;/code> 的具体实现类里,
接受到来自 Provider 的响应之后会调用 &lt;code>appResponse&lt;/code> 到 &lt;code>recreate&lt;/code> 方法,若 &lt;code>appResponse&lt;/code> 里包含异常,
则会抛出给用户,大体流程如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/1.jpeg" alt="1.jpeg">&lt;/p>
&lt;p>上述的异常处理相关环节是在 Consumer 端,在 Provider 端则是由 &lt;code>org.apache.dubbo.rpc.filter.ExceptionFilter&lt;/code> 进行处理,
它是一系列责任链 Filter 中的一环,专门用来处理异常。&lt;/p>
&lt;p>Dubbo 在 Provider 端的异常会在封装进 &lt;code>appResponse&lt;/code> 中。下面的流程图揭示了 &lt;code>ExceptionFilter&lt;/code> 源码的异常处理流程:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/2.jpeg" alt="2.jpeg">&lt;/p>
&lt;p>而当 &lt;code>appResponse&lt;/code> 回到了 Consumer 端,会在 &lt;code>InvocationUtil&lt;/code> 里调用 &lt;code>AppResponse&lt;/code> 的 &lt;code>recreate&lt;/code> 方法抛出异常,
最终可以在 Consumer 端捕获:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> Object &lt;span style="color:#268bd2">recreate&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Throwable &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>exception &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object stackTrace &lt;span style="color:#719e07">=&lt;/span> exception&lt;span style="color:#719e07">.&lt;/span>getStackTrace&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>stackTrace &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exception&lt;span style="color:#719e07">.&lt;/span>setStackTrace&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StackTraceElement&lt;span style="color:#719e07">[&lt;/span>0&lt;span style="color:#719e07">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> exception&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">return&lt;/span> result&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="triple-通信原理">Triple 通信原理&lt;/h2>
&lt;p>在上一节中,我们已经介绍了 Dubbo 在 Consumer 端大致发送数据的流程,
可以看到最终依靠的是 &lt;code>AbstractInvoker&lt;/code> 的实现类来发送数据。
在 Triple 协议中,&lt;code>AbstractInvoker&lt;/code> 的具体实现类是 &lt;code>TripleInvoker&lt;/code> ,
&lt;code>TripleInvoker&lt;/code> 在发送前会启动监听器,监听来自 Provider 端的响应结果,
并调用 &lt;code>ClientCallToObserverAdapter&lt;/code> 的 &lt;code>onNext&lt;/code> 方法发送消息,
最终会在底层封装成 Netty 请求发送数据。&lt;/p>
&lt;p>在正式的请求发起前,TripleServer 会注册 &lt;code>TripleHttp2FrameServerHandler&lt;/code>,
它继承自 Netty 的 &lt;code>ChannelDuplexHandler&lt;/code>,
其作用是会在 &lt;code>channelRead&lt;/code> 方法中不断读取 Header 和 Data 信息并解析,
经过层层调用,
会在 &lt;code>AbstractServerCall&lt;/code> 的 &lt;code>onMessage&lt;/code> 方法里把来自 consumer 的信息流进行反序列化,
并最终由交由 &lt;code>ServerCallToObserverAdapter&lt;/code> 的 &lt;code>invoke&lt;/code> 方法进行处理。&lt;/p>
&lt;p>在 &lt;code>invoke&lt;/code> 方法中,根据 consumer 请求的数据调用服务端相应的方法,并异步等待结果;'
若服务端抛出异常,则调用 &lt;code>onError&lt;/code> 方法进行处理,
否则,调用 &lt;code>onReturn&lt;/code> 方法返回正常的结果,大致代码逻辑如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">invoke&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用invoke方法请求服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Result response &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">.&lt;/span>invoke&lt;span style="color:#719e07">(&lt;/span>invocation&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//异步等待结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> response&lt;span style="color:#719e07">.&lt;/span>whenCompleteWithContext&lt;span style="color:#719e07">((&lt;/span>r&lt;span style="color:#719e07">,&lt;/span> t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//若异常不为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>t &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用方法过程出现异常,调用onError方法处理
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> responseObserver&lt;span style="color:#719e07">.&lt;/span>onError&lt;span style="color:#719e07">(&lt;/span>t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>response&lt;span style="color:#719e07">.&lt;/span>hasException&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//调用onReturn方法处理业务异常
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onReturn&lt;span style="color:#719e07">(&lt;/span>response&lt;span style="color:#719e07">.&lt;/span>getException&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//正常返回结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> onReturn&lt;span style="color:#719e07">(&lt;/span>r&lt;span style="color:#719e07">.&lt;/span>getValue&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>大体流程如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/3.jpeg" alt="3.jpeg">&lt;/p>
&lt;h2 id="实现版本">实现版本&lt;/h2>
&lt;p>了解了上述原理,我们就可以进行相应的改造了,
能让 consumer 端捕获异常的&lt;strong>关键在于把异常对象以及异常信息序列化后再发送给consumer端&lt;/strong>。
常见的序列化协议很多,例如 Dubbo/HSF 默认的 hessian2 序列化;
还有使用广泛的 JSON 序列化;以及 gRPC 原生支持的 protobuf(PB) 序列化等等。
Triple协议因为兼容grpc的原因,默认采用 Protobuf 进行序列化。
上述提到的这三种典型的序列化方案作用类似,但在实现和开发中略有不同。
PB 不可由序列化后的字节流直接生成内存对象,
而 Hessian 和 JSON 都是可以的。后两者反序列化的过程不依赖“二方包”,
其序列化和反序列化的代码由 proto 文件相同,只要客户端和服务端用相同的 proto 文件进行通信,
就可以构造出通信双方可解析的结构。&lt;/p>
&lt;p>单一的 protobuf 无法序列化异常信息,
因此我们采用 &lt;code>Wrapper + PB&lt;/code> 的形式进行序列化异常信息,
抽象出一个 &lt;code>TripleExceptionWrapperUtils&lt;/code> 用于序列化异常,
并在 &lt;code>trailer&lt;/code> 中采用 &lt;code>TripleExceptionWrapperUtils&lt;/code> 序列化异常,大致代码流程如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/4.jpeg" alt="4.jpeg">&lt;/p>
&lt;p>上面的实现方案看似非常合理,已经能把 Provider 端的异常对象和信息回传,
并在 Consumer 端进行捕获。但仔细想想还是有问题的:
通常在 HTTP2 为基础的通信协议里会对 header 大小做一定的限制,
太大的header size 会导致性能退化严重,为了保证性能,
往往以 HTTP2 为基础的协议在建立连接的时候是要协商最大 header size 的,
超过后会发送失败。对于 Triple 协议来说,在设计之初就是基于 HTTP 2.0,
能无缝兼容 Grpc,而 Grpc header 头部只有 8KB 大小,
异常对象大小可能超过限制,从而丢失异常信息;
且多一个 header 携带序列化的异常信息意味着用户能加的 header 数量会减少,
挤占了其他 header 所能占用的空间。&lt;/p>
&lt;p>经过讨论,考虑将异常信息放置在 Body,将序列化后的异常从 trailer 挪至 body,
采用 &lt;code>TripleWrapper + protobuf&lt;/code> 进行序列化,把相关的异常信息序列化后回传。
社区围绕这个问题进行了一系列的争论,读者也可尝试先思考一下:&lt;/p>
&lt;p>1.在 body 中携带回传的异常信息,其对应HTTP header状态码该设置为多少?&lt;/p>
&lt;p>2.基于 http2 构建的协议,按照主流的 grpc 实现方案,相关的错误信息放在 &lt;code>trailer&lt;/code>,理论上不存在body,上层协议也需要保持语义一致性,若此时在payload回传异常对象,且grpc并没有支持在Body回传序列化对象的功能, 会不会破坏Http和grpc协议的语义?从这个角度出发,异常信息更应该放在trailer里。&lt;/p>
&lt;p>3.作为开源社区,不能一味满足用户的需求,非标准化的用法注定是会被淘汰的,应该尽量避免更改 Protobuf的语义,是否在Wrapper层去支持序列化异常就能满足需求?&lt;/p>
&lt;p>首先回答第二、三个问题:HTTP 协议并没有约定在状态码非 2xx 的时候不能返回 body,返回之后是否读取取决于用户。grpc 采用protobuf进行序列化,所以无法返回 exception;且try catch机制为java独有,其他语言并没有对应的需求,但Grpc暂时不支持的功能并一定是unimplemented,Dubbo的设计目标之一是希望能和主流协议甚至架构进行对齐,但对于用户合理的需求也希望能进行一定程度的修改。且从throw本身的语义出发,throw 的数据不只是一个 error message,序列化的异常信息带有业务属性,根据这个角度,更不应该采用类似trailer的设计。至于单一的Wrapper层,也没办法和grpc进行互通。至于Http header状态码设置为200,因为其返回的异常信息已经带有一定的业务属性,不再是单纯的error,这个设计也与grpc保持一致,未来考虑网关采集可以增加新的triple-status。&lt;/p>
&lt;p>更改后的版本只需在异常不为空时返回相关的异常信息,采用 &lt;code>TripleWrapper + Protobuf&lt;/code> 进行序列化异常信息,并在consumer端进行解析和反序列化,大体流程如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/2022/12/19/triple/5.jpeg" alt="5.jpeg">&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>通过对 Dubbo 3.0 新增自定义异常的版本迭代中可以看出,尽管只能新增一个小小的特性,流程下并不复杂,但由于要考虑互通、兼容和协议的设计理念,因此思考和讨论的时间可能比写代码的时间更多。&lt;/p></description></item><item><title>Blog: Apache Dubbo 多语言体系再添新员:首个 Rust 语言版本正式发布</title><link>https://dubbo.apache.org/zh-cn/blog/2022/10/23/apache-dubbo-%E5%A4%9A%E8%AF%AD%E8%A8%80%E4%BD%93%E7%B3%BB%E5%86%8D%E6%B7%BB%E6%96%B0%E5%91%98%E9%A6%96%E4%B8%AA-rust-%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</link><pubDate>Sun, 23 Oct 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/10/23/apache-dubbo-%E5%A4%9A%E8%AF%AD%E8%A8%80%E4%BD%93%E7%B3%BB%E5%86%8D%E6%B7%BB%E6%96%B0%E5%91%98%E9%A6%96%E4%B8%AA-rust-%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83/</guid><description>
&lt;p>Dubbo Rust 定位为 Dubbo 多语言体系的重要实现,提供高性能、易用、可扩展的 RPC 框架,同时通过接入 Dubbo Mesh 体系提供丰富的服务治理能力。本文主要为大家介绍 Dubbo Rust 项目基本情况,通过一个示例快速体验 Rust 首个正式版本特性,并给出了 Dubbo Rust 社区的近期规划,适合于关注或正在采用 Rust 语言的开发者与企业用户阅读。&lt;/p>
&lt;h2 id="1-dubbo-rust-简介">1 Dubbo Rust 简介&lt;/h2>
&lt;p>Dubbo 作为 Apache 基金会最活跃的明星项目之一,同时也是国内最受欢迎的开源微服务框架,在易用性、高性能通信、服务治理等方面有着非常大的优势,通过 Dubbo3、Dubbo Mesh 等提供了云原生友好的开发与部署模式。与此同时,Dubbo 的多语言体系也得到了快速发展,长期以来提供的有 Java、Golang 两种语言实现,Rust、Node、Python、C++ 等语言实现的支持也已在社区正式启动。&lt;/p>
&lt;ul>
&lt;li>Dubbo 官网 &lt;a href="https://dubbo.apache.org/">https://dubbo.apache.org/&lt;/a>&lt;/li>
&lt;li>Dubbo Java &lt;a href="https://github.com/apache/dubbo/">https://github.com/apache/dubbo/&lt;/a>&lt;/li>
&lt;li>Dubbo Golang &lt;a href="https://github.com/apache/dubbo-go/">https://github.com/apache/dubbo-go/&lt;/a>&lt;/li>
&lt;li>&lt;strong>Dubbo Rust &lt;a href="https://github.com/apache/dubbo-rust/">https://github.com/apache/dubbo-rust/&lt;/a>&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Dubbo Rust 目标是对齐 Dubbo3 的所有核心功能设计,包括基于 HTTP/2 的高性能通信、用户友好的微服务开发编程模式、通过接入DubboMesh提供丰富的服务治理能力等,相比于其他语言实现,Dubbo Rust 将很好的利用 Rust 语言极致性能、安全和指令级掌控能力的特点。
对于微服务框架,主流的编程语言都有对应的实现,而 Dubbo Rust 将很好的填补 Rust 领域的空白:&lt;/p>
&lt;ul>
&lt;li>Golang:在微服务框架领域已经占据着很重要的地位;开源社区出现了dubbo-go、gRPC、go-micro、go-zero等多个微服务框架&lt;/li>
&lt;li>Java:国内用户量最大的编程语言,Spring Cloud、Dubbo等优秀的微服务框架已经非常流行&lt;/li>
&lt;li>C/C++:brpc、grpc 等微服务框架&lt;/li>
&lt;li>Rust:目前没有很完善的微服务框架&lt;/li>
&lt;/ul>
&lt;p>依托 Dubbo 庞大的用户群,以及 Dubbo 体系下的 Mesh 服务治理整体方案规划。Dubbo Rust 可以轻松地融入到现有的云原生研发体系中,不会增加使用者的研发负担。下图是社区推出的 Dubbo Mesh 架构设计。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/rust/dubbo-rust-mesh.png" alt="dubbo-rust">&lt;/p>
&lt;p>在上述架构下,整体分为控制面和数据面两个部分,其中,&lt;/p>
&lt;ul>
&lt;li>控制面负责管理流量治理、地址发现、安全认证、可观测性等服务治理相关的配置信管控工作,包括与K8S等底层技术设施的对接;&lt;/li>
&lt;li>Dubbo Rust 作为数据面组件,负责接收来自控制面的配置;将配置应用到服务中;同时为服务提供基础的RPC通信能力。&lt;/li>
&lt;/ul>
&lt;p>在架构设计方面,Dubbo Rust 将围绕 Dubbo 核心设计以及 Rust 语言的特性进行设计,并将 Dubbo 框架的核心设计输出为文档,从而提升Dubbo框架的易用性。因此,Dubbo Rust 具有如下特点:易用性、高性能以及可扩展,同时面向云原生提供丰富的服务治理能力。&lt;/p>
&lt;h2 id="2-快速体验-dubbo-rust">2 快速体验 Dubbo Rust&lt;/h2>
&lt;h3 id="21-首个版本核心能力">2.1 首个版本核心能力&lt;/h3>
&lt;p>&lt;strong>Dubbo Rust 首个正式版本为 v0.2.0&lt;/strong>,v0.2.0 提供的能力包括&lt;/p>
&lt;ul>
&lt;li>基于 HTTP/2 的 Triple 协议的基础通信能力&lt;/li>
&lt;li>基于 IDL 的 RPC 定义支持,Protobuf 来生成代码,同时支持 Serde 序列化&lt;/li>
&lt;li>request-response、request/response streaming、bi-streaming 通信模型支持&lt;/li>
&lt;li>设计了简洁的、可扩展的架构,支持对 Listener、Connector、Filter、Protocol以及Invoker组件进行扩展&lt;/li>
&lt;/ul>
&lt;p>Dubbo Rust v0.2.0 的核心组件及通信流程如下图所示&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/rust/dubbo-rust-module.png" alt="dubbo-rust">&lt;/p>
&lt;p>核心架构已经基本完成,接下来的版本将重点关注核心组件的扩展以及服务治理相关组件的设计实现。&lt;/p>
&lt;h3 id="22-quick-start">2.2 Quick Start&lt;/h3>
&lt;blockquote>
&lt;p>完整示例可查看 【Dubbo官网】 -&amp;gt; 【Rust SDK 文档】。
&lt;a href="https://dubbo.apache.org/zh-cn/overview/mannual/rust-sdk/quick-start/">https://dubbo.apache.org/zh-cn/overview/mannual/rust-sdk/quick-start/&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>使用 Dubbo Rust 服务开发的基本步骤为&lt;/p>
&lt;ol>
&lt;li>
&lt;p>使用 IDL 定义服务&lt;/p>
&lt;/li>
&lt;li>
&lt;p>添加 Dubbo Rust 依赖到项目&lt;/p>
&lt;/li>
&lt;li>
&lt;p>编译 IDL&lt;/p>
&lt;/li>
&lt;li>
&lt;p>基于 IDL 编译生成的 stub 编写 Server &amp;amp; Client 逻辑&lt;/p>
&lt;/li>
&lt;li>
&lt;p>运行项目&lt;/p>
&lt;/li>
&lt;li>
&lt;p>使用 IDL 定义 Dubb 服务&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-protobuf" data-lang="protobuf">&lt;span style="display:flex;">&lt;span>```protobuf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// ./proto/greeter.proto
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>syntax &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;proto3&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">option&lt;/span> java_multiple_files &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache.dubbo.sample.tri;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The request message containing the user&amp;#39;s name.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">message&lt;/span> &lt;span style="color:#268bd2">GreeterRequest&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">string&lt;/span> name &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// The response message containing the greetings
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">message&lt;/span> &lt;span style="color:#268bd2">GreeterReply&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#dc322f">string&lt;/span> &lt;span style="color:#268bd2">message&lt;/span> &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">service&lt;/span> Greeter{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// unary
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#719e07">rpc&lt;/span> greet(GreeterRequest) &lt;span style="color:#719e07">returns&lt;/span> (GreeterReply);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>�
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2. 增加 Dubbo Rust 依赖
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>```toml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>```toml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># ./Cargo.toml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[package]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>name = &amp;#34;example-greeter&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>version = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>edition = &amp;#34;2021&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[dependencies]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo-config = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[build-dependencies]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo-build = &amp;#34;0.1.0&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>�3. 编译 IDL 并根据生成的 stub 编写逻辑
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>编写 Dubbo Server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>```rust
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>#[tokio::main]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>async fn main() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> register_server(GreeterServerImpl {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &amp;#34;greeter&amp;#34;.to_string(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // Dubbo::new().start().await;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Dubbo::new()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .with_config({
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> let r = RootConfig::new();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> match r.load() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Ok(config) =&amp;gt; config,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Err(_err) =&amp;gt; panic!(&amp;#34;err: {:?}&amp;#34;, _err), // response was droped
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .await;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>struct GreeterServerImpl {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: String,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>impl Greeter for GreeterServerImpl {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> async fn greet(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;amp;self,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> request: Request&amp;lt;GreeterRequest&amp;gt;,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ) -&amp;gt; Result&amp;lt;Response&amp;lt;GreeterReply&amp;gt;, dubbo::status::Status&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println!(&amp;#34;GreeterServer::greet {:?}&amp;#34;, request.metadata);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Ok(Response::new(GreeterReply {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> message: &amp;#34;hello, dubbo-rust&amp;#34;.to_string(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>�
编写 Dubbo Client&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-rust" data-lang="rust">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">#[tokio::main]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">async&lt;/span> &lt;span style="color:#719e07">fn&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> &lt;span style="color:#719e07">mut&lt;/span> cli &lt;span style="color:#719e07">=&lt;/span> GreeterClient::new().with_uri(&lt;span style="color:#2aa198">&amp;#34;http://127.0.0.1:8888&amp;#34;&lt;/span>.to_string());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println!(&lt;span style="color:#2aa198">&amp;#34;# unary call&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> resp &lt;span style="color:#719e07">=&lt;/span> cli
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .greet(Request::new(GreeterRequest {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#2aa198">&amp;#34;message from client&amp;#34;&lt;/span>.to_string(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#719e07">await&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> resp &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">match&lt;/span> resp {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">Ok&lt;/span>(resp) &lt;span style="color:#719e07">=&amp;gt;&lt;/span> resp,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">Err&lt;/span>(err) &lt;span style="color:#719e07">=&amp;gt;&lt;/span> &lt;span style="color:#719e07">return&lt;/span> println!(&lt;span style="color:#2aa198">&amp;#34;&lt;/span>&lt;span style="color:#2aa198">{:?}&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&lt;/span>, err),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">let&lt;/span> (_parts, body) &lt;span style="color:#719e07">=&lt;/span> resp.into_parts();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println!(&lt;span style="color:#2aa198">&amp;#34;Response: &lt;/span>&lt;span style="color:#2aa198">{:?}&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&lt;/span>, body);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样,一个简单的 Dubbo Rust 示例就开发完成了,可以到 Dubbo 官网查看完整文档。&lt;/p>
&lt;h2 id="3-roadmap-与未来规划">3 Roadmap 与未来规划&lt;/h2>
&lt;p>Dubbo Rust Roadmap 规划分为三个阶段:&lt;/p>
&lt;ul>
&lt;li>首先,提供作为 RPC 框架的基础能力,此阶段重点完成的包括基于 HTTP/2 的 RPC 通信、基于 IDL 的 RPC 定义、其他必要的 RPC 内核组件等&lt;/li>
&lt;li>其实,是完善 Dubbo Rust 作为微服务框架的高级功能,此阶段包括微服务定义、配置、功能设计等,如服务超时、异步调用、上下文传递等,具体可参见 Dubbo Java 的高级特性。&lt;/li>
&lt;li>第三阶段重点是引入丰富的服务治理能力支持,如流量治理、限流降级、可观测性等,这一目标将主要通过融入 Dubbo Mesh 体系,即适配 Dubbo Mesh 控制面实现。&lt;/li>
&lt;/ul>
&lt;p>其中,第一阶段的工作已经基本完成,大家可通过上文的 Quick Start 进行深入体验,第二、第三阶段的工作已经在社区全面开展,欢迎感兴趣的社区开发者参与进来,具体联系方式参见下文。&lt;/p>
&lt;p>下图是侧重从第一阶段(RPC框架)、第二阶段(微服务开发框架)的视角对当前 Dubbo Rust 功能完备性的评估和任务拆解。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/rust/dubbo-rust-tasks.png" alt="dubbo-rust">&lt;/p>
&lt;p>上图中都是 Dubbo Rust 核心设计的重要组件,保证 Dubbo Rust 具备微服务框架中完整的 RPC 通信能力以及服务治理能力。&lt;/p>
&lt;ul>
&lt;li>Protocol、Filter、Listener、Connector 等组件都是 RPC 通信核心能力&lt;/li>
&lt;li>服务注册发现、负载均衡、Cluster、Metadata 为后续服务治理能力做铺垫&lt;/li>
&lt;/ul>
&lt;p>除了上图列出的模块以外,还有一些非功能需求也需要支持,例如:&lt;/p>
&lt;ul>
&lt;li>Dubbo 多语言框架之间相互通信测试&lt;/li>
&lt;li>性能验证与持续的 benchmark 机制&lt;/li>
&lt;li>整体架构的持续优化,如核心配置简化以及相应的文档完善&lt;/li>
&lt;/ul>
&lt;h2 id="4-参与-dubbo-rust-社区">4 参与 Dubbo Rust 社区&lt;/h2>
&lt;p>和 Rust 语言一样,Dubbo Rust 是一个非常有活力、非常前沿的社区,另一方面,依赖 Apache Dubbo 社区背后庞大的开发者群体和企业用户,Dubbo Rust 有着非常深厚的用户基础和发展潜力。Dubbo Rust 的快速发展期待社区贡献者的加入
参与 Dubbo Rust 社区可以收获&lt;/p>
&lt;ul>
&lt;li>见证 Dubbo Rust 开源项目的建设以及发展&lt;/li>
&lt;li>在大型项目中通过实际使用学习 Rust 语言,加深对 Rust 语言的理解&lt;/li>
&lt;li>获得提名为 Apache Dubbo CommitterPMC&lt;/li>
&lt;li>借助 Dubbo 社区提高个人曝光度,提高个人技术影响力&lt;/li>
&lt;li>与阿里巴巴等企业专家的面对面交流机会,快速提高技术视野&lt;/li>
&lt;/ul>
&lt;p>参与 Dubbo Rust 社区的方式有如下几种&lt;/p>
&lt;ul>
&lt;li>搜索并加入钉钉群并参与社区双周会,钉钉群号 &lt;strong>44694199&lt;/strong>&lt;/li>
&lt;li>到 GitHub 提 Issue 或贡献代码 &lt;a href="https://github.com/apache/dubbo-rust">https://github.com/apache/dubbo-rust&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Blog: Proxyless Mesh在Dubbo中的实践</title><link>https://dubbo.apache.org/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/09/05/proxyless-mesh%E5%9C%A8dubbo%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>
&lt;h2 id="背景">背景&lt;/h2>
&lt;p>随着 Dubbo 3.1 的 release,Dubbo 在云原生的路上又迈出了重要的一步。在这个版本中添加了 Proxyless Mesh 的新特性,Dubbo Proxyless Mesh 直接实现 xDS 协议解析,
实现 Dubbo 与 Control Plane 的直接通信,进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控,规避 Sidecar 模式带来的性能损耗与部署架构复杂性。&lt;/p>
&lt;h2 id="什么是service-mesh">什么是Service Mesh&lt;/h2>
&lt;p>Service Mesh 又译作 “服务网格”,作为服务间通信的基础设施层。Buoyant 公司的 CEO Willian Morgan 在他的这篇文章 &lt;a href="https://linkerd.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/">WHAT’S A Service Mesh? AND WHY DO I NEED ONE? &lt;/a>
中解释了什么是 Service Mesh,为什么云原生应用需要 Service Mesh。&lt;/p>
&lt;p>&lt;strong>下面是 Willian Morgan 对 Service Mesh 的解释。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>It’s responsible for the reliable delivery of requests through the complex topology of services
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>as an array of lightweight network proxies that are deployed alongside application code, without the
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>application needing to be aware.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>翻译成中文&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>服务网格(Service Mesh)是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>在实践中,Service Mesh 通常以轻量级网络代理阵列的形式实现,这些代理与应用程序代码部署在一起,对应用程序来说无需感知代理的存在。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>说到 Service Mesh 一定离不开 Sidecar 经典架构模式。它通过在业务 Pod 中注入 Sidecar 容器,接管业务容器的通信流量,同时 Sidecar 容器与网格平台的控制平面对接,
基于控制平面下发的策略,对代理流量实施治理和管控,将原有服务框架的治理能力下层到 Sidecar 容器中,从而实现了基础框架能力的下沉,与业务系统解耦。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/1.png" alt="Service Mesh">&lt;/p>
&lt;p>经典的 Sidecar Mesh 部署架构有很多优势,如平滑升级、多语言、业务侵入小等,但也带来了一些额外的问题,比如:&lt;/p>
&lt;ul>
&lt;li>Proxy 带来的性能损耗,在复杂拓扑的网络调用中将变得尤其明显&lt;/li>
&lt;li>流量拦截带来的架构复杂性&lt;/li>
&lt;li>Sidecar 生命周期管理&lt;/li>
&lt;li>部署环境受限,并不是所有环境都满足 Sidecar 流量拦截条件&lt;/li>
&lt;/ul>
&lt;p>针对 Sidecar Mesh 模型的问题,Dubbo 社区自很早之前就做了 Dubbo 直接对接到控制面的设想与思考,并在国内开源社区率先提出了 Proxyless Mesh 的概念,当然就 Proxyless 概念的说法而言,最开始是谷歌提出来的。&lt;/p>
&lt;h2 id="dubbo-proxyless-mesh">Dubbo Proxyless Mesh&lt;/h2>
&lt;p>Dubbo Proxyless 模式是指 Dubbo 直接与 Istiod通信,通过 xDS协议实现服务发现和服务治理等能力。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/2.png" alt="Proxyless">&lt;/p>
&lt;p>Proxyless 模式使得微服务又回到了 2.x 时代的部署架构,同 Dubbo 经典服务治理模式非常相似,所以说这个模式并不新鲜, Dubbo 从最开始就是这样的设计模式。
这样做可以极大的提升应用性能,降低网络延迟。有人说这种做法又回到了原始的基于 SDK 的微服务模式,其实非也,它依然使用了 Envoy 的 xDS API,
但是因为不再需要向应用程序中注入 Sidecar 代理,因此可以减少应用程序性能的损耗。&lt;/p>
&lt;p>但相比于 Mesh 架构,Dubbo 经典服务治理模式并没有强调控制面的统一管控,而这点恰好是 Service Mesh 所强调的,强调对流量、可观测性、证书等的标准化管控与治理,也是 Mesh 理念先进的地方。&lt;/p>
&lt;p>在 Dubbo Proxyless 架构模式下,Dubbo 进程将直接与控制面通信,Dubbo 进程之间也继续保持直连通信模式,我们可以看出 Proxyless 架构的优势:&lt;/p>
&lt;ul>
&lt;li>没有额外的 Proxy 中转损耗,因此更适用于性能敏感应用&lt;/li>
&lt;li>更有利于遗留系统的平滑迁移&lt;/li>
&lt;li>架构简单,容易运维部署&lt;/li>
&lt;li>适用于几乎所有的部署环境&lt;/li>
&lt;/ul>
&lt;h2 id="服务发现">服务发现&lt;/h2>
&lt;p>xDS 接入以注册中心的模式对接,节点发现同其他注册中心的服务自省模型一样,对于 xDS 的负载均衡和路由配置通过 ServiceInstance 的动态运行时配置传出,
在构建 Invoker 的时候将配置参数传入配置地址。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/3.png" alt="服务发现">&lt;/p>
&lt;h2 id="证书管理">证书管理&lt;/h2>
&lt;p>零信任架构下,需要严格区分工作负载的识别和信任,而签发 X.509 证书是推荐的一种认证方式。在 Kubernetes 集群中,服务间是通过 DNS 名称互相访问的,而网络流量可能被 DNS 欺骗、BGP/路由劫持、ARP 欺骗等手段劫持,为了将服务名称(DNS 名称)与服务身份强关联起来,Istio 使用置于 X.509 证书中的安全命名机制。SPIFFE是 Istio 所采用的安全命名的规范,它也是云原生定义的一种标准化的、可移植的工作负载身份规范。&lt;/p>
&lt;p>Secure Production Identity Framework For Everyone (SPIFFE) 是一套服务之间相互进行身份识别的标准,主要包含以下内容:&lt;/p>
&lt;ul>
&lt;li>SPIFFE ID 标准,SPIFFE ID 是服务的唯一标识,具体实现使用 URI 资源标识符&lt;/li>
&lt;li>SPIFFE Verifiable Identity Document (SVID) 标准,将 SPIFFE ID 编码到一个加密的可验证的数据格式中&lt;/li>
&lt;li>颁发与撤销 SVID 的 API 标准(SVID 是 SPIFFE ID 的识别凭证)&lt;/li>
&lt;/ul>
&lt;p>SPIFFE ID 规定了形如 &lt;code>spiffe://&amp;lt;trust domain&amp;gt;/&amp;lt;workload identifier&amp;gt;&lt;/code> 的 URI 格式,作为工作负载(Workload)的唯一标识。
而 Istio 在自身的生态中只使用到了 SPIFFE ID 作为安全命名,其数据格式由自己实现,通信格式采用 CNCF 支持的 xDS 协议规范(证书认证通信更具体来说是 xDS 的 SDS)。&lt;/p>
&lt;p>Istio 使用形如 &lt;code>spiffe://&amp;lt;trust_domain&amp;gt;/ns/&amp;lt;namespace&amp;gt;/sa/&amp;lt;service_account&amp;gt;&lt;/code> 格式的 SPIFFE ID 作为安全命名,注入到 X.509 证书的 subjectAltName 扩展中。
其中&amp;quot;trust_domain&amp;quot;参数通过 Istiod 环境变量TRUST_DOMAIN 注入,用于在多集群环境中交互。&lt;/p>
&lt;p>以下是 Dubbo Proxyless Mesh 证书颁发的过程&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/4.png" alt="证书颁发">&lt;/p>
&lt;ul>
&lt;li>创建 RSA 私钥(Istio 还不支持 ECDSA 私钥)&lt;/li>
&lt;li>构建 CSR(Certificate signing request)模板&lt;/li>
&lt;li>自签名 CSR 生成证书&lt;/li>
&lt;li>创建 Kubernetes Secret 资源储存 CA 证书和私钥(CA Service处理)&lt;/li>
&lt;/ul>
&lt;h2 id="案例实践">案例实践&lt;/h2>
&lt;p>接下来我将带领大家通过一个例子使已有的项目快速跑在 Proxyless Mesh 模式下。&lt;/p>
&lt;h2 id="环境准备">环境准备&lt;/h2>
&lt;h3 id="安装docker">安装docker&lt;/h3>
&lt;p>&lt;a href="https://www.docker.com/">https://www.docker.com/&lt;/a>&lt;/p>
&lt;h3 id="安装minikube">安装minikube&lt;/h3>
&lt;p>墙裂推荐:&lt;a href="https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/">https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/&lt;/a>&lt;/p>
&lt;h3 id="安装istio">安装istio&lt;/h3>
&lt;p>&lt;a href="https://istio.io/latest/docs/setup/getting-started/">https://istio.io/latest/docs/setup/getting-started/&lt;/a>&lt;/p>
&lt;p>❗❗❗ 安装 Istio 的时候需要开启 first-party-jwt 支持(使用 istioctl 工具安装的时候加上 &amp;ndash;set values.global.jwtPolicy=first-party-jwt 参数),否则将导致客户端认证失败的问题。
参考命令如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl -L https://istio.io/downloadIstio | sh -
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">cd&lt;/span> istio-1.xx.x
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>&lt;span style="color:#268bd2">$PWD&lt;/span>/bin:&lt;span style="color:#268bd2">$PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>istioctl install --set &lt;span style="color:#268bd2">profile&lt;/span>&lt;span style="color:#719e07">=&lt;/span>demo --set values.global.jwtPolicy&lt;span style="color:#719e07">=&lt;/span>first-party-jwt -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="代码准备">代码准备&lt;/h2>
&lt;h3 id="xds-provider">xds-provider&lt;/h3>
&lt;h4 id="定义一个接口">定义一个接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">GreetingService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现对应的接口">实现对应的接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@DubboService&lt;/span>&lt;span style="color:#719e07">(&lt;/span>version &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">AnnotatedGreetingService&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> GreetingService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;greeting service received: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;! from host: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> NetUtils&lt;span style="color:#719e07">.&lt;/span>getLocalHost&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写启动类">编写启动类&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderBootstrap&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> Exception &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> AnnotationConfigApplicationContext&lt;span style="color:#719e07">(&lt;/span>ProviderConfiguration&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context&lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo service started&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>1&lt;span style="color:#719e07">).&lt;/span>await&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@EnableDubbo&lt;/span>&lt;span style="color:#719e07">(&lt;/span>scanBasePackages &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.samples.impl&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@PropertySource&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;classpath:/spring/dubbo-provider.properties&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ProviderConfiguration&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写配置信息">编写配置信息&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 由于 Dubbo 3 应用级服务发现的元数据无法从 istio 中获取,需要走服务自省模式。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 这要求了 Dubbo MetadataService 的端口在全集群的是统一的。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.metadataServicePort=20885
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 走xds协议
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=xds://istiod.istio-system.svc:15012
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.name=tri
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.protocol.port=50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 对齐k8s pod生命周期,由于 Kubernetes probe 探活机制的工作原理限制,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 探活请求的发起方不是 localhost,所以需要配置 qosAcceptForeignIp 参数开启允许全局访问
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写deploymentyml和serviceyml">编写Deployment.yml和Service.yml&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas: 3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchLabels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> template:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> containers:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image: apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> livenessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /live
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> readinessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /ready
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startupProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /startup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failureThreshold: 30
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clusterIP: None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ports:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: grpc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetPort: 50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写dockerfile">编写Dockerfile&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>FROM openjdk:8-jdk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ADD ./target/dubbo-samples-xds-provider-1.0-SNAPSHOT.jar dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="xds-consumer">xds-consumer&lt;/h3>
&lt;h4 id="定义一个接口-1">定义一个接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public interface GreetingService {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String sayHello(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="实现对应的接口-1">实现对应的接口&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>@Component(&amp;#34;annotatedConsumer&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>public class GreetingServiceConsumer {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 这里特别注意的是、由于当前 Dubbo 版本受限于 istio 的通信模型无法获取接口所对应的应用名,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // 因此需要配置 providedBy 参数来标记此服务来自哪个应用。
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @DubboReference(version = &amp;#34;1.0.0&amp;#34;, providedBy = &amp;#34;dubbo-samples-xds-provider&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private GreetingService greetingService;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public String doSayHello(String name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return greetingService.sayHello(name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写启动类-1">编写启动类&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public class ConsumerBootstrap {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public static void main(String[] args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> context.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> while (true) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> try {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String hello = greetingServiceConsumer.doSayHello(&amp;#34;xDS Consumer&amp;#34;);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&amp;#34;result: &amp;#34; + hello);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Thread.sleep(100);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } catch (Throwable t) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> t.printStackTrace();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Configuration
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @EnableDubbo(scanBasePackages = &amp;#34;org.apache.dubbo.samples.action&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @PropertySource(&amp;#34;classpath:/spring/dubbo-consumer.properties&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @ComponentScan(value = {&amp;#34;org.apache.dubbo.samples.action&amp;#34;})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> static class ConsumerConfiguration {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写配置信息-1">编写配置信息&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>dubbo.application.name=dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.metadataServicePort=20885
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.registry.address=xds://istiod.istio-system.svc:15012
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.timeout=3000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.consumer.check=false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosEnable=true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dubbo.application.qosAcceptForeignIp=true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写deploymentyml和serviceyml-1">编写Deployment.yml和Service.yml&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas: 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> matchLabels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> template:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> containers:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image: apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> livenessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /live
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> readinessProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /ready
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialDelaySeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> startupProbe:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> httpGet:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /startup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 22222
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> failureThreshold: 30
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> periodSeconds: 10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>apiVersion: v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kind: Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>metadata:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace: dubbo-demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>spec:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clusterIP: None
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> selector:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app: dubbo-samples-xds-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ports:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: grpc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol: TCP
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 50051
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetPort: 50051
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="编写dockerfile-1">编写Dockerfile&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>FROM openjdk:8-jdk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ADD ./target/dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>✅ 到目前为止我们的环境和代码就全都准备完毕了!&lt;/p>
&lt;h2 id="构建镜像">构建镜像&lt;/h2>
&lt;h3 id="1启动docker">1、启动docker&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/5.png" alt="启动docker">&lt;/p>
&lt;h3 id="2启动minikube">2、启动minikube&lt;/h3>
&lt;p>因为minikube是一个本地的k8s,他启动需要一个虚拟引擎,这里我们用docker来管理。我们通过如下命令启动&lt;/p>
&lt;p>&lt;code>minikube start&lt;/code>&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/6.png" alt="启动minikube">&lt;/p>
&lt;p>我们可以在docker里看到minikube&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/7.png" alt="minikube">&lt;/p>
&lt;h3 id="3检查istio的状态">3、检查istio的状态&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/8.png" alt="istio的状态">&lt;/p>
&lt;h3 id="4构建镜像">4、构建镜像&lt;/h3>
&lt;p>在本地找到代码所在位置、依次执行以下命令&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到provider所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ./dubbo-samples-xds-provider/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 构建provider的镜像
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker build -t apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/9.png" alt="构建provider">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到consumer所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../dubbo-samples-xds-consumer/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 构建consumer的镜像
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker build -t apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/10.png" alt="构建consumer">&lt;/p>
&lt;h3 id="5检查本地镜像">5、检查本地镜像&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/11.png" alt="检查本地镜像">&lt;/p>
&lt;h3 id="6创建namespace">6、创建namespace&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 初始化命名空间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/dubbo-samples-xds/deploy/Namespace.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 切换命名空间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubens dubbo-demo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果不创建namespace,那么会看到如下错误&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/12.png" alt="错误">&lt;/p>
&lt;h2 id="部署容器">部署容器&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到provider所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ./dubbo-samples-xds-provider/src/main/resources/k8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Service.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 部署provider的Deployment和Service
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Service.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/13.png" alt="部署provider">&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span># 找到consumer所在路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../../../../../dubbo-samples-xds-consumer/src/main/resources/k8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># dubbo-samples-xds/dubbo-samples-xds-consumer/src/main/resources/k8s/Deployment.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 部署consumer的Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl apply -f Deployment.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/14.png" alt="部署provider">&lt;/p>
&lt;p>在minikube dashboard看到我们已经部署的pod&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/15.png" alt="部署provider">&lt;/p>
&lt;h2 id="观察consumer效果">观察consumer效果&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>kubectl logs xxx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.6
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>result: hello, xDS Consumer! from host: 172.17.0.6
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结展望">总结&amp;amp;展望&lt;/h2>
&lt;p>本文主要剖析了 Dubbo Proxyless Mesh 的架构、服务发现以及证书管理等核心流程,最后通过示例给大家演示了如何使用 Dubbo Proxyless。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/20220905/16.png" alt="部署provider">&lt;/p>
&lt;p>随着 Dubbo 3.1 的 release,Dubbo 在云原生的路上又迈出了重要的一步。在今年年底,Dubbo Mesh 将发布具有服务发现能力的版本,
届时将面向所有 Dubbo 用户提供从低版本平滑迁移到 Mesh 架构的能力;在明年年初春季的时候将发布带有治理能力的版本;在明年年底前发布带热插件更新能力的版本,
希望有兴趣见证 Dubbo 云原生之路的同学可以积极参与社区贡献!&lt;/p>
&lt;p>更多关于 Dubbo Mesh 的动态可以关注 Apache Dubbo 社区官方公众号(ApacheDubbo),及时获取最新消息。&lt;/p></description></item><item><title>Blog: 22-Dubbo3消费者自动感应决策应用级服务发现原理</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/22/22-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E8%87%AA%E5%8A%A8%E6%84%9F%E5%BA%94%E5%86%B3%E7%AD%96%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%8E%9F%E7%90%86/</link><pubDate>Mon, 22 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/22/22-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E8%87%AA%E5%8A%A8%E6%84%9F%E5%BA%94%E5%86%B3%E7%AD%96%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%8E%9F%E7%90%86/</guid><description>
&lt;h1 id="22-dubbo3消费者自动感应决策应用级服务发现原理">22-Dubbo3消费者自动感应决策应用级服务发现原理&lt;/h1>
&lt;h2 id="221-简介">22.1 简介&lt;/h2>
&lt;p>这里要说的内容对Dubbo2迁移到Dubbo3的应用比较有帮助,消费者应用级服务发现做了一些自动决策的逻辑来决定当前消费者是应用级发现还是接口级服务发现,这里与前面说的提供者双注册的原理是对等的,提供者默认同时进行应用级注册和接口级注册,消费者对提供者注册的数据来决定使用应用级发现或者接口级发现。这些都是默认的行为,当然对于消费者来说还可以自定义其他的迁移规则,具体的需要我们详细来看逻辑。&lt;/p>
&lt;p>如果说对于迁移过程比较感兴趣可以直接去官网看文档相对来说还是比较清晰:&lt;a href="https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/">https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/&lt;/a>&lt;/p>
&lt;p>这里再借官网的图来用用,迁移过程主要如下所示:
第一个图是提供者双注册的图:
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>第二个图是消费者订阅决策的图:
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/consumer-subscription.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>第三个图就是精确到消费者订阅的代码层的逻辑了,消费者服务间调用通过一个Invoker类型对象来进行对象,如下图所示消费者代理对象通过创建一个迁移容错的调用器对象来对应用级或者接口级订阅进行适配如下所示
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/migration-cluster-invoker.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>第二个图和第三个图是重点要关注的这一个文章的内容主要就是说这里的逻辑。&lt;/p>
&lt;p>关于代码位置如果不知道是如何调用到这一块逻辑的可以查看博文&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">《21-Dubbo3消费者引用服务入口》&lt;/a>&lt;/p>
&lt;p>这里直接将代码位置定位到:RegistryProtocol类型的interceptInvoker方法中:
如下所示:&lt;/p>
&lt;p>RegistryProtocol类型的interceptInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">interceptInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//目前存在的扩展类型为RegistryProtocolListener监听器的实现类型MigrationRuleListener
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryProtocolListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> findRegistryProtocolListeners&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>listeners&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RegistryProtocolListener listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listener&lt;span style="color:#719e07">.&lt;/span>onRefer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。&lt;/p>
&lt;h2 id="222-migrationrulelistener-类型的onrefer方法">22.2 MigrationRuleListener 类型的onRefer方法&lt;/h2>
&lt;p>直接来看代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">onRefer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryProtocol registryProtocol&lt;span style="color:#719e07">,&lt;/span> ClusterInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">,&lt;/span> URL registryURL&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建一个对应invoker对象的MigrationRuleHandler类型对象 然后将其存放在缓存Map&amp;lt;MigrationInvoker, MigrationRuleHandler&amp;gt;类型对象handles中
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MigrationRuleHandler&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> migrationRuleHandler &lt;span style="color:#719e07">=&lt;/span> handlers&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">((&lt;/span>MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> _key &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">((&lt;/span>MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> invoker&lt;span style="color:#719e07">).&lt;/span>setMigrationRuleListener&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> MigrationRuleHandler&lt;span style="color:#719e07">&amp;lt;&amp;gt;((&lt;/span>MigrationInvoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型,在初始化对象的时候进行了配置初始化
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> migrationRuleHandler&lt;span style="color:#719e07">.&lt;/span>doMigrate&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于这个igrationRule的文可以直接看官方的文档比较详细:&lt;a href="https://dubbo.apache.org/zh-cn/docs/advanced/migration-invoker/#1-%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%8B%E5%8F%91%E6%8E%A8%E8%8D%90">地址迁移规则说明&lt;/a>&lt;/p>
&lt;p>这个迁移规则是为了更细粒度的迁移决策:
相关配置可以参考下面这个样例:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">key&lt;/span>: 消费者应用名(必填)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">step&lt;/span>: 状态名(必填)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值(默认1.0)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例(默认100)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间(默认0)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">force&lt;/span>: 强制切换(默认 false)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">interfaces&lt;/span>: 接口粒度配置(可选)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey: 接口名(接口 + &lt;/span>: + 版本号)(必填)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: 强制切换
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名(必填)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey: 接口名(接口 + &lt;/span>: + 版本号)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">applications&lt;/span>: 应用粒度配置(可选)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">serviceKey&lt;/span>: 应用名(消费的上游应用名)(必填)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">threshold&lt;/span>: 决策阈值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">proportion&lt;/span>: 灰度比例
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">delay&lt;/span>: 延迟决策时间
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">force&lt;/span>: 强制切换
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: 状态名(必填)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不过为了简单起见暂时先不详细说这个配置细节,我们继续往下看&lt;/p>
&lt;h2 id="223-迁移规则处理器执行迁移规则migrationrulehandler类型的domigrate方法">22.3 迁移规则处理器执行迁移规则MigrationRuleHandler类型的doMigrate方法&lt;/h2>
&lt;h3 id="2231-迁移规则的模版方法">22.3.1 迁移规则的模版方法:&lt;/h3>
&lt;p>MigrationRuleHandler类型的doMigrate方法代码如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">doMigrate&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationRule rule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//默认情况下这个类型是MigrationInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>migrationInvoker &lt;span style="color:#719e07">instanceof&lt;/span> ServiceDiscoveryMigrationInvoker&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> refreshInvoker&lt;span style="color:#719e07">(&lt;/span>MigrationStep&lt;span style="color:#719e07">.&lt;/span>FORCE_APPLICATION&lt;span style="color:#719e07">,&lt;/span> 1&lt;span style="color:#719e07">.&lt;/span>0f&lt;span style="color:#719e07">,&lt;/span> rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//迁移步骤,MigrationStep 一共有3种枚举情况:FORCE_INTERFACE, APPLICATION_FIRST, FORCE_APPLICATION
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// initial step : APPLICATION_FIRST
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> MigrationStep step &lt;span style="color:#719e07">=&lt;/span> MigrationStep&lt;span style="color:#719e07">.&lt;/span>APPLICATION_FIRST&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">float&lt;/span> threshold &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">-&lt;/span>1f&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取配置的类型 默认走APPLICATION_FIRST
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> step &lt;span style="color:#719e07">=&lt;/span> rule&lt;span style="color:#719e07">.&lt;/span>getStep&lt;span style="color:#719e07">(&lt;/span>consumerURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//threshold: 决策阈值(默认-1.0)计算与获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> threshold &lt;span style="color:#719e07">=&lt;/span> rule&lt;span style="color:#719e07">.&lt;/span>getThreshold&lt;span style="color:#719e07">(&lt;/span>consumerURL&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Exception e&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Failed to get step and threshold info from rule: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> rule&lt;span style="color:#719e07">,&lt;/span> e&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷洗调用器对象 来进行决策服务发现模式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>refreshInvoker&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> threshold&lt;span style="color:#719e07">,&lt;/span> rule&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refresh success, update rule
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> setMigrationRule&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2232-服务发现调用器对象的选择决策服务发现策略">22.3.2 服务发现调用器对象的选择(决策服务发现策略)&lt;/h3>
&lt;p>这里就是关键代码了:通过迁移配置和当前提供者注册信息来决定创建什么类型的调用器对象(Invoker)来为后续服务调用做准备&lt;/p>
&lt;p>MigrationRuleHandler的refreshInvoker,注意默认情况下这个step参数为APPLICATION_FIRST&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> &lt;span style="color:#268bd2">refreshInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationStep step&lt;span style="color:#719e07">,&lt;/span> Float threshold&lt;span style="color:#719e07">,&lt;/span> MigrationRule newRule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> threshold &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Step or threshold of migration rule cannot be null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MigrationStep originStep &lt;span style="color:#719e07">=&lt;/span> currentStep&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>currentStep &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> currentStep &lt;span style="color:#719e07">!=&lt;/span> step&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#719e07">!&lt;/span>currentThreshold&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>threshold&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">boolean&lt;/span> success &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">switch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> APPLICATION_FIRST&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//默认和配置了应用级优先的服务发现则走这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> migrationInvoker&lt;span style="color:#719e07">.&lt;/span>migrateToApplicationFirstInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> FORCE_APPLICATION&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置了应用级服务发现则走这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> success &lt;span style="color:#719e07">=&lt;/span> migrationInvoker&lt;span style="color:#719e07">.&lt;/span>migrateToForceApplicationInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">case&lt;/span> FORCE_INTERFACE&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置了接口级服务发现则走这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">default&lt;/span>&lt;span style="color:#719e07">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> success &lt;span style="color:#719e07">=&lt;/span> migrationInvoker&lt;span style="color:#719e07">.&lt;/span>migrateToForceInterfaceInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>success&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setCurrentStepAndThreshold&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> threshold&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Succeed Migrated to &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> step &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; mode. Service Name: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> consumerURL&lt;span style="color:#719e07">.&lt;/span>getDisplayServiceKey&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> report&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> originStep&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// migrate failed, do not save new step and rule
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Migrate to &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> step &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; mode failed. Probably not satisfy the threshold you set &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">+&lt;/span> threshold &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;. Please try re-publish configuration if you still after check.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> report&lt;span style="color:#719e07">(&lt;/span>step&lt;span style="color:#719e07">,&lt;/span> originStep&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#2aa198">&amp;#34;false&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> success&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ignore if step is same with previous, will continue override rule for MigrationInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这个代码做了判断的逻辑分别对应了Dubbo3消费者迁移的一个状态逻辑:
三种状态分别如下枚举类型:
当前共存在三种状态,&lt;/p>
&lt;ul>
&lt;li>FORCE_INTERFACE(强制接口级)&lt;/li>
&lt;li>APPLICATION_FIRST(应用级优先)&lt;/li>
&lt;li>FORCE_APPLICATION(强制应用级)&lt;/li>
&lt;/ul>
&lt;p>通过代码我们可以看到默认情况下都会走APPLICATION_FIRST(应用级优先)的策略,这里我们也重点来说 APPLICATION_FIRST(应用级优先)来看下Dubbo3是如何决策使用接口级还是应用级发现模型来兼容迁移的服务的。&lt;/p>
&lt;h3 id="2233-应用级优先的服务发现规则逻辑">22.3.3 应用级优先的服务发现规则逻辑&lt;/h3>
&lt;p>这个规则就是智能选择应用级还是接口级的代码了,对应类型为MigrationInvoker的migrateToApplicationFirstInvoker方法,接下来我们详细看下:&lt;/p>
&lt;p>MigrationInvoker类型的migrateToApplicationFirstInvoker方法:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">migrateToApplicationFirstInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationRule newRule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CountDownLatch latch &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> CountDownLatch&lt;span style="color:#719e07">(&lt;/span>0&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新接口级服务发现Invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> refreshInterfaceInvoker&lt;span style="color:#719e07">(&lt;/span>latch&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新应用级服务发现Invoker类型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> refreshServiceDiscoveryInvoker&lt;span style="color:#719e07">(&lt;/span>latch&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// directly calculate preferred invoker, will not wait until address notify
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// calculation will re-occurred when address notify later
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//计算当前使用应用级还是接口级服务发现的Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> calcPreferredInvoker&lt;span style="color:#719e07">(&lt;/span>newRule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2234-刷新接口级服务发现invoker">22.3.4 刷新接口级服务发现Invoker&lt;/h3>
&lt;p>MigrationInvoker类型的refreshInterfaceInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshInterfaceInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CountDownLatch latch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clearListener&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>needRefresh&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isDebugEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>debug&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Re-subscribing interface addresses for interface &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>invoker &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> registryProtocol&lt;span style="color:#719e07">.&lt;/span>getInvoker&lt;span style="color:#719e07">(&lt;/span>cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setListener&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> latch&lt;span style="color:#719e07">.&lt;/span>countDown&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>reportService&lt;span style="color:#719e07">.&lt;/span>hasReporter&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>reportConsumptionStatus&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>createConsumptionReport&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;interface&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step &lt;span style="color:#719e07">==&lt;/span> APPLICATION_FIRST&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> calcPreferredInvoker&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2235-刷新应用级服务发现invoker类型对象">22.3.5 刷新应用级服务发现Invoker类型对象&lt;/h3>
&lt;p>MigrationInvoker类型的refreshServiceDiscoveryInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">refreshServiceDiscoveryInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>CountDownLatch latch&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clearListener&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>needRefresh&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isDebugEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>debug&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Re-subscribing instance addresses, current interface &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> type&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDiscoveryInvoker&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDiscoveryInvoker &lt;span style="color:#719e07">=&lt;/span> registryProtocol&lt;span style="color:#719e07">.&lt;/span>getServiceDiscoveryInvoker&lt;span style="color:#719e07">(&lt;/span>cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setListener&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> latch&lt;span style="color:#719e07">.&lt;/span>countDown&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>reportService&lt;span style="color:#719e07">.&lt;/span>hasReporter&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>reportConsumptionStatus&lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reportService&lt;span style="color:#719e07">.&lt;/span>createConsumptionReport&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">.&lt;/span>getServiceInterface&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getVersion&lt;span style="color:#719e07">(),&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getGroup&lt;span style="color:#719e07">(),&lt;/span> &lt;span style="color:#2aa198">&amp;#34;app&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>step &lt;span style="color:#719e07">==&lt;/span> APPLICATION_FIRST&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> calcPreferredInvoker&lt;span style="color:#719e07">(&lt;/span>rule&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2236-计算当前使用应用级还是接口级服务发现的invoker对象">22.3.6 计算当前使用应用级还是接口级服务发现的Invoker对象&lt;/h3>
&lt;p>MigrationInvoker类型的的calcPreferredInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">calcPreferredInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>MigrationRule migrationRule&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> invoker &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Set&lt;span style="color:#719e07">&amp;lt;&lt;/span>MigrationAddressComparator&lt;span style="color:#719e07">&amp;gt;&lt;/span> detectors &lt;span style="color:#719e07">=&lt;/span> ScopeModelUtil&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">(&lt;/span>consumerUrl &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">?&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">:&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>getExtensionLoader&lt;span style="color:#719e07">(&lt;/span>MigrationAddressComparator&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">).&lt;/span>getSupportedExtensionInstances&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>detectors&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// pick preferred invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// the real invoker choice in invocation will be affected by promotion
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>detectors&lt;span style="color:#719e07">.&lt;/span>stream&lt;span style="color:#719e07">().&lt;/span>allMatch&lt;span style="color:#719e07">(&lt;/span>comparator &lt;span style="color:#719e07">-&amp;gt;&lt;/span> comparator&lt;span style="color:#719e07">.&lt;/span>shouldMigrate&lt;span style="color:#719e07">(&lt;/span>serviceDiscoveryInvoker&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> migrationRule&lt;span style="color:#719e07">)))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>currentAvailableInvoker &lt;span style="color:#719e07">=&lt;/span> serviceDiscoveryInvoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>currentAvailableInvoker &lt;span style="color:#719e07">=&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>currentAvailableInvoker是后期服务调用使用的Invoker对象&lt;/p>
&lt;p>原文地址:&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/22-dubbo3-xiao-fei-zhe-zi-dong-gan-ying-jue-ce-ying-yong-ji-fu-wu-fa-xian-yuan-li/">22-Dubbo3消费者自动感应决策应用级服务发现原理&lt;/a>&lt;/p></description></item><item><title>Blog: 21-Dubbo3消费者引用服务入口</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/21/21-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E5%BC%95%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%85%A5%E5%8F%A3/</link><pubDate>Sun, 21 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/21/21-dubbo3%E6%B6%88%E8%B4%B9%E8%80%85%E5%BC%95%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%85%A5%E5%8F%A3/</guid><description>
&lt;h1 id="21-dubbo3消费者引用服务入口">21-Dubbo3消费者引用服务入口&lt;/h1>
&lt;h2 id="211-简介">21.1 简介&lt;/h2>
&lt;p>前面我们通过Demo说了一个服务引用配置的创建。另外也在前面的文章说了服务提供者的启动完整过程,不过在说服务提供者启动的过程中并未提到服务消费者是如何发现服务,如果调用服务的,这里先就不再说关于服务消费者启动的一个细节了,直接来看前面未提到的服务消费者是如何引用到服务提供者提供的服务的。
先来回顾下样例代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">runWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setGeneric&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosEnable&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosPort&lt;span style="color:#719e07">(-&lt;/span>1&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://8.131.79.126:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>1&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>reference&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> bootstrap&lt;span style="color:#719e07">.&lt;/span>getCache&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String message &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generic invoke
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GenericService genericService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GenericService&lt;span style="color:#719e07">)&lt;/span> demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object genericInvokeResult &lt;span style="color:#719e07">=&lt;/span> genericService&lt;span style="color:#719e07">.&lt;/span>$invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo generic invoke&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>genericInvokeResult&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这段代码我们前面详细说了服务引用的配置ReferenceConfig和Dubbo启动器启动应用的过程DubboBootstrap,后面我们直接定位到消费者引用服务的代码位置来看。&lt;/p>
&lt;h2 id="212-入口代码">21.2 入口代码&lt;/h2>
&lt;h3 id="2121-defaultmoduledeployer的start方法">21.2.1 DefaultModuleDeployer的start方法&lt;/h3>
&lt;p>第一个要关注的就是模块发布器DefaultModuleDeployer的start方法,这个start方法包含了Dubbo应用启动的过程&lt;/p>
&lt;p>DefaultModuleDeployer的start方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> Future &lt;span style="color:#268bd2">start&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> IllegalStateException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>省略掉若干代码
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> onModuleStarting&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// initialize
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialize&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// export services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> exportServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// prepare application instance
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// exclude internal module to avoid wait itself
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>moduleModel &lt;span style="color:#719e07">!=&lt;/span> moduleModel&lt;span style="color:#719e07">.&lt;/span>getApplicationModel&lt;span style="color:#719e07">().&lt;/span>getInternalModule&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationDeployer&lt;span style="color:#719e07">.&lt;/span>prepareInternalModule&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// refer services
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referServices&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">...&lt;/span>省略掉若干代码
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> startFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个方法大部分代码已经省略,也不会详细去说了,感兴趣的可以看之前讲到的博客,这里主要来看引用服务方法referServices&lt;/p>
&lt;h3 id="2122-defaultmoduledeployer的referservices方法">21.2.2 DefaultModuleDeployer的referServices方法&lt;/h3>
&lt;p>下面就要来看消费者应用如何引用的服务的入口了,这个方法主要从大的方面做了一些服务引用生命周期的代码,看懂了这个方法我们就可以不依赖Dubbo负载的启动逻辑可以单独调用ReferenceConfigBase类型的对应方法来刷新,启动,销毁引用的服务了这里先来看下代码再详细介绍内容:&lt;/p>
&lt;p>DefaultModuleDeployer的referServices方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">referServices&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个是获取配置的所有的ReferenceConfigBase类型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> configManager&lt;span style="color:#719e07">.&lt;/span>getReferences&lt;span style="color:#719e07">().&lt;/span>forEach&lt;span style="color:#719e07">(&lt;/span>rc &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> referenceConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;?&amp;gt;)&lt;/span> rc&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>referenceConfig&lt;span style="color:#719e07">.&lt;/span>isRefreshed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新引用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceConfig&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">.&lt;/span>shouldInit&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>referAsync &lt;span style="color:#719e07">||&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>shouldReferAsync&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ExecutorService executor &lt;span style="color:#719e07">=&lt;/span> executorRepository&lt;span style="color:#719e07">.&lt;/span>getServiceReferExecutor&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>Void&lt;span style="color:#719e07">&amp;gt;&lt;/span> future &lt;span style="color:#719e07">=&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>runAsync&lt;span style="color:#719e07">(()&lt;/span> &lt;span style="color:#719e07">-&amp;gt;&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">try&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//间接的通过缓存对象来引用服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceCache&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; refer async catch error : &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> t&lt;span style="color:#719e07">.&lt;/span>getMessage&lt;span style="color:#719e07">(),&lt;/span> t&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">},&lt;/span> executor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> asyncReferringFutures&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>future&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//间接的通过缓存对象来引用服务配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceCache&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">catch&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Throwable t&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>error&lt;span style="color:#719e07">(&lt;/span>getIdentifier&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34; refer catch error.&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//出现异常销毁引用配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referenceCache&lt;span style="color:#719e07">.&lt;/span>destroy&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> t&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个代码中我们核心需要关心的就是SimpleReferenceCache类型的get方法了,在获取服务对象之外包装了一层缓存。&lt;/p>
&lt;p>如果出现了异常则执行referenceCache的destroy方法进行销毁引用配置。&lt;/p>
&lt;h2 id="213-开始引用服务">21.3 开始引用服务&lt;/h2>
&lt;h3 id="2131-simplereferencecache是什么">21.3.1 SimpleReferenceCache是什么?&lt;/h3>
&lt;p>一个用于缓存引用ReferenceConfigBase的util工具类。
ReferenceConfigBase是一个重对象,对于频繁创建ReferenceConfigBase的框架来说,有必要缓存这些对象。
如果需要使用复杂的策略,可以实现并使用自己的ReferenceConfigBase缓存
这个Cache是引用服务的开始如果我们想在代码中自定义一些服务引用的逻辑,可以直接创建SimpleReferenceCache类型对象然后调用其get方法进行引用服务。那这个缓存对象是和缓存与引用服务的可以继续往下看。&lt;/p>
&lt;h3 id="2132-引用服务之前的缓存处理逻辑">21.3.2 引用服务之前的缓存处理逻辑?&lt;/h3>
&lt;p>关于逻辑的处理,看代码有时候比文字更清晰明了,这里可以直接来看 SimpleReferenceCache类型的get方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@SuppressWarnings&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;unchecked&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> T &lt;span style="color:#268bd2">get&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ReferenceConfigBase&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> rc&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个生成的key规则是这样的 服务分组/服务接口:版本号 详细的代码就不看了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//例如: group/link.elastic.dubbo.entity.DemoService:1.0
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String key &lt;span style="color:#719e07">=&lt;/span> generator&lt;span style="color:#719e07">.&lt;/span>generateKey&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务类型 如果是泛化调用则这个类型为GenericService
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&lt;/span> type &lt;span style="color:#719e07">=&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>getInterfaceClass&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//服务是否为单例的这里默认值都为空,为单例模式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> singleton &lt;span style="color:#719e07">=&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>getSingleton&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span> &lt;span style="color:#719e07">||&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>getSingleton&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> T proxy &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Check existing proxy of the same &amp;#39;key&amp;#39; and &amp;#39;type&amp;#39; first.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>singleton&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//一般为单例的 这个方法是从缓存中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proxy &lt;span style="color:#719e07">=&lt;/span> get&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;)&lt;/span> type&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//非单例容易造成内存泄露,无法从缓存中获取
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> logger&lt;span style="color:#719e07">.&lt;/span>warn&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//前面是从缓存中拿,如果缓存中获取不到则开始引用服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>proxy &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取或者创建值,为引用类型referencesOfType对象(类型为Map&amp;lt;Class&amp;lt;?&amp;gt;, List&amp;lt;ReferenceConfigBase&amp;lt;?&amp;gt;&amp;gt;&amp;gt;)缓存对象生成值(值不存咋时候会生成一个)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ReferenceConfigBase&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> referencesOfType &lt;span style="color:#719e07">=&lt;/span> referenceTypeMap&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">,&lt;/span> _t &lt;span style="color:#719e07">-&amp;gt;&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>synchronizedList&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//每次走到这里都会添加一个ReferenceConfigBase 引用配置对象(单例的从缓存中拿到就可以直接返回了)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> referencesOfType&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//与前面一样 前面是类型映射,这里是key映射
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>ReferenceConfigBase&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> referenceConfigList &lt;span style="color:#719e07">=&lt;/span> referenceKeyMap&lt;span style="color:#719e07">.&lt;/span>computeIfAbsent&lt;span style="color:#719e07">(&lt;/span>key&lt;span style="color:#719e07">,&lt;/span> _k &lt;span style="color:#719e07">-&amp;gt;&lt;/span> Collections&lt;span style="color:#719e07">.&lt;/span>synchronizedList&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;()));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfigList&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>rc&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//开始引用服务
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> proxy &lt;span style="color:#719e07">=&lt;/span> rc&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> proxy&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到这个逻辑使用了享元模式(其实就是先查缓存,缓存不存在则创建对象存入缓存)来进行引用对象的管理这样一个过程,这里一共有两个缓存对象referencesOfType和referenceConfigList
key分别为引用类型和引用的服务的key,值是引用服务的基础配置对象列表List&amp;lt;ReferenceConfigBase&lt;?>&amp;gt;&lt;/p>
&lt;p>后面可以详细看下如果借助ReferenceConfigBase类型对象来进行具体类型的引用。&lt;/p>
&lt;h2 id="214-初始化引用服务的过程">21.4 初始化引用服务的过程&lt;/h2>
&lt;h3 id="2141-初始化引用服务的调用入口">21.4.1 初始化引用服务的调用入口&lt;/h3>
&lt;p>引用服务的逻辑其实是相对复杂一点的,包含了服务发现,引用对象的创建等等,接下来就让我们详细看下:&lt;/p>
&lt;p>ReferenceConfig类型的get方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> T &lt;span style="color:#268bd2">get&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>destroyed&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalStateException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;The invoker of ReferenceConfig(&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> url &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;) has already destroyed!&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ref类型为 transient volatile T ref;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ref &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// ensure start module, compatible with old api usage
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getDeployer&lt;span style="color:#719e07">().&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>ref &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> init&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> ref&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里有一段代码是:getScopeModel().getDeployer().start();
这个前面已经调用了模块发布器启动过了,这里有这么一行代码是有一定作用的,如果使用方直接调用了ReferenceConfigBase的get方法或者缓存对象SimpleReferenceCache类型的对象的get方法来引用服务端的时候就会造成很多配置没有初始化下面执行逻辑的时候出现问题,这个代码其实就是启动模块进行一些基础配置的初始化操作 比如元数据中心默认配置选择,注册中心默认配置选择这些都是比较重要的。&lt;/p>
&lt;p>另外可以看到的是这里使用了双重校验锁来保证单例对象的创建,发现Dubbo种大量的使用了双重校验锁的逻辑。&lt;/p>
&lt;h3 id="2142-初始化引用服务">21.4.2 初始化引用服务&lt;/h3>
&lt;p>这个就直接看代码了这,初始化过程相对复杂一点,我们一点点来看
ReferenceConfig类型init()方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#268bd2">synchronized&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//初始化标记变量保证只初始化一次,这里又是加锁🔐又是加标记变量的
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>initialized&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initialized &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//刷新配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>isRefreshed&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">.&lt;/span>refresh&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init serviceMetadata
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//初始化ServiceMetadata类型对象serviceMetadata 为其设置服务基本属性比如版本号,分组,服务接口名
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initServiceMetadata&lt;span style="color:#719e07">(&lt;/span>consumer&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//继续初始化元数据信息 服务接口类型和key
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setServiceType&lt;span style="color:#719e07">(&lt;/span>getServiceInterfaceClass&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// TODO, uncomment this line once service key is unified
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setServiceKey&lt;span style="color:#719e07">(&lt;/span>URL&lt;span style="color:#719e07">.&lt;/span>buildKey&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">,&lt;/span> group&lt;span style="color:#719e07">,&lt;/span> version&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//配置转Map类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> referenceParameters &lt;span style="color:#719e07">=&lt;/span> appendConfig&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// init service-application mapping
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//来自本地存储和url参数的初始化映射。 参数转URL配置初始化 Dubbo中喜欢用url作为配置的一种处理方式
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> initServiceAppsMapping&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地内存模块服务存储库
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ModuleServiceRepository repository &lt;span style="color:#719e07">=&lt;/span> getScopeModel&lt;span style="color:#719e07">().&lt;/span>getServiceRepository&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//ServiceModel和ServiceMetadata在某种程度上是相互重复的。我们将来应该合并它们。
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ServiceDescriptor serviceDescriptor&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>NATIVE_STUB&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>getProxy&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceDescriptor &lt;span style="color:#719e07">=&lt;/span> StubSuppliers&lt;span style="color:#719e07">.&lt;/span>getServiceDescriptor&lt;span style="color:#719e07">(&lt;/span>interfaceName&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>serviceDescriptor&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地存储库注册服务接口类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceDescriptor &lt;span style="color:#719e07">=&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerService&lt;span style="color:#719e07">(&lt;/span>interfaceClass&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//消费者模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> consumerModel &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ConsumerModel&lt;span style="color:#719e07">(&lt;/span>serviceMetadata&lt;span style="color:#719e07">.&lt;/span>getServiceKey&lt;span style="color:#719e07">(),&lt;/span> proxy&lt;span style="color:#719e07">,&lt;/span> serviceDescriptor&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> getScopeModel&lt;span style="color:#719e07">(),&lt;/span> serviceMetadata&lt;span style="color:#719e07">,&lt;/span> createAsyncMethodInfo&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地存储库注册消费者模型对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> repository&lt;span style="color:#719e07">.&lt;/span>registerConsumer&lt;span style="color:#719e07">(&lt;/span>consumerModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//与前面代码一样基础初始化服务元数据对象为其设置附加参数
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>getAttachments&lt;span style="color:#719e07">().&lt;/span>putAll&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//创建服务的代理对象 !!!核心代码在这里
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ref &lt;span style="color:#719e07">=&lt;/span> createProxy&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//为服务元数据对象设置代理对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>setTarget&lt;span style="color:#719e07">(&lt;/span>ref&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> serviceMetadata&lt;span style="color:#719e07">.&lt;/span>addAttribute&lt;span style="color:#719e07">(&lt;/span>PROXY_CLASS_REF&lt;span style="color:#719e07">,&lt;/span> ref&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerModel&lt;span style="color:#719e07">.&lt;/span>setProxyObject&lt;span style="color:#719e07">(&lt;/span>ref&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerModel&lt;span style="color:#719e07">.&lt;/span>initMethodModels&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//检查invoker对象初始结果
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> checkInvokerAvailable&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="215-referenceconfig创建服务引用代理对象的原理">21.5 ReferenceConfig创建服务引用代理对象的原理&lt;/h2>
&lt;h3 id="2151-代理对象的创建过程">21.5.1 代理对象的创建过程&lt;/h3>
&lt;p>这里就要继续看 ReferenceConfig类型的创建代理方法createProxy了
直接贴一下源码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> T &lt;span style="color:#268bd2">createProxy&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> referenceParameters&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//本地引用 这里为false
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>shouldJvmRefer&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> createInvokerForLocal&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> urls&lt;span style="color:#719e07">.&lt;/span>clear&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//url存在则为点对点引用
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// user specified URL, could be peer-to-peer address, or register center&amp;#39;s address.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> parseUrl&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// if protocols not in jvm checkRegistry
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//这里不是local协议默认这里为空
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>LOCAL_PROTOCOL&lt;span style="color:#719e07">.&lt;/span>equalsIgnoreCase&lt;span style="color:#719e07">(&lt;/span>getProtocol&lt;span style="color:#719e07">()))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> aggregateUrlFromRegistry&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个代码非常重要 创建远程引用,创建远程引用调用器
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> createInvokerForRemote&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>logger&lt;span style="color:#719e07">.&lt;/span>isInfoEnabled&lt;span style="color:#719e07">())&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Referred dubbo service: [&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;].&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">(&lt;/span>Boolean&lt;span style="color:#719e07">.&lt;/span>parseBoolean&lt;span style="color:#719e07">(&lt;/span>referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>GENERIC_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">?&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34; it&amp;#39;s GenericService reference&amp;#34;&lt;/span> &lt;span style="color:#719e07">:&lt;/span> &lt;span style="color:#2aa198">&amp;#34; it&amp;#39;s not GenericService reference&amp;#34;&lt;/span>&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL consumerUrl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfigURL&lt;span style="color:#719e07">(&lt;/span>CONSUMER_PROTOCOL&lt;span style="color:#719e07">,&lt;/span> referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>REGISTER_IP_KEY&lt;span style="color:#719e07">),&lt;/span> 0&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceParameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>INTERFACE_KEY&lt;span style="color:#719e07">),&lt;/span> referenceParameters&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>setScopeModel&lt;span style="color:#719e07">(&lt;/span>getScopeModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerUrl &lt;span style="color:#719e07">=&lt;/span> consumerUrl&lt;span style="color:#719e07">.&lt;/span>setServiceModel&lt;span style="color:#719e07">(&lt;/span>consumerModel&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MetadataUtils&lt;span style="color:#719e07">.&lt;/span>publishServiceDefinition&lt;span style="color:#719e07">(&lt;/span>consumerUrl&lt;span style="color:#719e07">,&lt;/span> consumerModel&lt;span style="color:#719e07">.&lt;/span>getServiceModel&lt;span style="color:#719e07">(),&lt;/span> getApplicationModel&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// create service proxy
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">(&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> proxyFactory&lt;span style="color:#719e07">.&lt;/span>getProxy&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">,&lt;/span> ProtocolUtils&lt;span style="color:#719e07">.&lt;/span>isGeneric&lt;span style="color:#719e07">(&lt;/span>generic&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2152-创建远程引用创建远程引用调用器">21.5.2 创建远程引用,创建远程引用调用器&lt;/h3>
&lt;p>ReferenceConfig类型的createInvokerForRemote方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">createInvokerForRemote&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个url 为注册协议如registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;amp;dubbo=2.0.2&amp;amp;pid=6204&amp;amp;qos.enable=false&amp;amp;qos.port=-1&amp;amp;registry=zookeeper&amp;amp;release=3.0.9&amp;amp;timestamp=1657439419495
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>urls&lt;span style="color:#719e07">.&lt;/span>size&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">==&lt;/span> 1&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL curUrl &lt;span style="color:#719e07">=&lt;/span> urls&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>0&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个SPI对象是由字节码动态生成的自适应对象Protocol$Adaptie直接看看不到源码,后续可以解析一个字节码生成的类型,这里后续来调用链路即可
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> protocolSPI&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>interfaceClass&lt;span style="color:#719e07">,&lt;/span> curUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(!&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>curUrl&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> invokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invokers&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>invoker&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>DEFAULT&lt;span style="color:#719e07">).&lt;/span>join&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StaticDirectory&lt;span style="color:#719e07">(&lt;/span>curUrl&lt;span style="color:#719e07">,&lt;/span> invokers&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>Invoker&lt;span style="color:#719e07">&amp;lt;?&amp;gt;&amp;gt;&lt;/span> invokers &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ArrayList&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL registryUrl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>URL url &lt;span style="color:#719e07">:&lt;/span> urls&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// For multi-registry scenarios, it is not checked whether each referInvoker is available.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Because this invoker may become available later.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>add&lt;span style="color:#719e07">(&lt;/span>protocolSPI&lt;span style="color:#719e07">.&lt;/span>refer&lt;span style="color:#719e07">(&lt;/span>interfaceClass&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>UrlUtils&lt;span style="color:#719e07">.&lt;/span>isRegistry&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// use last registry url
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> registryUrl &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>registryUrl &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// registry url is available
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// for multi-subscription scenario, use &amp;#39;zone-aware&amp;#39; policy by default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> String cluster &lt;span style="color:#719e07">=&lt;/span> registryUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CLUSTER_KEY&lt;span style="color:#719e07">,&lt;/span> ZoneAwareCluster&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -&amp;gt; FailoverClusterInvoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// (RegistryDirectory, routing happens here) -&amp;gt; Invoker
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> invoker &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> cluster&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">).&lt;/span>join&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StaticDirectory&lt;span style="color:#719e07">(&lt;/span>registryUrl&lt;span style="color:#719e07">,&lt;/span> invokers&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span> &lt;span style="color:#719e07">else&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// not a registry url, must be direct invoke.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>invokers&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">throw&lt;/span> &lt;span style="color:#719e07">new&lt;/span> IllegalArgumentException&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;invokers == null&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL curUrl &lt;span style="color:#719e07">=&lt;/span> invokers&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>0&lt;span style="color:#719e07">).&lt;/span>getUrl&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String cluster &lt;span style="color:#719e07">=&lt;/span> curUrl&lt;span style="color:#719e07">.&lt;/span>getParameter&lt;span style="color:#719e07">(&lt;/span>CLUSTER_KEY&lt;span style="color:#719e07">,&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>DEFAULT&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> invoker &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>scopeModel&lt;span style="color:#719e07">,&lt;/span> cluster&lt;span style="color:#719e07">).&lt;/span>join&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> StaticDirectory&lt;span style="color:#719e07">(&lt;/span>curUrl&lt;span style="color:#719e07">,&lt;/span> invokers&lt;span style="color:#719e07">),&lt;/span> &lt;span style="color:#cb4b16">true&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2153-invoker对象创建的全过程">21.5.3 Invoker对象创建的全过程&lt;/h3>
&lt;p>为了更好理解Protocol$Adaptie内部的引用执行过程这里我把Debug的链路截图了过来
按照固定的顺序先执行AOP的逻辑再执行具体的逻辑:&lt;/p>
&lt;ul>
&lt;li>Protocol$Adaptie的refer方法&lt;/li>
&lt;li>ProtocolSerializationWrapper AOP类型的协议序列化器refer方法&lt;/li>
&lt;li>ProtocolFilterWrapper AOP类型的协议过滤器的refer方法&lt;/li>
&lt;li>QosProtocolWrapper AOP类型的QOS协议包装器的refer方法&lt;/li>
&lt;li>ProtocolListenerWrapper APO类型监听器包装器的refer方法&lt;/li>
&lt;li>RegistryProtocol 注册协议的refer方法 (会添加容错逻辑)&lt;/li>
&lt;li>RegistryProtocol 注册协议的doRefer方法(调用方法创建Invoker对象)&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://dubbo.apache.org/imgs/blog/source-blog/21-createInvokerRemote.png">&lt;/a>&lt;/p>
&lt;p>这里我们不再详细说这个引用链的具体过程直接定位到RegistryProtocol中创建Invoker类型的地方。
先来看RegistryProtocol类型的refer方法,如下代码所示:&lt;/p>
&lt;p>RegistryProtocol类型的refer方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@SuppressWarnings&lt;/span>&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;unchecked&amp;#34;&lt;/span>&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">refer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#268bd2">throws&lt;/span> RpcException &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这个url已经被转换为具体的注册中心协议类型了
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&amp;amp;dubbo=2.0.2&amp;amp;pid=7944&amp;amp;qos.enable=false&amp;amp;qos.port=-1&amp;amp;release=3.0.9&amp;amp;timestamp=1657440673100
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> url &lt;span style="color:#719e07">=&lt;/span> getRegistryUrl&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取用于操作Zookeeper的Registry类型
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Registry registry &lt;span style="color:#719e07">=&lt;/span> getRegistry&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RegistryService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>type&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> proxyFactory&lt;span style="color:#719e07">.&lt;/span>getInvoker&lt;span style="color:#719e07">((&lt;/span>T&lt;span style="color:#719e07">)&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// group=&amp;#34;a,b&amp;#34; or group=&amp;#34;*&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> qs &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;)&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>getAttribute&lt;span style="color:#719e07">(&lt;/span>REFER_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String group &lt;span style="color:#719e07">=&lt;/span> qs&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>GROUP_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>StringUtils&lt;span style="color:#719e07">.&lt;/span>isNotEmpty&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">((&lt;/span>COMMA_SPLIT_PATTERN&lt;span style="color:#719e07">.&lt;/span>split&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">)).&lt;/span>length &lt;span style="color:#719e07">&amp;gt;&lt;/span> 1 &lt;span style="color:#719e07">||&lt;/span> &lt;span style="color:#2aa198">&amp;#34;*&amp;#34;&lt;/span>&lt;span style="color:#719e07">.&lt;/span>equals&lt;span style="color:#719e07">(&lt;/span>group&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> doRefer&lt;span style="color:#719e07">(&lt;/span>Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> MergeableCluster&lt;span style="color:#719e07">.&lt;/span>NAME&lt;span style="color:#719e07">),&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> qs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//降级容错的逻辑处理对象 类型为Cluster 实际类型为MockClusterWrapper 内部包装的是FailoverCluster
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//后续调用服务失败时候会先失效转移再降级
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Cluster cluster &lt;span style="color:#719e07">=&lt;/span> Cluster&lt;span style="color:#719e07">.&lt;/span>getCluster&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getScopeModel&lt;span style="color:#719e07">(),&lt;/span> qs&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>CLUSTER_KEY&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里才是具体的Invoker对象的创建
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> doRefer&lt;span style="color:#719e07">(&lt;/span>cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> qs&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RegistryProtocol类型的doRefer方法创建Invoker对象
直接来看代码了&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">doRefer&lt;/span>&lt;span style="color:#719e07">(&lt;/span>Cluster cluster&lt;span style="color:#719e07">,&lt;/span> Registry registry&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> String&lt;span style="color:#719e07">&amp;gt;&lt;/span> parameters&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Map&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">,&lt;/span> Object&lt;span style="color:#719e07">&amp;gt;&lt;/span> consumerAttribute &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> HashMap&lt;span style="color:#719e07">&amp;lt;&amp;gt;(&lt;/span>url&lt;span style="color:#719e07">.&lt;/span>getAttributes&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerAttribute&lt;span style="color:#719e07">.&lt;/span>remove&lt;span style="color:#719e07">(&lt;/span>REFER_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String p &lt;span style="color:#719e07">=&lt;/span> isEmpty&lt;span style="color:#719e07">(&lt;/span>parameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>PROTOCOL_KEY&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">?&lt;/span> CONSUMER &lt;span style="color:#719e07">:&lt;/span> parameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>PROTOCOL_KEY&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL consumerUrl &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceConfigURL &lt;span style="color:#719e07">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> p&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> parameters&lt;span style="color:#719e07">.&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>REGISTER_IP_KEY&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 0&lt;span style="color:#719e07">,&lt;/span> getPath&lt;span style="color:#719e07">(&lt;/span>parameters&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> parameters&lt;span style="color:#719e07">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> consumerAttribute
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#719e07">=&lt;/span> url&lt;span style="color:#719e07">.&lt;/span>putAttribute&lt;span style="color:#719e07">(&lt;/span>CONSUMER_URL_KEY&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//重点看这一行 带迁移性质的Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> migrationInvoker &lt;span style="color:#719e07">=&lt;/span> getMigrationInvoker&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这一行回来执行迁移规则创建应用级优先的服务发现Invoker对象
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">return&lt;/span> interceptInvoker&lt;span style="color:#719e07">(&lt;/span>migrationInvoker&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里代码比较重要的其实只有两行getMigrationInvoker和interceptInvoker方法
比较核心也是Dubbo3比较重要的消费者启动逻辑基本都在这个方法里面interceptInvoker,这个方法执行了消费者应用级发现和接口级发现迁移的逻辑,会自动帮忙决策一个Invoker类型对象,不过这个逻辑这里先简单看下,后续单独整个文章来说。&lt;/p>
&lt;p>这里我们先来看 ClusterInvoker对象的创建,下面先看代码:&lt;/p>
&lt;p>RegistryProtocol类型的getMigrationInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">getMigrationInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>RegistryProtocol registryProtocol&lt;span style="color:#719e07">,&lt;/span> Cluster cluster&lt;span style="color:#719e07">,&lt;/span> Registry registry&lt;span style="color:#719e07">,&lt;/span> Class&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ServiceDiscoveryMigrationInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;(&lt;/span>registryProtocol&lt;span style="color:#719e07">,&lt;/span> cluster&lt;span style="color:#719e07">,&lt;/span> registry&lt;span style="color:#719e07">,&lt;/span> type&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>详细的逻辑这里就不再看了,我们继续看RegistryProtocol类型的interceptInvoker方法:&lt;/p>
&lt;p>具体代码如下:
RegistryProtocol类型的interceptInvoker方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protected&lt;/span> &lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> Invoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">interceptInvoker&lt;/span>&lt;span style="color:#719e07">(&lt;/span>ClusterInvoker&lt;span style="color:#719e07">&amp;lt;&lt;/span>T&lt;span style="color:#719e07">&amp;gt;&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> URL url&lt;span style="color:#719e07">,&lt;/span> URL consumerUrl&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//获取激活的注册协议监听器扩展里面registry.protocol.listener,这里激活的类型为MigrationRuleListener
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> List&lt;span style="color:#719e07">&amp;lt;&lt;/span>RegistryProtocolListener&lt;span style="color:#719e07">&amp;gt;&lt;/span> listeners &lt;span style="color:#719e07">=&lt;/span> findRegistryProtocolListeners&lt;span style="color:#719e07">(&lt;/span>url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> &lt;span style="color:#719e07">(&lt;/span>CollectionUtils&lt;span style="color:#719e07">.&lt;/span>isEmpty&lt;span style="color:#719e07">(&lt;/span>listeners&lt;span style="color:#719e07">))&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> &lt;span style="color:#719e07">(&lt;/span>RegistryProtocolListener listener &lt;span style="color:#719e07">:&lt;/span> listeners&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">//这里执行MigrationRuleListener类型的onRefer方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> listener&lt;span style="color:#719e07">.&lt;/span>onRefer&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">this&lt;/span>&lt;span style="color:#719e07">,&lt;/span> invoker&lt;span style="color:#719e07">,&lt;/span> consumerUrl&lt;span style="color:#719e07">,&lt;/span> url&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> invoker&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。
当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。&lt;/p>
&lt;p>可以看到核心的逻辑集中在了这个位置MigrationRuleListener类型的onRefer方法,这个这里就不深入往下说了,后续会有个文章专门来看Dubbo2迁移Dubbo3时候处理的逻辑。&lt;/p>
&lt;p>Invoker对象的创建完成其实就代表了服务引用执行完成,不过这里核心的协议并没有来说&lt;/p>
&lt;p>原文地址:&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/21-dubbo-xiao-fei-zhe-yin-yong-fu-wu-de-ru-kou/">21-Dubbo3消费者引用服务入口&lt;/a>&lt;/p></description></item><item><title>Blog: 20-Dubbo3服务引用配置ReferenceConfig</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/20/20-dubbo3%E6%9C%8D%E5%8A%A1%E5%BC%95%E7%94%A8%E9%85%8D%E7%BD%AEreferenceconfig/</link><pubDate>Sat, 20 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/20/20-dubbo3%E6%9C%8D%E5%8A%A1%E5%BC%95%E7%94%A8%E9%85%8D%E7%BD%AEreferenceconfig/</guid><description>
&lt;h1 id="20-dubbo3服务引用配置referenceconfig">20-Dubbo3服务引用配置ReferenceConfig&lt;/h1>
&lt;h2 id="201-简介">20.1 简介&lt;/h2>
&lt;p>前面简单介绍了一下消费者的例子,消费者创建的第一步就是先进行消费者信息的配置对应类型为ReferenceConfig,这里详细来看ReferenceConfig包含哪些信息?先简单了解下消费者配置的类型关系如下图所示:引用配置与服务配置类型都是通过继承接口配置来扩展的,在分析生产者的时候详细介绍过服务相关的配置,这里来详细看消费者引用者的相关配置信息.
&lt;img src="https://dubbo.apache.org/imgs/blog/source-blog/20-refe.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>前面例子说了消费者配置对象的创建主要是通过如下代码:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个配置类型的对象创建过程并没有太多的逻辑这里主要来说下各种配置信息:
服务消费者引用服务配置。对应的配置类: &lt;code>org.apache.dubbo.config.ReferenceConfig&lt;/code>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>属性&lt;/th>
&lt;th>对应URL参数&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>是否必填&lt;/th>
&lt;th>缺省值&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>描述&lt;/th>
&lt;th>兼容性&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>id&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>服务引用BeanId&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>interface&lt;/td>
&lt;td>&lt;/td>
&lt;td>class&lt;/td>
&lt;td>&lt;strong>必填&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务接口名&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>version&lt;/td>
&lt;td>version&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务版本,与服务提供者的版本一致&lt;/td>
&lt;td>1.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>group&lt;/td>
&lt;td>group&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务发现&lt;/td>
&lt;td>服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致&lt;/td>
&lt;td>1.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>timeout&lt;/td>
&lt;td>timeout&lt;/td>
&lt;td>long&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的timeout&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务方法调用超时时间(毫秒)&lt;/td>
&lt;td>1.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>retries&lt;/td>
&lt;td>retries&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的retries&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>connections&lt;/td>
&lt;td>connections&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的connections&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>loadbalance&lt;/td>
&lt;td>loadbalance&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的loadbalance&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>async&lt;/td>
&lt;td>async&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的async&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>generic&lt;/td>
&lt;td>generic&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的generic&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否缺省泛化接口,如果为泛化接口,将返回GenericService&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check&lt;/td>
&lt;td>check&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省使用&lt;a href="dubbo:consumer">dubbo:consumer&lt;/a>的check&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>启动时检查提供者是否存在,true报错,false忽略&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>url&lt;/td>
&lt;td>url&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>点对点直连服务提供者地址,将绕过注册中心&lt;/td>
&lt;td>1.0.6以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>stub&lt;/td>
&lt;td>stub&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mock&lt;/td>
&lt;td>mock&lt;/td>
&lt;td>class/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。&lt;/td>
&lt;td>Dubbo1.0.13及其以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cache&lt;/td>
&lt;td>cache&lt;/td>
&lt;td>string/boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等&lt;/td>
&lt;td>Dubbo2.1.0及其以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>validation&lt;/td>
&lt;td>validation&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验&lt;/td>
&lt;td>Dubbo2.1.0及其以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>proxy&lt;/td>
&lt;td>proxy&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>javassist&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>选择动态代理实现策略,可选:javassist, jdk&lt;/td>
&lt;td>2.0.2以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>client&lt;/td>
&lt;td>client&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>客户端传输类型设置,如Dubbo协议的netty或mina。&lt;/td>
&lt;td>Dubbo2.0.0以上版本支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>registry&lt;/td>
&lt;td>&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>缺省将从所有注册中心获服务列表后合并结果&lt;/td>
&lt;td>配置关联&lt;/td>
&lt;td>从指定注册中心注册获取服务列表,在多个注册中心时使用,值为&lt;a href="dubbo:registry">dubbo:registry&lt;/a>的id属性,多个注册中心ID用逗号分隔&lt;/td>
&lt;td>2.0.0以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>owner&lt;/td>
&lt;td>owner&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>actives&lt;/td>
&lt;td>actives&lt;/td>
&lt;td>int&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>0&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>每服务消费者每服务每方法最大并发调用数&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cluster&lt;/td>
&lt;td>cluster&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>failover&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>集群方式,可选:failover/failfast/failsafe/failback/forking&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>filter&lt;/td>
&lt;td>reference.filter&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>default&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务消费方远程调用过程拦截器名称,多个名称用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>listener&lt;/td>
&lt;td>invoker.listener&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>default&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>服务消费方引用服务监听器名称,多个名称用逗号分隔&lt;/td>
&lt;td>2.0.5以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>layer&lt;/td>
&lt;td>layer&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>服务调用者所在的分层。如:biz、dao、intl:web、china:acton。&lt;/td>
&lt;td>2.0.7以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>init&lt;/td>
&lt;td>init&lt;/td>
&lt;td>boolean&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>false&lt;/td>
&lt;td>性能调优&lt;/td>
&lt;td>是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。&lt;/td>
&lt;td>2.0.10以上版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>protocol&lt;/td>
&lt;td>protocol&lt;/td>
&lt;td>string&lt;/td>
&lt;td>可选&lt;/td>
&lt;td>&lt;/td>
&lt;td>服务治理&lt;/td>
&lt;td>只调用指定协议的服务提供方,其它协议忽略。&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>原文地址:&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/20-dubbo3-fu-wu-yin-yong-pei-zhi-referenceconfig/">20-Dubbo3服务引用配置ReferenceConfig&lt;/a>&lt;/p></description></item><item><title>Blog: 19 重新来过从一个服务消费者的Demo说起</title><link>https://dubbo.apache.org/zh-cn/blog/2022/08/19/19-%E9%87%8D%E6%96%B0%E6%9D%A5%E8%BF%87%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</link><pubDate>Fri, 19 Aug 2022 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2022/08/19/19-%E9%87%8D%E6%96%B0%E6%9D%A5%E8%BF%87%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84demo%E8%AF%B4%E8%B5%B7/</guid><description>
&lt;h1 id="19-重新来过从一个服务消费者的demo说起">19 重新来过从一个服务消费者的Demo说起&lt;/h1>
&lt;p>为了更方便了解原理,我们先来编写一个Demo,从例子中来看源码实现:,前面说了提供者现在已经有服务注册上去了,那接下来我们编写一个消费者的例子来进行服务发现与服务RPC调用。&lt;/p>
&lt;h2 id="191-启动zookeeper">19.1 启动Zookeeper&lt;/h2>
&lt;p>为了Demo可以正常启动,需要我们先在本地启动一个Zookeeper如下图所示:
&lt;img src="https://dubbo.apache.org/imgs/blog/source-blog/19-zk.png" alt="在这里插入图片描述">&lt;/p>
&lt;h2 id="192-服务消费者">19.2 服务消费者&lt;/h2>
&lt;p>接下来给大家贴一下示例源码,这个源码来源于Dubbo源码目录的 dubbo-demo/dubbo-demo-api 目录下面的dubbo-demo-api-consumer子项目,这里我做了删减,方便看核心代码:
首先我们定义一个服务接口如下所示:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 同步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * 用于异步处理的服务方法
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @param name
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> * @return
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">default&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture&lt;span style="color:#719e07">.&lt;/span>completedFuture&lt;span style="color:#719e07">(&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>name&lt;span style="color:#719e07">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>服务实现类如下:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.RpcContext&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.Logger&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.slf4j.LoggerFactory&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> java.util.concurrent.CompletableFuture&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">DemoServiceImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> DemoService &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> Logger logger &lt;span style="color:#719e07">=&lt;/span> LoggerFactory&lt;span style="color:#719e07">.&lt;/span>getLogger&lt;span style="color:#719e07">(&lt;/span>DemoServiceImpl&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> String &lt;span style="color:#268bd2">sayHello&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger&lt;span style="color:#719e07">.&lt;/span>info&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, request from consumer: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getServiceContext&lt;span style="color:#719e07">().&lt;/span>getRemoteAddress&lt;span style="color:#719e07">());&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;Hello &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> name &lt;span style="color:#719e07">+&lt;/span> &lt;span style="color:#2aa198">&amp;#34;, response from provider: &amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> RpcContext&lt;span style="color:#719e07">.&lt;/span>getServiceContext&lt;span style="color:#719e07">().&lt;/span>getLocalAddress&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloAsync&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String name&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">null&lt;/span>&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="193-启用服务消费者">19.3 启用服务消费者&lt;/h2>
&lt;p>有了服务接口之后我们来启用服务,启用服务的源码如下:
这里如果要启动消费者,主要要修改QOS端口这里我已经配置可以直接复用&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">package&lt;/span> link.elastic.dubbo.consumer&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> link.elastic.dubbo.entity.DemoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.common.constants.CommonConstants&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ApplicationConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.MetadataReportConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ProtocolConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.ReferenceConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.RegistryConfig&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.config.bootstrap.DubboBootstrap&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> org.apache.dubbo.rpc.service.GenericService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">ConsumerApplication&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>&lt;span style="color:#719e07">(&lt;/span>String&lt;span style="color:#719e07">[]&lt;/span> args&lt;span style="color:#719e07">)&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runWithBootstrap&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#268bd2">static&lt;/span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">runWithBootstrap&lt;/span>&lt;span style="color:#719e07">()&lt;/span> &lt;span style="color:#719e07">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&lt;/span>DemoService&lt;span style="color:#719e07">&amp;gt;&lt;/span> reference &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ReferenceConfig&lt;span style="color:#719e07">&amp;lt;&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setInterface&lt;span style="color:#719e07">(&lt;/span>DemoService&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setGeneric&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reference&lt;span style="color:#719e07">.&lt;/span>setProtocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DubboBootstrap bootstrap &lt;span style="color:#719e07">=&lt;/span> DubboBootstrap&lt;span style="color:#719e07">.&lt;/span>getInstance&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ApplicationConfig applicationConfig &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">new&lt;/span> ApplicationConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo-demo-api-consumer&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosEnable&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#cb4b16">false&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> applicationConfig&lt;span style="color:#719e07">.&lt;/span>setQosPort&lt;span style="color:#719e07">(-&lt;/span>1&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bootstrap&lt;span style="color:#719e07">.&lt;/span>application&lt;span style="color:#719e07">(&lt;/span>applicationConfig&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>registry&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> RegistryConfig&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;zookeeper://8.131.79.126:2181&amp;#34;&lt;/span>&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>protocol&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#719e07">new&lt;/span> ProtocolConfig&lt;span style="color:#719e07">(&lt;/span>CommonConstants&lt;span style="color:#719e07">.&lt;/span>DUBBO&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">-&lt;/span>1&lt;span style="color:#719e07">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>reference&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">.&lt;/span>start&lt;span style="color:#719e07">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoService demoService &lt;span style="color:#719e07">=&lt;/span> bootstrap&lt;span style="color:#719e07">.&lt;/span>getCache&lt;span style="color:#719e07">().&lt;/span>get&lt;span style="color:#719e07">(&lt;/span>reference&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String message &lt;span style="color:#719e07">=&lt;/span> demoService&lt;span style="color:#719e07">.&lt;/span>sayHello&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>message&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// generic invoke
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> GenericService genericService &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#719e07">(&lt;/span>GenericService&lt;span style="color:#719e07">)&lt;/span> demoService&lt;span style="color:#719e07">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Object genericInvokeResult &lt;span style="color:#719e07">=&lt;/span> genericService&lt;span style="color:#719e07">.&lt;/span>$invoke&lt;span style="color:#719e07">(&lt;/span>&lt;span style="color:#2aa198">&amp;#34;sayHello&amp;#34;&lt;/span>&lt;span style="color:#719e07">,&lt;/span> &lt;span style="color:#719e07">new&lt;/span> String&lt;span style="color:#719e07">[]{&lt;/span>String&lt;span style="color:#719e07">.&lt;/span>class&lt;span style="color:#719e07">.&lt;/span>getName&lt;span style="color:#719e07">()},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">new&lt;/span> Object&lt;span style="color:#719e07">[]{&lt;/span>&lt;span style="color:#2aa198">&amp;#34;dubbo generic invoke&amp;#34;&lt;/span>&lt;span style="color:#719e07">});&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System&lt;span style="color:#719e07">.&lt;/span>out&lt;span style="color:#719e07">.&lt;/span>println&lt;span style="color:#719e07">(&lt;/span>genericInvokeResult&lt;span style="color:#719e07">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="14-启用服务后写入zookeeper的节点数据">1.4 启用服务后写入Zookeeper的节点数据&lt;/h2>
&lt;p>启动服务,这个时候我们打开Zookeeper图形化客户端来看看这个服务在Zookeeper上面写入来哪些数据,如下图:
在这里插入图片描述
&lt;img src="https://dubbo.apache.org/imgs/blog/source-blog/19-zk2.png" alt="在这里插入图片描述">&lt;/p>
&lt;p>写入Zookeper上的节点用于服务在分布式场景下的协调,这些节点是比较重要的。&lt;/p>
&lt;p>如果了解过Dubbo的同学,应该会知道Dubbo在低版本的时候会向注册中心中写入服务接口,具体路径在上面的 &lt;strong>dubbo目录下&lt;/strong> ,然后在 &lt;strong>/dubbo/服务接口/&lt;/strong> 路径下写入如下信息:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>服务提供者&lt;/strong>配置信息URL形式&lt;/li>
&lt;li>&lt;strong>服务消费者&lt;/strong>的配置信息URL形式&lt;/li>
&lt;li>服务&lt;strong>路由信息&lt;/strong>&lt;/li>
&lt;li>&lt;strong>配置信息&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>上面这个图就是Dubbo3的注册信息了,后面我们也会围绕细节来说明下,这里可以看下新增了:&lt;/p>
&lt;ul>
&lt;li>/dubbo/metadata &lt;strong>元数据信息&lt;/strong>&lt;/li>
&lt;li>/dubbo/mapping 服务和应用的&lt;strong>映射信息&lt;/strong>&lt;/li>
&lt;li>/dubbo/config &lt;strong>注册中心配置&lt;/strong>&lt;/li>
&lt;li>/services目录&lt;strong>应用信息&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>在这里可以大致了解下,在后面会有更详细的源码解析这个示例代码.通过透析代码来看透Dubbo3服务注册原理,服务提供原理。&lt;/p>
&lt;p>原文地址:&lt;a href="https://blog.elastic.link/2022/07/10/dubbo/19-chong-xin-lai-guo-cong-yi-ge-fu-wu-xiao-fei-zhe-de-demo-shuo-qi/">19-重新来过从一个服务消费者的Demo说起&lt;/a>&lt;/p></description></item></channel></rss>