blob: db31e1c507700d3c2d07b0d75c14594b84cc1b86 [file] [log] [blame]
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – 升级与兼容性</title><link>https://dubbo.apache.org/zh-cn/docs/migration/</link><description>Recent content in 升级与兼容性 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><atom:link href="https://dubbo.apache.org/zh-cn/docs/migration/index.xml" rel="self" type="application/rss+xml"/><item><title>Docs: 3.x 升级与兼容性指南</title><link>https://dubbo.apache.org/zh-cn/docs/migration/migration-and-compatibility-guide/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/docs/migration/migration-and-compatibility-guide/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>此文档已经不再维护。您当前查看的是快照版本。如果想要查看最新版本的文档,请参阅&lt;a href="https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/upgrades-and-compatibility/2.x-to-3.x-compatibility-guide/">最新版本&lt;/a>。&lt;/p>
&lt;/div>
&lt;p>&lt;strong>无需改动任何代码,直接升级到 Dubbo 3.0。&lt;/strong>&lt;/p>
&lt;p>在 3.0 版本的设计与开发之初,我们就定下了兼容老版本 Dubbo 用户(2.5、2.6、2.7)的目标。因此,往 3.0 版本的升级过程将会是完全透明的,用户无需做任何业务改造,升级 3.x 后的框架行为将保持与 2.x 版本完全一致。&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.0.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;p>但也要注意,透明升级仅仅是通往 3.0 的第一步,因为 &amp;ldquo;框架行为保持一致&amp;rdquo; 也就意味着用户将无法体验到 3.0 的新特性。&lt;strong>如果要启用 3.0 的带来的新特性,用户则需要进行一定的改造,我们称这个过程为迁移,这是一个按需开启的过程。&lt;/strong>&lt;/p>
&lt;p>因此,对老用户而言,有两条不同的迁移路径:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>分两步走,先以兼容模式推动业务升级到 3.0 版本(无需改造),之后在某些时机按需启用新特性(按需改造);&lt;/strong>&lt;/li>
&lt;li>&lt;strong>升级与迁移同步完成,在业务升级到 3.0 版本的同时,完成改造并启用新特性;&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Dubbo 3.0 提供的新特性包括:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>新的地址发现模型(应用级服务发现)。&lt;/strong>
&lt;ul>
&lt;li>查看&lt;a href="../migration-service-discovery">应用级服务发现的迁移步骤&lt;/a>&lt;/li>
&lt;li>查看应用级服务发现的使用方式&lt;/li>
&lt;li>查看应用级服务发现设计与实现。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>下一代基于 HTTP/2 的 Triple 协议。&lt;/strong>
&lt;ul>
&lt;li>查看&lt;a href="../migration-triple">Triple 协议迁移步骤&lt;/a>&lt;/li>
&lt;li>查看 &lt;a href="../../references/protocols/tri">Triple 协议使用方式&lt;/a>&lt;/li>
&lt;li>查看 Triple 协议设计与实现。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>统一的路由规则。&lt;/strong>
&lt;ul>
&lt;li>查看&lt;a href="../migration-routingrule/">统一路由规则的迁移步骤&lt;/a>&lt;/li>
&lt;li>查看&lt;a href="../../references/routers/">统一路由规则使用方式&lt;/a>&lt;/li>
&lt;li>查看统一路由规则设计与实现&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul></description></item><item><title>Docs: 应用级地址发现迁移指南</title><link>https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>此文档已经不再维护。您当前查看的是快照版本。如果想要查看最新版本的文档,请参阅&lt;a href="https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/upgrades-and-compatibility/service-discovery/">最新版本&lt;/a>。&lt;/p>
&lt;/div>
&lt;p>&lt;strong>总体上来说,在地址注册与发现环节,3.x 是完全兼容 2.x 版本的,这意味着,用户可以选择将集群内任意数量的应用或机器升级到 3.x,同时在这个过程中保持与 2.x 版本的互操作性。&lt;/strong>&lt;/p>
&lt;h2 id="1-快速升级步骤">1 快速升级步骤&lt;/h2>
&lt;p>简单的修改 pom.xml 到最新版本就可以完成升级,如果要迁移到应用级地址,只需要调整开关控制 3.x 版本的默认行为。&lt;/p>
&lt;ol>
&lt;li>升级 Provider 应用到最新 3.x 版本依赖,配置双注册开关&lt;code>dubbo.application.register-mode=all&lt;/code>(建议通过全局配置中心设置,默认已自动开启),完成应用发布。&lt;/li>
&lt;li>升级 Consumer 应用到最新 3.x 版本依赖,配置双订阅开关&lt;code>dubbo.application.service-discovery.migration=APPLICATION_FIRST&lt;/code>(建议通过全局配置中心设置,默认已自动开启),完成应用发布。&lt;/li>
&lt;li>在确认 Provider 的上有 Consumer 全部完成应用级地址迁移后,Provider 切到应用级地址单注册。完成升级&lt;/li>
&lt;/ol>
&lt;p>以下是关于迁移流程的详细描述。&lt;/p>
&lt;h2 id="2-provider-端升级过程详解">2 Provider 端升级过程详解&lt;/h2>
&lt;p>在不改变任何 Dubbo 配置的情况下,可以将一个应用或实例升级到 3.x 版本,升级后的 Dubbo 实例会默保保证与 2.x 版本的兼容性,即会正常注册 2.x 格式的地址到注册中心,因此升级后的实例仍会对整个集群仍保持可见状态。&lt;/p>
&lt;p>同时新的地址发现模型(注册应用级别的地址)也将会自动注册。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/provider-registration.png" alt="//imgs/v3/migration/provider-registration.png">&lt;/p>
&lt;p>通过 -D 参数,可以指定 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-text" data-lang="text">&lt;span style="display:flex;">&lt;span>-Ddubbo.application.register-mode=all
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 可选值 interface、instance、all,默认是 all,即接口级地址、应用级地址都注册
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>另外,可以在配置中心修改全局默认行为,来控制所有 3.x 实例注册行为。其中,全局性开关的优先级低于 -D 参数。&lt;/p>
&lt;p>为了保证平滑迁移,即升级到 3.x 的实例能同时被 2.x 与 3.x 的消费者实例发现,3.x 实例需要开启双注册;当所有上游的消费端都迁移到 3.x 的地址模型后,提供端就可以切换到 instance 模式(只注册应用级地址)。对于如何升级消费端到 3.x 请参见下一小节。&lt;/p>
&lt;h3 id="21-双注册带来的资源消耗">2.1 双注册带来的资源消耗&lt;/h3>
&lt;p>双注册不可避免的会带来额外的注册中心存储压力,但考虑到应用级地址发现模型的数据量在存储方面的极大优势,即使对于一些超大规模集群的用户而言,新增的数据量也并不会带来存储问题。总体来说,对于一个普通集群而言,数据增长可控制在之前数据总量的 1/100 ~ 1/1000&lt;/p>
&lt;p>以一个中等规模的集群实例来说: 2000 实例、50个应用(500 个 Dubbo 接口,平均每个应用 10 个接口)。&lt;/p>
&lt;p>​ 假设每个接口级 URL 地址平均大小为 5kb,每个应用级 URL 平均大小为 0.5kb&lt;/p>
&lt;p>​ 老的接口级地址量:2000 * 500 * 5kb ≈ 4.8G&lt;/p>
&lt;p>​ 新的应用级地址量:2000 * 50 * 0.5kb ≈ 48M&lt;/p>
&lt;p>​ 双注册后仅仅增加了 48M 的数据量。&lt;/p>
&lt;h2 id="3-consumer-端升级过程">3 Consumer 端升级过程&lt;/h2>
&lt;p>对于 2.x 的消费者实例,它们看到的自然都是 2.x 版本的提供者地址列表;&lt;/p>
&lt;p>对于 3.x 的消费者,它具备同时发现 2.x 与 3.x 提供者地址列表的能力。在默认情况下,如果集群中存在可以消费的 3.x 的地址,将自动消费 3.x 的地址,如果不存在新地址则自动消费 2.x 的地址。Dubbo3 提供了开关来控制这个行为:&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-text" data-lang="text">&lt;span style="display:flex;">&lt;span>dubbo.application.service-discovery.migration=APPLICATION_FIRST
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># 可选值
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># FORCE_INTERFACE,只消费接口级地址,如无地址则报错,单订阅 2.x 地址
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># APPLICATION_FIRST,智能决策接口级/应用级地址,双订阅
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>dubbo.application.service-discovery.migration &lt;/code> 支持通过 &lt;code>-D&lt;/code> 以及 &lt;code>全局配置中心&lt;/code> 两种方式进行配置。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/consumer-subscription.png" alt="//imgs/v3/migration/consumer-subscription.png">&lt;/p>
&lt;p>接下来,我们就具体看一下,如何通过双订阅模式(APPLICATION_FIRST)让升级到 3.x 的消费端迁移到应用级别的地址。在具体展开之前,先明确一条消费端的选址行为:&lt;strong>对于双订阅的场景,消费端虽然可同时持有 2.x 地址与 3.x 地址,但选址过程中两份地址是完全隔离的:要么用 2.x 地址,要么用 3.x 地址,不存在两份地址混合调用的情况,这个决策过程是在收到第一次地址通知后就完成了的。&lt;/strong>&lt;/p>
&lt;p>下面,我们看一个&lt;code>APPLICATION_FIRST&lt;/code>策略的具体操作过程。&lt;/p>
&lt;p>首先,提前在全局配置中心 Nacos 配置一条配置项(所有消费端都将默认执行这个选址策略):&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/nacos-migration-item.png" alt="//imgs/v3/migration/nacos-migration-item.png">&lt;/p>
&lt;p>紧接着,升级消费端到 3.x 版本并启动,这时消费端读取到&lt;code>APPLICATION_FIRST&lt;/code>配置后,执行双订阅逻辑(订阅 2.x 接口级地址与 3.x 应用级地址)&lt;/p>
&lt;p>至此,升级操作就完成了,剩下的就是框架内部的执行了。在调用发生前,框架在消费端会有一个“选址过程”,注意这里的选址和之前 2.x 版本是有区别的,选址过程包含了两层筛选:&lt;/p>
&lt;ul>
&lt;li>先进行地址列表(ClusterInvoker)筛选(接口级地址 or 应用级地址)&lt;/li>
&lt;li>再进行实际的 provider 地址(Invoker)筛选。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/migration-cluster-invoker.png" alt="//imgs/v3/migration/migration-cluster-item.png">&lt;/p>
&lt;p>ClusterInvoker 筛选的依据,可以通过 MigrationAddressComparator SPI 自行定义,目前官方提供了一个简单的地址数量比对策略,即当 &lt;code>应用级地址数量 == 接口级地址数量&lt;/code> 满足时则进行迁移。&lt;/p>
&lt;blockquote>
&lt;p>其实 FORCE_INTERFACE、APPLICATION_FIRST、FORCE_APPLICATION 控制的都是这里的 ClusterInvoker 类型的筛选策略&lt;/p>
&lt;/blockquote>
&lt;h3 id="31-双订阅带来的资源消耗">3.1 双订阅带来的资源消耗&lt;/h3>
&lt;p>双订阅不可避免的会增加消费端的内存消耗,但由于应用级地址发现在地址总量方面的优势,这个过程通常是可接受的,我们从两个方面进行分析:&lt;/p>
&lt;ol>
&lt;li>双订阅带来的地址推送数据量增长。这点我们在 ”双注册资源消耗“ 一节中做过介绍,应用级服务发现带来的注册中心数据量增长非常有限。&lt;/li>
&lt;li>双订阅带来的消费端内存增长。要注意双订阅只存在于启动瞬态,在ClusterInvoker选址决策之后其中一份地址就会被完全销毁;对单个服务来说,启动阶段双订阅带来的内存增长大概能控制在原内存量的 30% ~ 40%,随后就会下降到单订阅水平,如果切到应用级地址,能实现内存 50% 的下降。&lt;/li>
&lt;/ol>
&lt;h3 id="32-消费端更细粒度的控制">3.2 消费端更细粒度的控制&lt;/h3>
&lt;p>除了全局的迁移策略之外,Dubbo 在消费端提供了更细粒度的迁移策略支持。控制单位可以是某一个消费者应用,它消费的服务A、服务B可以有各自独立的迁移策略,具体是用方式是在消费端配置迁移规则:&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>: demo-consumer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">step&lt;/span>: APPLICATION_FIRST
&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">name&lt;/span>: demo-provider
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: FORCE_APPLICATION
&lt;/span>&lt;/span>&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:#268bd2">serviceKey&lt;/span>: org.apache.dubbo.config.api.DemoService:1.0.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">step&lt;/span>: FORCE_INTERFACE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用这种方式能做到比较精细迁移控制,但是当下及后续的改造成本会比较高,除了一些特别场景,我们不太推荐启用这种配置方式。
(&lt;a href="../../advanced/migration-invoker/">迁移指南&lt;/a>) &lt;strong>官方推荐使用的全局的开关式的迁移策略,让消费端实例在启动阶段自行决策使用哪份可用的地址列表。&lt;/strong>&lt;/p>
&lt;h2 id="4-迁移状态的收敛">4 迁移状态的收敛&lt;/h2>
&lt;p>为了同时兼容 2.x 版本,升级到 3.x 版本的应用在一段时间内要么处在双注册状态,要么处在双订阅状态。&lt;/p>
&lt;p>解决这个问题,我们还是从 Provider 视角来看,当所有的 Provider 都切换到应用级地址注册之后,也就不存在双订阅的问题了。&lt;/p>
&lt;h3 id="41-不同的升级策略影响很大">4.1 不同的升级策略影响很大&lt;/h3>
&lt;p>毫无疑问越早越彻底的升级,就能尽快摆脱这个局面。设想,如果可以将组织内所有的应用都升级到 3.x 版本,则版本收敛就变的非常简单:升级过程中 Provider 始终保持双注册,当所有的应用都升级到 3.x 之后,就可以调整全局默认行为,让 Provider 都变成应用级地址单注册了,这个过程并不会给 Consumer 应用带来困扰,因为它们已经是可以识别应用级地址的 3.x 版本了。&lt;/p>
&lt;p>如果没有办法做到应用的全量升级,甚至在相当长的时间内只能升级一部分应用,则不可避免的迁移状态要持续比较长的时间。
在这种情况下,我们追求的只能是尽量保持已升级应用的上下游实现版本及功能收敛。推动某些 Provider 的上游消费者都升级到 Dubbo3,这样就可以解除这部分 Provider 的双注册,要做到这一点,可能需要一些辅助统计工具的支持。&lt;/p>
&lt;ol>
&lt;li>要能分析出应用间的依赖关系,比如一个 Provdier 应用被哪些消费端应用消费,这可以通过 Dubbo 提供的服务元数据上报能力来实现。&lt;/li>
&lt;li>要能知道每个应用当前使用的 dubbo 版本,可以通过扫描或者主动上报手段。&lt;/li>
&lt;/ol></description></item><item><title>Docs: Triple协议迁移指南</title><link>https://dubbo.apache.org/zh-cn/docs/migration/migration-triple/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/docs/migration/migration-triple/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>此文档已经不再维护。您当前查看的是快照版本。如果想要查看最新版本的文档,请参阅&lt;a href="https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/upgrades-and-compatibility/migration-triple/">最新版本&lt;/a>。&lt;/p>
&lt;/div>
&lt;h2 id="triple-介绍">Triple 介绍&lt;/h2>
&lt;p>&lt;code>Triple&lt;/code> 协议的格式和原理请参阅 &lt;a href="https://dubbo.apache.org/zh-cn/docs/concepts/rpc-protocol/">RPC 通信协议&lt;/a>&lt;/p>
&lt;p>根据 Triple 设计的目标,&lt;code>Triple&lt;/code> 协议有以下优势:&lt;/p>
&lt;ul>
&lt;li>具备跨语言交互的能力,传统的多语言多 SDK 模式和 Mesh 化跨语言模式都需要一种更通用易扩展的数据传输协议。&lt;/li>
&lt;li>提供更完善的请求模型,除了支持传统的 Request/Response 模型(Unary 单向通信),还支持 Stream(流式通信) 和 Bidirectional(双向通信)。&lt;/li>
&lt;li>易扩展、穿透性高,包括但不限于 Tracing / Monitoring 等支持,也应该能被各层设备识别,网关设施等可以识别数据报文,对 Service Mesh 部署友好,降低用户理解难度。&lt;/li>
&lt;li>完全兼容 grpc,客户端/服务端可以与原生grpc客户端打通。&lt;/li>
&lt;li>可以复用现有 grpc 生态下的组件, 满足云原生场景下的跨语言、跨环境、跨平台的互通需求。&lt;/li>
&lt;/ul>
&lt;p>当前使用其他协议的 Dubbo 用户,框架提供了兼容现有序列化方式的迁移能力,在不影响线上已有业务的前提下,迁移协议的成本几乎为零。&lt;/p>
&lt;p>需要新增对接 Grpc 服务的 Dubbo 用户,可以直接使用 Triple 协议来实现打通,不需要单独引入 grpc client 来完成,不仅能保留已有的 Dubbo 易用性,也能降低程序的复杂度和开发运维成本,不需要额外进行适配和开发即可接入现有生态。&lt;/p>
&lt;p>对于需要网关接入的 Dubbo 用户,Triple 协议提供了更加原生的方式,让网关开发或者使用开源的 grpc 网关组件更加简单。网关可以选择不解析 payload ,在性能上也有很大提高。在使用 Dubbo 协议时,语言相关的序列化方式是网关的一个很大痛点,而传统的 HTTP 转 Dubbo 的方式对于跨语言序列化几乎是无能为力的。同时,由于 Triple 的协议元数据都存储在请求头中,网关可以轻松的实现定制需求,如路由和限流等功能。&lt;/p>
&lt;h2 id="dubbo2-协议迁移流程">Dubbo2 协议迁移流程&lt;/h2>
&lt;p>Dubbo2 的用户使用 dubbo 协议 + 自定义序列化,如 hessian2 完成远程调用。&lt;/p>
&lt;p>而 Grpc 的默认仅支持 Protobuf 序列化,对于 Java 语言中的多参数以及方法重载也无法支持。&lt;/p>
&lt;p>Dubbo3的之初就有一条目标是完美兼容 Dubbo2,所以为了 Dubbo2 能够平滑升级, Dubbo 框架侧做了很多工作来保证升级的无感,目前默认的序列化和 Dubbo2 保持一致为&lt;code>hessian2&lt;/code>。&lt;/p>
&lt;p>所以,如果决定要升级到 Dubbo3 的 &lt;code>Triple&lt;/code> 协议,只需要修改配置中的协议名称为 &lt;code>tri&lt;/code> (注意: 不是triple)即可。&lt;/p>
&lt;p>接下来我们我们以一个使用 Dubbo2 协议的&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri">工程&lt;/a> 来举例,如何一步一步安全的升级。&lt;/p>
&lt;ol>
&lt;li>仅使用 &lt;code>dubbo&lt;/code> 协议启动 &lt;code>provider&lt;/code> 和 &lt;code>consumer&lt;/code>,并完成调用。&lt;/li>
&lt;li>使用 &lt;code>dubbo&lt;/code> 和 &lt;code>tri&lt;/code> 协议 启动&lt;code>provider&lt;/code>,以 &lt;code>dubbo&lt;/code> 协议启动 &lt;code>consumer&lt;/code>,并完成调用。&lt;/li>
&lt;li>仅使用 &lt;code>tri&lt;/code> 协议 启动 &lt;code>provider&lt;/code>和 &lt;code>consumer&lt;/code>,并完成调用。&lt;/li>
&lt;/ol>
&lt;h3 id="定义服务">定义服务&lt;/h3>
&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">IWrapperGreeter&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>&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"> * 这是一个普通接口,没有使用 pb 序列化
&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>(String request);
&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;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:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">IGreeter2Impl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> IWrapperGreeter {
&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>(String request) {
&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> request;
&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;h3 id="仅使用-dubbo-协议">仅使用 dubbo 协议&lt;/h3>
&lt;p>为保证兼容性,我们先将部分 provider 升级到 &lt;code>dubbo3&lt;/code> 版本并使用 &lt;code>dubbo&lt;/code> 协议。&lt;/p>
&lt;p>使用 &lt;code>dubbo&lt;/code> 协议启动一个 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationDubboProvider.java">&lt;code>Provider&lt;/code>&lt;/a> 和 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationDubboConsumer.java">&lt;code>Consumer&lt;/code>&lt;/a> ,完成调用,输出如下:
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/dubbo3-tri-migration-dubbo-dubbo-result.png" alt="result">&lt;/p>
&lt;h3 id="同时使用-dubbo-和-triple-协议">同时使用 dubbo 和 triple 协议&lt;/h3>
&lt;p>对于线上服务的升级,不可能一蹴而就同时完成 provider 和 consumer 升级, 需要按步操作,保证业务稳定。
第二步, provider 提供双协议的方式同时支持 dubbo + tri 两种协议的客户端。&lt;/p>
&lt;p>结构如图所示:
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/migrate-dubbo-tri-strust.png" alt="strust">&lt;/p>
&lt;blockquote>
&lt;p>按照推荐升级步骤,provider 已经支持了tri协议,所以 dubbo3的 consumer 可以直接使用 tri 协议&lt;/p>
&lt;/blockquote>
&lt;p>使用&lt;code>dubbo&lt;/code>协议和&lt;code>triple&lt;/code>协议启动&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationBothProvider.java">&lt;code>Provider&lt;/code>&lt;/a>和&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationBothConsumer.java">&lt;code>Consumer&lt;/code>&lt;/a>,完成调用,输出如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/dubbo3-tri-migration-both-dubbo-tri-result.png" alt="result">&lt;/p>
&lt;h3 id="仅使用-triple-协议">仅使用 triple 协议&lt;/h3>
&lt;p>当所有的 consuemr 都升级至支持 &lt;code>Triple&lt;/code> 协议的版本后,provider 可切换至仅使用 &lt;code>Triple&lt;/code> 协议启动&lt;/p>
&lt;p>结构如图所示:
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/migrate-only-tri-strust.png" alt="strust">&lt;/p>
&lt;p>&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationTriProvider.java">Provider&lt;/a>
和 &lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationTriConsumer.java">Consumer&lt;/a> 完成调用,输出如下:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/dubbo3-tri-migration-tri-tri-result.png" alt="result">&lt;/p>
&lt;h3 id="实现原理">实现原理&lt;/h3>
&lt;p>通过上面介绍的升级过程,我们可以很简单的通过修改协议类型来完成升级。框架是怎么帮我们做到这些的呢?&lt;/p>
&lt;p>通过对 &lt;code>Triple&lt;/code> 协议的介绍,我们知道Dubbo3的 &lt;code>Triple&lt;/code> 的数据类型是 &lt;code>protobuf&lt;/code> 对象,那为什么非 &lt;code>protobuf&lt;/code> 的 java 对象也可以被正常传输呢。&lt;/p>
&lt;p>这里 Dubbo3 使用了一个巧妙的设计,首先判断参数类型是否为 &lt;code>protobuf&lt;/code> 对象,如果不是。用一个 &lt;code>protobuf&lt;/code> 对象将 &lt;code>request&lt;/code> 和 &lt;code>response&lt;/code> 进行 wrapper,这样就屏蔽了其他各种序列化带来的复杂度。在 &lt;code>wrapper&lt;/code> 对象内部声明序列化类型,来支持序列化的扩展。&lt;/p>
&lt;p>wrapper 的&lt;code>protobuf&lt;/code>的 IDL如下:&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-proto" data-lang="proto">&lt;span style="display:flex;">&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">package&lt;/span> org&lt;span style="color:#719e07">.&lt;/span>apache.dubbo.triple;
&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">message&lt;/span> &lt;span style="color:#268bd2">TripleRequestWrapper&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// hessian4
&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">// json
&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">string&lt;/span> serializeType &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 style="color:#719e07">repeated&lt;/span> &lt;span style="color:#dc322f">bytes&lt;/span> args &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">2&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">repeated&lt;/span> &lt;span style="color:#dc322f">string&lt;/span> argTypes &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">3&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">message&lt;/span> &lt;span style="color:#268bd2">TripleResponseWrapper&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">string&lt;/span> serializeType &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 style="color:#dc322f">bytes&lt;/span> data &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">2&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">string&lt;/span> type &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">3&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>对于请求,使用&lt;code>TripleRequestWrapper&lt;/code>进行包装,对于响应使用&lt;code>TripleResponseWrapper&lt;/code>进行包装。&lt;/p>
&lt;blockquote>
&lt;p>对于请求参数,可以看到 args 被&lt;code>repeated&lt;/code>修饰,这是因为需要支持 java 方法的多个参数。当然,序列化只能是一种。序列化的实现沿用 Dubbo2 实现的 spi&lt;/p>
&lt;/blockquote>
&lt;h2 id="多语言用户-正在使用-protobuf">多语言用户 (正在使用 Protobuf)&lt;/h2>
&lt;blockquote>
&lt;p>建议新服务均使用该方式&lt;/p>
&lt;/blockquote>
&lt;p>对于 Dubbo3 和 Triple 来说,主推的是使用 &lt;code>protobuf&lt;/code> 序列化,并且使用 &lt;code>proto&lt;/code> 定义的 &lt;code>IDL&lt;/code> 来生成相关接口定义。以 &lt;code>IDL&lt;/code> 做为多语言中的通用接口约定,加上 &lt;code>Triple&lt;/code> 与 &lt;code>Grpc&lt;/code> 的天然互通性,可以轻松地实现跨语言交互,例如 Go 语言等。&lt;/p>
&lt;p>将编写好的 &lt;code>.proto&lt;/code> 文件使用 &lt;code>dubbo-compiler&lt;/code> 插件进行编译并编写实现类,完成方法调用:&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/dubbo3-tri-migration-tri-tri-result.png" alt="result">&lt;/p>
&lt;p>从上面升级的例子我们可以知道,&lt;code>Triple&lt;/code> 协议使用 &lt;code>protbuf&lt;/code> 对象序列化后进行传输,所以对于本身就是 &lt;code>protobuf&lt;/code> 对象的方法来说,没有任何其他逻辑。&lt;/p>
&lt;p>使用 &lt;code>protobuf&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">PbGreeter&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">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> String JAVA_SERVICE_NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.sample.tri.PbGreeter&amp;#34;&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">final&lt;/span> String SERVICE_NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.sample.tri.PbGreeter&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:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> inited &lt;span style="color:#719e07">=&lt;/span> PbGreeterDubbo.init();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org.apache.dubbo.sample.tri.GreeterReply &lt;span style="color:#268bd2">greet&lt;/span>(org.apache.dubbo.sample.tri.GreeterRequest request);
&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> CompletableFuture&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">greetAsync&lt;/span>(org.apache.dubbo.sample.tri.GreeterRequest request){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> CompletableFuture.supplyAsync(() &lt;span style="color:#719e07">-&amp;gt;&lt;/span> greet(request));
&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:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">greetServerStream&lt;/span>(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org.apache.dubbo.common.stream.StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterRequest&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">greetStream&lt;/span>(org.apache.dubbo.common.stream.StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="开启-triple-新特性--stream-流">开启 Triple 新特性 —— Stream (流)&lt;/h2>
&lt;p>Stream 是 Dubbo3 新提供的一种调用类型,在以下场景时建议使用流的方式:&lt;/p>
&lt;ul>
&lt;li>接口需要发送大量数据,这些数据无法被放在一个 RPC 的请求或响应中,需要分批发送,但应用层如果按照传统的多次 RPC 方式无法解决顺序和性能的问题,如果需要保证有序,则只能串行发送&lt;/li>
&lt;li>流式场景,数据需要按照发送顺序处理, 数据本身是没有确定边界的&lt;/li>
&lt;li>推送类场景,多个消息在同一个调用的上下文中被发送和处理&lt;/li>
&lt;/ul>
&lt;p>Stream 分为以下三种:&lt;/p>
&lt;ul>
&lt;li>SERVER_STREAM(服务端流)
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/migrate-server-stream.png" alt="SERVER_STREAM">&lt;/li>
&lt;li>CLIENT_STREAM(客户端流)
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/migrate-client-stream.png" alt="CLIENT_STREAM">&lt;/li>
&lt;li>BIDIRECTIONAL_STREAM(双向流)
&lt;img src="https://dubbo.apache.org/imgs/v3/migration/tri/migrate-bi-stream.png" alt="BIDIRECTIONAL_STREAM">&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>由于 &lt;code>java&lt;/code> 语言的限制,BIDIRECTIONAL_STREAM 和 CLIENT_STREAM 的实现是一样的。&lt;/p>
&lt;/blockquote>
&lt;p>在 Dubbo3 中,流式接口以 &lt;code>SteamObserver&lt;/code> 声明和使用,用户可以通过使用和实现这个接口来发送和处理流的数据、异常和结束。&lt;/p>
&lt;blockquote>
&lt;p>对于 Dubbo2 用户来说,可能会对StreamObserver感到陌生,这是Dubbo3定义的一种流类型,Dubbo2 中并不存在 Stream 的类型,所以对于迁移场景没有任何影响。&lt;/p>
&lt;/blockquote>
&lt;p>流的语义保证&lt;/p>
&lt;ul>
&lt;li>提供消息边界,可以方便地对消息单独处理&lt;/li>
&lt;li>严格有序,发送端的顺序和接收端顺序一致&lt;/li>
&lt;li>全双工,发送不需要等待&lt;/li>
&lt;li>支持取消和超时&lt;/li>
&lt;/ul>
&lt;h3 id="非-pb-序列化的流">非 PB 序列化的流&lt;/h3>
&lt;ol>
&lt;li>api&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">IWrapperGreeter&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloStream&lt;/span>(StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> response);
&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:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">sayHelloServerStream&lt;/span>(String request, StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> response);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Stream 方法的方法入参和返回值是严格约定的,为防止写错而导致问题,Dubbo3 框架侧做了对参数的检查, 如果出错则会抛出异常。
对于 &lt;code>双向流(BIDIRECTIONAL_STREAM)&lt;/code>, 需要注意参数中的 &lt;code>StreamObserver&lt;/code> 是响应流,返回参数中的 &lt;code>StreamObserver&lt;/code> 为请求流。&lt;/p>
&lt;/blockquote>
&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:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">WrapGreeterImpl&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> WrapGreeter {
&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>&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> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">sayHelloStream&lt;/span>(StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> response) {
&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> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&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">@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">onNext&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(data);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.onNext(&lt;span style="color:#2aa198">&amp;#34;hello,&amp;#34;&lt;/span>&lt;span style="color:#719e07">+&lt;/span>data);
&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">@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">onError&lt;/span>(Throwable throwable) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> throwable.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 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">onCompleted&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onCompleted&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.onCompleted();
&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> &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">sayHelloServerStream&lt;/span>(String request, StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> response) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> 10; i&lt;span style="color:#719e07">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.onNext(&lt;span style="color:#2aa198">&amp;#34;hello,&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> request);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response.onCompleted();
&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;ol start="3">
&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>delegate.sayHelloServerStream(&lt;span style="color:#2aa198">&amp;#34;server stream&amp;#34;&lt;/span>, &lt;span style="color:#719e07">new&lt;/span> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&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">@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">onNext&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(data);
&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">@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">onError&lt;/span>(Throwable throwable) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> throwable.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 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">onCompleted&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onCompleted&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>&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>StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&lt;span style="color:#719e07">&amp;gt;&lt;/span> request &lt;span style="color:#719e07">=&lt;/span> delegate.sayHelloStream(&lt;span style="color:#719e07">new&lt;/span> StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>String&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">@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">onNext&lt;/span>(String data) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(data);
&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">@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">onError&lt;/span>(Throwable throwable) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> throwable.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 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">onCompleted&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#2aa198">&amp;#34;onCompleted&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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">for&lt;/span> (&lt;span style="color:#dc322f">int&lt;/span> i &lt;span style="color:#719e07">=&lt;/span> 0; i &lt;span style="color:#719e07">&amp;lt;&lt;/span> n; i&lt;span style="color:#719e07">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> request.onNext(&lt;span style="color:#2aa198">&amp;#34;stream request&amp;#34;&lt;/span> &lt;span style="color:#719e07">+&lt;/span> i);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>request.onCompleted();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="使用-protobuf-序列化的流">使用 Protobuf 序列化的流&lt;/h2>
&lt;p>对于 &lt;code>Protobuf&lt;/code> 序列化方式,推荐编写 &lt;code>IDL&lt;/code> 使用 &lt;code>compiler&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">PbGreeter&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">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> String JAVA_SERVICE_NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.sample.tri.PbGreeter&amp;#34;&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">final&lt;/span> String SERVICE_NAME &lt;span style="color:#719e07">=&lt;/span> &lt;span style="color:#2aa198">&amp;#34;org.apache.dubbo.sample.tri.PbGreeter&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:#268bd2">static&lt;/span> &lt;span style="color:#268bd2">final&lt;/span> &lt;span style="color:#dc322f">boolean&lt;/span> inited &lt;span style="color:#719e07">=&lt;/span> PbGreeterDubbo.init();
&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#dc322f">void&lt;/span> &lt;span style="color:#268bd2">greetServerStream&lt;/span>(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> org.apache.dubbo.common.stream.StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterRequest&lt;span style="color:#719e07">&amp;gt;&lt;/span> &lt;span style="color:#268bd2">greetStream&lt;/span>(org.apache.dubbo.common.stream.StreamObserver&lt;span style="color:#719e07">&amp;lt;&lt;/span>org.apache.dubbo.sample.tri.GreeterReply&lt;span style="color:#719e07">&amp;gt;&lt;/span> responseObserver);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="流的实现原理">流的实现原理&lt;/h3>
&lt;p>&lt;code>Triple&lt;/code>协议的流模式是怎么支持的呢?&lt;/p>
&lt;ul>
&lt;li>
&lt;p>从协议层来说,&lt;code>Triple&lt;/code> 是建立在 &lt;code>HTTP2&lt;/code> 基础上的,所以直接拥有所有 &lt;code>HTTP2&lt;/code> 的能力,故拥有了分 &lt;code>stream&lt;/code> 和全双工的能力。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>框架层来说,&lt;code>StreamObserver&lt;/code> 作为流的接口提供给用户,用于入参和出参提供流式处理。框架在收发 stream data 时进行相应的接口调用, 从而保证流的生命周期完整。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="triple-与应用级注册发现">Triple 与应用级注册发现&lt;/h2>
&lt;p>关于 Triple 协议的应用级服务注册和发现和其他语言是一致的,可以通过下列内容了解更多。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://dubbo.apache.org/zh-cn/docs/concepts/service-discovery/">服务发现&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://dubbo.apache.org/zh-cn/docs/migration/migration-service-discovery/">应用级地址发现迁移指南&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="与-grpc-互通">与 GRPC 互通&lt;/h2>
&lt;p>通过对于协议的介绍,我们知道 &lt;code>Triple&lt;/code> 协议是基于 &lt;code>HTTP2&lt;/code> 并兼容 &lt;code>GRPC&lt;/code>。为了保证和验证与&lt;code>GRPC&lt;/code>互通能力,Dubbo3 也编写了各种从场景下的测试。详细的可以通过&lt;a href="https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/README.MD">这里&lt;/a> 了解更多。&lt;/p>
&lt;h2 id="未来-everything-on-stub">未来: Everything on Stub&lt;/h2>
&lt;p>用过 &lt;code>Grpc&lt;/code> 的同学应该对 &lt;code>Stub&lt;/code> 都不陌生。
Grpc 使用 &lt;code>compiler&lt;/code> 将编写的 &lt;code>proto&lt;/code> 文件编译为相关的 protobuf 对象和相关 rpc 接口。默认的会同时生成几种不同的 &lt;code>stub&lt;/code>&lt;/p>
&lt;ul>
&lt;li>blockingStub&lt;/li>
&lt;li>futureStub&lt;/li>
&lt;li>reactorStub&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>&lt;code>stub&lt;/code> 用一种统一的使用方式帮我们屏蔽了不同调用方式的细节。不过目前 &lt;code>Dubbo3&lt;/code> 暂时只支持传统定义接口并进行调用的使用方式。&lt;/p>
&lt;p>在不久的未来,&lt;code>Triple&lt;/code> 也将实现各种常用的 &lt;code>Stub&lt;/code>,让用户写一份&lt;code>proto&lt;/code>文件,通过 &lt;code>comipler&lt;/code> 可以在任意场景方便的使用,请拭目以待。&lt;/p></description></item><item><title>Docs: 统一路由规则升级指南</title><link>https://dubbo.apache.org/zh-cn/docs/migration/migration-routingrule/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/docs/migration/migration-routingrule/</guid><description>
&lt;div class="pageinfo pageinfo-primary">
&lt;p>此文档已经不再维护。您当前查看的是快照版本。如果想要查看最新版本的文档,请参阅&lt;a href="https://dubbo.apache.org/zh-cn/overview/tasks/traffic-management/">最新版本&lt;/a>。&lt;/p>
&lt;/div></description></item></channel></rss>