Proxyless 模式是指 Dubbo 直接与 Istiod 通信,通过 xDS 协议实现服务发现和服务治理等能力。本示例中将通过一个简单的示例来演示如何使用 Proxyless 模式。示例地址代码架构本小节中主要介绍本文所使用的示例的代码架构,通过模仿本示例中的相关配置改造已有的项目代码可以使已有的项目快速跑在 Proxyless Mesh 模式下。1. 接口定义为了示例足够简单,这里使用了一个简单的接口定义,仅对参数做拼接进行返回。public interface GreetingService { String sayHello(String name); }2. 接口实现@DubboService(version = "1.0.0") public class AnnotatedGreetingService implements GreetingService { @Override public String sayHello(String name) { System.out.println("greeting service received: " + name); return "hello, " + name + "! from host: " + NetUtils.getLocalHost(); } }3. 客户端订阅方式由于原生 xDS 协议无法支持获取从接口到应用名的映射,因此需要配置 providedBy 参数来标记此服务来自哪个应用。未来我们将基于 Dubbo Mesh 的控制面实现自动的服务映射关系获取,届时将不需要独立配置参数即可将 Dubbo 运行在 Mesh 体系下,敬请期待。@Component("annotatedConsumer") public class GreetingServiceConsumer { @DubboReference(version = "1.0.0", providedBy = "dubbo-samples-xds-provider") private GreetingService greetingService; public String doSayHello(String name) { return greetingService.sayHello(name); } }4. 服务端配置服务端配置注册中心为 istio 的地址,协议为 xds。我们建议将 protocol 配置为 tri 协议(全面兼容 grpc 协议),以获得在 istio 体系下更好的体验。为了使 Kubernetes 感知到应用的状态,需要配置 qosAcceptForeignIp 参数,以便 Kubernetes 可以获得正确的应用状态,对齐生命周期。dubbo.application.name=dubbo-samples-xds-provider dubbo.application.metadataServicePort=20885 dubbo.registry.address=xds://istiod.istio-system.svc:15012 dubbo.protocol.name=tri dubbo.protocol.port=50052 dubbo.application.qosAcceptForeignIp=true5. 客户端配置客户端配置注册中心为 istio 的地址,协议为 xds。dubbo.application.name=dubbo-samples-xds-consumer dubbo.application.metadataServicePort=20885 dubbo.registry.address=xds://istiod.istio-system.svc:15012 dubbo.application.qosAcceptForeignIp=true快速开始Step 1: 搭建 Kubernetes 环境目前 Dubbo 仅支持在 Kubernetes 环境下的 Mesh 部署,所以在运行启动本示例前需要先搭建 Kubernetes 环境。搭建参考文档:minikube(https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/)kubeadm(https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/)k3s(https://k3s.io/)Step 2: 搭建 Istio 环境搭建 Istio 环境参考文档:Istio 安装文档(https://istio.io/latest/docs/setup/getting-started/)注:安装 Istio 的时候需要开启 first-party-jwt 支持(使用 istioctl 工具安装的时候加上 --set values.global.jwtPolicy=first-party-jwt 参数),否则将导致客户端认证失败的问题。附安装命令参考:curl -L https://istio.io/downloadIstio | sh - cd istio-1.xx.x export PATH=$PWD/bin:$PATH istioctl install --set profile=demo --set values.global.jwtPolicy=first-party-jwt -yStep 3: 拉取代码并构建git clone https://github.com/apache/dubbo-samples.git cd dubbo-samples/dubbo-samples-xds mvn clean package -DskipTestsStep 4: 构建镜像由于 Kubernetes 采用容器化部署,需要将代码打包在镜像中再进行部署。cd ./dubbo-samples-xds-provider/ # dubbo-samples-xds/dubbo-samples-xds-provider/Dockerfile docker build -t apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 . cd ../dubbo-samples-xds-consumer/ # dubbo-samples-xds/dubbo-samples-xds-consumer/Dockerfile docker build -t apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 . cd ../Step 5: 创建namespace# 初始化命名空间 kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/dubbo-samples-xds/deploy/Namespace.yml # 切换命名空间 kubens dubbo-demoStep 6: 部署容器cd ./dubbo-samples-xds-provider/src/main/resources/k8s # dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Deployment.yml # dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Service.yml kubectl apply -f Deployment.yml kubectl apply -f Service.yml cd ../../../../../dubbo-samples-xds-consumer/src/main/resources/k8s # dubbo-samples-xds/dubbo-samples-xds-consumer/src/main/resources/k8s/Deployment.yml kubectl apply -f Deployment.yml cd ../../../../../查看 consumer 的日志可以观察到如下的日志:result: hello, xDS Consumer! from host: 172.17.0.5 result: hello, xDS Consumer! from host: 172.17.0.5 result: hello, xDS Consumer! from host: 172.17.0.6 result: hello, xDS Consumer! from host: 172.17.0.6常见问题配置独立的 Istio 集群 clusterId通常在 Kubernetes 体系下 Istio 的 clusterId 是 Kubernetes,如果你使用的是自建的 istio 生产集群或者云厂商提供的集群则可能需要配置 clusterId。配置方式:指定 ISTIO_META_CLUSTER_ID 环境变量为所需的 clusterId。参考配置:apiVersion: apps/v1 kind: Deployment metadata: name: dubbo-samples-xds-consumer spec: selector: matchLabels: demo: consumer replicas: 2 template: metadata: labels: demo: consumer spec: containers: - env: - name: ISTIO_META_CLUSTER_ID value: Kubernetes - name: dubbo-samples-xds-provider image: xxxclusterId 获取方式:kubectl describe pod -n istio-system istiod-58b4f65df9-fq2ks读取环境变量中 CLUSTER_ID 的值Istio 认证失败由于当前 Dubbo 版本还不支持 istio 的 third-party-jwt 认证,所以需要配置 jwtPolicy 为 first-party-jwt。providedBy由于当前 Dubbo 版本受限于 istio 的通信模型无法获取接口所对应的应用名,因此需要配置 providedBy 参数来标记此服务来自哪个应用。未来我们将基于 Dubbo Mesh 的控制面实现自动的服务映射关系获取,届时将不需要独立配置参数即可将 Dubbo 运行在 Mesh 体系下,敬请期待。protocol nameProxyless 模式下应用级服务发现通过 Kubernetes Native Service 来进行应用服务发现,而由于 istio 的限制,目前只支持 http 协议和 grpc 协议的流量拦截转发,所以 Kubernetes Service 在配置的时候需要指定 spec.ports.name 属性为 http 或者 grpc 开头。因此我们建议使用 triple 协议(完全兼容 grpc 协议)。此处即使 name 配置为 grpc 开头,但是实际上是 dubbo 协议也可以正常服务发现,但是影响流量路由的功能。参考配置:apiVersion: v1 kind: Service metadata: name: dubbo-samples-xds-provider spec: clusterIP: None selector: demo: provider ports: - name: grpc-tri protocol: TCP port: 50052 targetPort: 50052metadataServicePort由于 Dubbo 3 应用级服务发现的元数据无法从 istio 中获取,需要走服务自省模式。这要求了 Dubbo MetadataService 的端口在全集群的是统一的。参考配置:dubbo.application.metadataServicePort=20885未来我们将基于 Dubbo Mesh 的控制面实现自动的服务元数据获取,届时将不需要独立配置参数即可将 Dubbo 运行在 Mesh 体系下,敬请期待。qosAcceptForeignIp由于 Kubernetes probe 探活机制的工作原理限制,探活请求的发起方不是 localhost,所以需要配置 qosAcceptForeignIp 参数开启允许全局访问。dubbo.application.qosAcceptForeignIp=true注:qos 端口存在危险命令,请先评估网络的安全性。即使 qos 不开放也仅影响 Kubernetes 无法获取 Dubbo 的生命周期状态。不需要开启注入Proxyless 模式下 pod 不需要再开启 envoy 注入,请确认 namespace 中没有 istio-injection=enabled 的标签。
Dubbo3 是 HSF 与社区 Dubbo2 融合后的产品,目前 Dubbo3 已开始在集团内逐步取代 HSF 成为新一代的服务开发框架,新的 FY22 财年,我们有望看到 Dubbo3 在核心电商等重要业务线全面落地。而就在今天,Dubbo 社区也刚刚发布了 3.0 的首个预览版本 - 3.0.0.preview,我们借着这次陆龟同学在云原生中间件上海 meetup 活动现场的分享,一起分析一下 Dubbo3 的一些设计理念与核心功能。 本文整理自陆龟在云原生中间件Meetup上海站上的分享 回复关键字“中间件”可以获取视频录播地址和PPT 就在今天,Dubbo 社区刚刚发布了 3.0 的首个预览版本 - 3.0.0.preview。 https://github.com/apache/dubbo/releases/tag/3.0.0.preview 本文将和读者一起解读 Dubbo3 的首个 preview 版本:一方面,我们将深入分析 Dubbo3 云原生变革的核心理念;另一方面,我们将逐个解读 preview 版本的核心功能。 做过微服务开发的开发者相信对 Dubbo 都不陌生,Dubbo 是一款能帮助我们快速解决微服务开发、通信以及流量治理的框架。相比于之前只限定在 Java 语言范围内,Dubbo 的多语言版本在这两年呈现了良好的发展势头,其中,Dubbo Go 语言版本在功能、稳定性各个方面都已非常完备,其它几种主流的多语言版本在社区也有提供。 云原生视角的微服务变革 Dubbo 主要解决微服务开发、运行域问题,它和微服务的编程、通信、流量治理等密切关联,因此,在探寻 Dubbo3 的云原生变革之前,我们先尝试从云原生视角观察云原生基础设施带给微服务架构和实践的变革,进而总结出 Dubbo 这样一个和微服务实践密切相关的框架所面临的变革与挑战。 微服务在让业务开发演进更灵活、快捷的同时,也带来了一些它独有的特征和需求:如微服务之后组件数量越来越多,如何解决各个组件的稳定性,如何快速的水平扩容等。 这些诉求,尤其是运维、交付相关诉求,如微服务组件的生命周期、网络、通用服务绑定、服务状态管理等,并不应该是开发人员关注的重点,因为它们已经完全脱离了业务逻辑,开发人员更愿意专注在有业务价值组件上,但这个层次的诉求却是实现微服务交付的关键能力。开发者期望由底层基础设施来提供此类能力支持,而处于不同阶段发展的基础设施却不一定具备这样的能力,因此,在微服务软件架构和底层基础设施之间就出现了一条鸿沟,我们需要有组件能填补这一鸿沟,让微服务组件能更好的接入底层基础设施。 这里从一个更抽象的层面,尝试用两条发展曲线演示了软件架构诉求与底层基础设施能力丰富度之间的关系。总体上,两者之间的发展关系可拆分为两个阶段。 在第一个阶段,软件架构(这条红色的线)从单体应用、到面向服务的软件架构、再到微服务架构,快速演讲,从而也提出了上文我们讲到的对基础设施对交付的诉求,这个时候基础设施层还多是以定制化的方式来满足软件架构的诉求,如过往的集中化的 ESB、各个不同的 PaaS 平台等; 第二个阶段,是从容器、Kubernetes 为代表的基础产品的出现开始,蓝线与红线之间的增长速度被快速拉近,以云原生技术为代表的底层基础设施丰富度得到了极大改善,它们不再只是被动的满足微服务开发的诉求,而是开始抽象更多的软件诉求到底层的基础设施,将它们下沉为基础能力,并开始以统一的、标准化的形式向上输出以满足微服务对交付、运维等的诉求,不仅如此,通过更深入的主动创新、持续的向上释放能力,底层基础设施还开始反过来影响微服务的开发、接入方式,如 sidecar、dapr 等模型。 Dubbo3 的云原生变革 通过上文云对原生基础设施演进给传统微服务带来变革的分析,我们得出,以 Dubbo 为代表的微服务开发框架,应重点在以下方向突破与变革。 更多的微服务组件及能力正下沉到以 Kubernetes 为代表的基础设施层。传统微服务开发框架应剔除一些冗余机制,积极的适配到基础设施层以做到能力复用;微服务框架生命周期、服务治理等能力应更好地与 Kubernetes 服务编排机制融合;以 Service Mesh 为代表微服务架构给微服务开发带来的新的选择,但由于 Mesh 架构本身的复杂性,其距离大规模生产落地还有一段距离。我们相信,基于 ServiceMesh 的体系会逐步从孵化器走向成熟期,会有越来越多的企业采用 Service Mesh技术,但在未来在很长一段时间内,基于服务框架的传统微服务体系还将是主流,长期仍将占据半壁江山。 我们不妨回想一下,在云原生基础设施能力被充分释放前,围绕 Dubbo 构建微服务时,它给微服务开发提供了哪些能力?或者我们期望它提供哪些能力? Dubbo2 提供了包括服务注册、服务发现、负载均衡、流量治理等相当丰富的能力,当然还包括微服务最需要的远程通信能力,这些能力很好的解决了微服务的诉求。 而在云原生架构之下,我们需要重新审势,Dubbo2 的哪些能力是冗余的,是需要接入基础设施的?哪些能力是已经不适合云原生时代的,需要被重构的? 首先,是接入云原生基础设施后,一些能力出现了重复,像服务定义、服务注册、甚至是服务治理能力在不同层面基础设施中出现了重复,需要 Dubbo3 作出适配与调整,以更好的解放业务开发效率,利用好这些基础能力。 其次,是 Dubbo 在微服务架构中的最基本的能力:RPC 远程通信。通信协议和数据传输格式的标准化应该算是 Dubbo2 面临的又一重要挑战,在云原生背影下,协议、数据定义、传输格式的标准化和穿透能力成为更需要优先考虑的指标,纵然私有协议具有更高效、灵活的潜力,但考虑到云原生下多语言、组件间互通、网关等代理设备友好性、避免厂商锁定等诉求,在 Dubbo3 中私有协议都应该被摒弃,转而拥抱基于 HTTP/2 的更通用的协议,采用跨语言的更通用的数据定义和传输格式。 最后,是关于服务治理能力,Dubbo 的服务治理能力应该充分结合底层基础设施的特点,比如之前绑定 ip 的流量过滤方案在地址不固定的 Kubernetes 平台就已不再适用,另外,流量治理也要充分的与调度平台的灰度发布、动态扩缩容能力整合起来;考虑到未来微服务可能会有多种不同的部署形态(下文会讲到),Dubbo3 应该制定一种能适用于各种部署形态的路由规则。 从云原生视角来说,Dubbo3 的核心能力基本上也就成围绕以上几点分析展开的,主要涉及:抽象全新的服务发现模型、定义下一代的更能用的 RPC 协议与数据传输格式、服务治理能力重构等。接下来,我们就看看 3.0 preview 中这些能力的具体实现。 Preview 版本功能速览 就在今天,Dubbo 3.0.0.preview 版本正式通过了 Apache 社区投票并完成了正式发布,作为 3.0 的首个发布版本,代表了社区 3.0 版本的全面启动,也展示了未来 3.0 的发展方向。当然,我们要意识到 preview 版本还远未达到生产可用,它只是为了让大家快速、方便了解 Dubbo3 的一个预览版本,离正式版本甚至 alpha版本还有一段时间要走,具体大家可关注文后的 Dubbo Roadmap。 以下 preview 版本发布的几个核心特性: 全新的服务发现模型下一代基于 HTTP/2 的 RPC 协议:Triple服务治理重构:全新路由规则性能提升 百万节点级水平扩容调用链路 QPS 性能提升 在 preview 以上能力中,特别值得注意的是得益于 Dubbo3 与 HSF 的融合,Dubbo3 的整体性能也有望得到大幅提升。 Preview 版本是从架构层面对 Dubbo 的一次全面升级,接下来,社区一方面会从功能完善度、稳定性等几个方面继续增强 3.0 版本,并将在 6 月份发布首个稳定版本,另一方面社区将兑现更多新的功能。首先,接下来即将交付的是 Kubernetes Service 集成,而 Proxyless Mesh、基于反压的智能流量调度等功能正在前期的调研或开发阶段。 下面,我们就选取以上三个核心功能,深入了解它们的实现机制。 全新服务发现模型 下图是 Dubbo2 的服务发现模型:Provider 注册服务地址,Consumer 经过注册中心协调并发现服务地址,进而对地址发起通信,这是被绝大多数微服务框架的经典服务发现流程。而 Dubbo2 的特殊之处在于,它把 “RPC 接口”的信息也融合在了地址发现过程中,而这部分信息往往是和具体的业务定义密切相关的。 而在接入云原生基础设施后,基础设施融入了微服务概念的抽象,容器化微服务被编排、调度的过程即 完成了在基础设施层面的注册。如下图所示,基础设施即承担了注册中心的职责,又完成了服务注册的动作,而 “RPC 接口”这部分信息,由于与具体的业务相关,不可能也不适合被基础设施托管。 在这样的场景下,对 Dubbo3 的服务注册发现机制提出了两个要求: Dubbo3 需要在原有服务发现流程中抽象出通用的、与业务逻辑无关的地址映射模型,并确保这部分模型足够合理,以支持将地址的注册行为和存储委托给下层基础设施Dubbo3 特有的业务接口同步机制,是 Dubbo3 需要保留的优势,需要在 1 中定义的新地址模型之上,通过框架内的自有机制予以解决。 这样设计的全新的服务发现模型,在架构兼容性、可伸缩性上都给 Dubbo3 带来了更大的优势。 在架构兼容性上,如上文所述,Dubbo3 复用下层基础设施的服务抽象能力成为了可能;另一方面,如 Spring Cloud 等业界其它微服务解决方案也沿用这种模型,在打通了地址发现之后,使得用户探索用 Dubbo 连接异构的微服务体系成为了一种可能。 Dubbo3 服务发现模型更适合构建可伸缩的服务体系,这点要如何理解?这里先举个简单的例子,来直观的对比 Dubbo2 与 Dubbo3 在地址发现流程上的数据流量变化:假设一个微服务应用定义了 100 个接口(Dubbo 中的服务),则需要往注册中心中注册 100 个服务,如果这个应用被部署在了 100 台机器上,那这 100 个服务总共会产生 100 * 100 = 10000 个虚拟节点;而同样的应用,对于 Dubbo3 来说,新的注册发现模型只需要 1 个服务(只和应用有关和接口无关), 只注册和机器实例数相等的 1 * 100 = 100 个虚拟节点到注册中心。在这个简单的示例中,Dubbo 所注册的地址数量下降到了原来的 1 / 100,对于注册中心、订阅方的存储压力都是一个极大的释放。更重要的是,地址发现容量彻底与业务 RPC 定义解藕开来,整个集群的容量评估对运维来说将变得更加透明:部署多少台机器就会有多大负载,不会像 Dubbo2 一样,因为业务 RPC 重构就会影响到整个集群服务发现的稳定性。 下一代通信协议:Triple 我们将 Dubbo3 的新协议命名为 Triple,有代表第 3 代协议的意思。在云原生背景下,Triple 协议需要解决两大问题: 通信协议和数据传输格式的标准化问题。这涉及到 RPC 协议、数据定义、数据传输等环节,未来可移植性、不被厂商锁定会成为每个上云企业用户的诉求,组件内的私有协议和特有数据格式,必然会成为很多用户选型的顾虑;穿透性、通用性问题。在 Mesh 等架构设想下,微服务甚至所有组件的通信都要经过 sidecar 代理转发,理论上,Sidecar 是要透明的转发流量的(收到什么就转发什么),起码 payload 不需要被 Sidecar 识别;在某些情况下,Sidecar 在某些时候也是需要感知流量内容的,因为它要基于某些请求元数据做流量的调度,这个时候,Triple 就要做到足够通用 -- 让所有的 Sidecar 都能在预期的地方取到其关注的元数据,而不用解析整个 payload。 除了以上两个核心问题,Triple 协议还需要被设计用来支持更多的业务语义。 协议应该提供更完善的请求模型,除了 Request/Response模型,还应该支持Streaming和Bidirectional。在性能上,新的协议应该保留 request Id 机制,以避免HOL带来的性能损耗。新协议应该易于扩展,包括但不限于 Tracing、Monitoring、BackPressure 等支持。 基于以上需求,Triple 协议是完全基于 HTTP/2 之上构建的,另外,Triple 协议做到了完全兼容 gRPC 协议;在服务定义、数据格式定义以及传输编码上,Triple 增加了对 Protobuf 的支持。 统一的路由规则 Dubbo3 会对服务治理规则进行全面的重构,以更好的适应云原生基础设施的变革。 当前的 3.0 preview 版本提供了一个重构后的路由规则机制原型,虽然当前版本的实现还需要继续增强,但从设计理念上,我们可以解读出:Dubbo3 期望提供一种跨平台、跨语言、跨多种部署架构的通用路由规则。 随着微服务对治理需求的挖掘,Dubbo2 路由规则除了在语义表达上不能涵盖所有场景之外,更为重要的是,其基于特定语言、特定主机 ip 的过滤机制,已不再适应底层调度平台的工作机制,Dubbo3 需要引入一种全新设计的路由规则。而对于“跨多种部署架构” 这个点,我们设想未来以 Dubbo 构建的微服务会有三种部署架构:传统 SDK、基于 Sidecar 的 Service Mesh以及脱离 Sidecar 的 Service Mesh,这三种部署形态都将由 Dubbo3 统一的路由规则进行治理。 基于 Sidecar 的 Service Mesh,经典的 Mesh 架构,独立的 sidecar 运行时接管所有的流量脱离 Sidecar 的 Service Mesh,变种 Mesh 架构,没有 sidecar 运行时,富 SDK 直接通过 xDS 与控制面通信,我们将在后续发布关于 Proxyless 模式更详细的解读。 实践中的 Dubbo3 云原生微服务变革在各大厂商内部早已展开,相比于当前开源社区的 preview 版本,Dubbo3 在阿里巴巴的开发与实践已经在逐步铺开:部分功能已经在阿里巴巴的部分业务线上得到了规模化验证(考拉),并且更多的功能和业务线也将进入试点、推广阶段(饿了么、钉钉等)。有一点是值得特别提及的是:在接下来阿里巴巴的微服务架构升级战略中,Dubbo3 将成为阿里巴巴经济体未来唯一的标准服务框架,它将逐步在所有业务线取代 HSF 和 Dubbo2,并且我们期待在接下来的 1-2 年 Dubbo3 内能覆盖大多数重要的业务线。 说在这里,有必要提一下阿里巴巴的微服务框架演进历程。大概在 2011 年,阿里巴巴开源了 Dubbo2 这一款服务框架并获得极大成功,在 Dubbo2 开源不久,在阿里巴巴内部又发展出了一款全新的服务框架 -- HSF,两者在设计理念上是高度相似的,而经过这么些年的发展,HSF 得以跟随阿里巴巴的业务系统更快速的成长,由其是在大集群、大流量治理下展现出了更好的性能和稳定性。在阿里云原生微服务战略下,整合各个优秀的框架并发展统一品牌的 Dubbo3 被纳入发展规划,在此背景下,Dubbo3 实现了Dubbo2 与 HSF 的融合,并将推动实现内、外技术栈的统一。 除了阿里之外,工商银行等标杆用户也已启动了对 Dubbo3 项目的试点。从社区角度来说,preview 预览版本的发布只是开始,未来随着阿里巴巴、工商银行等更多标杆用户的全面落地,将推动项目更快、更高质量的发展,助力项目保持持续的创新能力和社区生命力。 未来规划(Roadmap) 以下是 Dubbo3 的 Roadmap,截止此文发稿,社区已经完成了 3.0 preview 版本的发布 在 6 月份,我们期望能迎来 Dubbo3 的首个社区正式版本。 随后,一直到下半年的 11 月份,我们将重点投入在对 Kubernetes、ServiceMesh 架构的支持上,中间当然也包括了对服务治理规则的全面重构。 在此之后,我们将开始在服务柔性上的尝试,以期提供一种能更高效的利用资源且能提高系统稳定性的流量高度机制。 总结 以云原生架构为代表的基础设施丰富度不断提升并向上层持续释放,给以 Dubbo 为代表的微服务开发框架提出了新的诉求与挑战,Dubbo3 Preview 从服务发现、服务治理、下一代协议等多个核心能力上给出了变革方案,也进一步确立了未来 Dubbo3 在 Kubernetes、Mesh 为主导的新兴架构下的发展方向,随着 Dubbo3 在阿里巴巴及更多社区标杆用户的大规模落地,Dubbo3 衣将迎来更稳定、更高质量的发展。 本文开篇关于云原生微服务变革部分思想引自 张磊 同学《Microservices - A Cloud Native View》一文分享。
本文整理自刘军在 Dubbo 成都 meetup 上分享的《Dubbo 在多语言和协议穿透性方向上的探索》。 本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点;第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf,以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同;第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。 基本介绍 Dubbo 协议 从协议层面展开,以下是当前 2.7 版本支持的 Dubbo 协议 众所周知,Dubbo 协议是直接定义在 TCP 传输层协议之上,由于 TCP 高可靠全双工的特点,为 Dubbo 协议的定义提供了最大的灵活性,但同时也正是因为这样的灵活性,RPC 协议普遍都是定制化的私有协议,Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方,关于协议详细解析请参见官网博客 Dubbo 协议体 Body 中有一个可扩展的 attachments 部分,这给 RPC 方法之外额外传递附加属性提供了可能,是一个很好的设计。但是类似的 Header 部分,却缺少类似的可扩展 attachments,这点可参考 HTTP 定义的 Ascii Header 设计,将 Body Attachments 和 Header Attachments 做职责划分。 Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具体的序列化协议解耦,以更好的被网络基础设施识别或用于流量管控。 扩展性不够好,欠缺协议升级方面的设计,如 Header 头中没有预留的状态标识位,或者像 HTTP 有专为协议升级或协商设计的特殊 packet。 在 Java 版本的代码实现上,不够精简和通用。如在链路传输中,存在一些语言绑定的内容;消息体中存在冗余内容,如 Service Name 在 Body 和 Attachments 中都存在。 HTTP/1 相比于直接构建与 TPC 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。 之所有选择构建在 HTTP 之上,我认为有两个最大的优势: HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。 通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。 具体来说,HTTP/1 的优势和限制是: 典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求 HTTP/1 支持 Keep-Alive 链接,避免了链接重复创建开销 Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式 无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式 HTTP/2 HTTP/2 保留了 HTTP/1 的所有语义,在保持兼容的同时,在通信模型和传输效率上做了很大的改进。 支持单条链路上的 Multiplexing,相比于 Request - Response 独占链路,基于 Frame 实现更高效利用链路 Request - Stream 语义,原生支持 Server Push 和 Stream 数据传输 Flow Control,单条 Stream 粒度的和整个链路粒度的流量控制 头部压缩 HPACK Binary Frame 原生 TLS 支持 gRPC 上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点,相比于 Dubbo 构建于 TPC 传输层之上,Google 选择将 gRPC 直接定义在 HTTP/2 协议之上,关于 gRPC 的 基本介绍和 设计愿景 请参考以上两篇文章,我这里仅摘取 设计愿景 中几个能反映 gRPC 设计目的特性来做简单说明。 Coverage & Simplicity,协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源首先的如 IoT、Mobile 等设备。 Interoperability & Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持。 General Purpose & Performant,要在场景和性能间做好平衡,首先协议本身要是适用于各种场景的,同时也要尽量有高的性能。 Payload Agnostic,协议上传输的负载要保持语言和平台中立。 Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。 Flow Control,协议自身具备流量感知和限制的能力。 Metadata Exchange,在 RPC 服务定义之外,提供额外附加数据传输的能力。 总的来说,在这样的设计理念指导下,gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。 Protobuf Protocol buffers (Protobuf) 是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品,它定义了一套结构化数据定义的协议,同时也提供了相应的 Compiler 工具,用来将语言中立的描述转化为相应语言的具体描述。 它的一些特性包括: 跨语言 跨平台,语言中立的数据描述格式,默认提供了生成多种语言的 Compiler 工具。 安全性,由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的,因此绕过了类似 Java Deserialization Vulnarability 的问题。 二进制 高性能 强类型 字段变更向后兼容 message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } 除了结构化数据描述之外,Protobuf 还支持定义 RPC 服务,它允许我们定义一个 .proto 的服务描述文件,进而利用 Protobuf Compiler 工具生成特定语言和 RPC 框架的接口和 stub。后续将要具体讲到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通过定制 Compiler 类实现的。 service SearchService { rpc Search (SearchRequest) returns (SearchResponse); } Dubbo 所做的支持 跨语言的服务开发涉及到多个方面,从服务定义、RPC 协议到序列化协议都要做到语言中立,同时还针对每种语言有对应的 SDK 实现。虽然得益于社区的贡献,现在 Dubbo 在多语言 SDK 实现上逐步有了起色,已经提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客户端或全量实现版本,但在以上提到的跨语言友好型方面,以上三点还是有很多可改进之处。 协议,上面我们已经分析过 Dubbo 协议既有的缺点,如果能在 HTTP/2 之上构建应用层协议,则无疑能避免这些弊端,同时最大可能的提高协议的穿透性,避免网关等协议转换组件的存在,更有利于链路上的流量管控。考虑到 gRPC 是构建在 HTTP/2 之上,并且已经是云原生领域推荐的通信协议,Dubbo 在第一阶段选择了直接支持 gRPC 协议作为当前的 HTTP/2 解决方案。我们也知道 gRPC 框架自身的弊端在于易用性不足以及服务治理能力欠缺(这也是目前绝大多数厂商不会直接裸用 gRPC 框架的原因),通过将其集成进 Dubbo 框架,用户可以方便的使用 Dubbo 编程模型 + Dubbo 服务治理 + gRPC 协议通信的组合。 服务定义,当前 Dubbo 的服务定义和具体的编程语言绑定,没有提供一种语言中立的服务描述格式,比如 Java 就是定义 Interface 接口,到了其他语言又得重新以另外的格式定义一遍。因此 Dubbo 通过支持 Protobuf 实现了语言中立的服务定义。 序列化,Dubbo 当前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而这其中支持跨语言的只有 Json、Hessian2,通用的 Json 有固有的性能问题,而 Hessian2 无论在效率还是多语言 SDK 方面都有所欠缺。为此,Dubbo 通过支持 Protobuf 序列化来提供更高效、易用的跨语言序列化方案。 示例 示例 1,使用 Dubbo 开发 gRPC 服务 gRPC 是 Google 开源的构建在 HTTP/2 之上的一个 PRC 通信协议。Dubbo 依赖其灵活的协议扩展机制,增加了对 gRPC (HTTP/2) 协议的支持。 目前的支持限定在 Dubbo Java 语言版本,后续 Go 语言或其他语言版本将会以类似方式提供支持。下面,通过一个简单的示例来演示如何在 Dubbo 中使用 gRPC 协议通信。 1. 定义服务 IDL 首先,通过标准的 Protobuf 协议定义服务如下: syntax = "proto3"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; option objc_class_prefix = "HLW"; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } 在此,我们定义了一个只有一个方法 sayHello 的 Greeter 服务,同时定义了方法的入参和出参, 2. Protobuf Compiler 生成 Stub 定义 Maven Protobuf Compiler 插件工具。这里我们扩展了 Protobuf 的 Compiler 工具,以用来生成 Dubbo 特有的 RPC stub,此当前以 Maven 插件的形式发布。 <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} </protocArtifact> <pluginId>dubbo-grpc-java</pluginId> <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact> <outputDirectory>build/generated/source/proto/main/java</outputDirectory> <clearOutputDirectory>false</clearOutputDirectory> <pluginParameter>grpc</pluginParameter> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> 其中, pluginArtifact 指定了 Dubbo 定制版本的 Java Protobuf Compiler 插件,通过这个插件来在编译过程中生成 Dubbo 定制版本的 gRPC stub。 <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact> 由于 protoc-gen-dubbo-java 支持 gRPC 和 Dubbo 两种协议,可生成的 stub 类型,默认值是 gRPC,关于 dubbo 协议的使用可参见 使用 Protobuf 开发 Dubbo 服务。 <pluginParameter>grpc</pluginParameter> 2. 生成 Java Bean 和 Dubbo-gRPC stub # 运行以下 maven 命令 $ mvn clean compile 生成的 Stub 和消息类 如下: 重点关注 GreeterGrpc ,包含了所有 gRPC 标准的 stub 类/方法,同时增加了 Dubbo 特定的接口,之后 Provider 端的服务暴露和 Consumer 端的服务调用都将依赖这个接口。 /** * Code generated for Dubbo */ public interface IGreeter { default public io.grpc.examples.helloworld.HelloReply sayHello(io.grpc.examples.helloworld.HelloRequest request) { throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows."); } default public com.google.common.util.concurrent.ListenableFuture<io.grpc.examples.helloworld.HelloReply> sayHelloAsync( io.grpc.examples.helloworld.HelloRequest request) { throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows."); } public void sayHello(io.grpc.examples.helloworld.HelloRequest request, io.grpc.stub.StreamObserver<io.grpc.examples.helloworld.HelloReply> responseObserver); } 3. 业务逻辑开发 继承 GreeterGrpc.GreeterImplBase (来自第 2 步),编写业务逻辑,这点和原生 gRPC 是一致的。 package org.apache.dubbo.samples.basic.impl; import io.grpc.examples.helloworld.GreeterGrpc; import io.grpc.examples.helloworld.HelloReply; import io.grpc.examples.helloworld.HelloRequest; import io.grpc.stub.StreamObserver; public class GrpcGreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) { System.out.println("Received request from client."); System.out.println("Executing thread is " + Thread.currentThread().getName()); HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + request.getName()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } } Provider 端暴露 Dubbo 服务 以 Spring XML 为例 <dubbo:application name="demo-provider"/> <!-- 指定服务暴露协议为 gRPC --> <dubbo:protocol id="grpc" name="grpc"/> <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/> <bean id="greeter" class="org.apache.dubbo.samples.basic.impl.GrpcGreeterImpl"/> <!-- 指定 protoc-gen-dubbo-java 生成的接口 --> <dubbo:service interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" ref="greeter" protocol="grpc"/> public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml"); context.start(); System.out.println("dubbo service started"); new CountDownLatch(1).await(); } 引用 Dubbo 服务 <dubbo:application name="demo-consumer"/> <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/> <!-- 指定 protoc-gen-dubbo-java 生成的接口 --> <dubbo:reference id="greeter" interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" protocol="grpc"/> public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml"); context.start(); GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter"); HelloReply reply = greeter.sayHello(HelloRequest.newBuilder().setName("world!").build()); System.out.println("Result: " + reply.getMessage()); System.in.read(); } 示例1附:高级用法 一、异步调用 再来看一遍 protoc-gen-dubbo-java 生成的接口: /** * Code generated for Dubbo */ public interface IGreeter { default public HelloReply sayHello(HelloRequest request) { // ...... } default public ListenableFuture<HelloReply> sayHelloAsync(HelloRequest request) { // ...... } public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver); } 这里为 sayHello 方法生成了三种类型的重载方法,分别用于同步调用、异步调用和流式调用,如果消费端要进行异步调用,直接调用 sayHelloAsync() 即可: public static void main(String[] args) throws IOException { // ... GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter"); ListenableFuture<HelloReply> future = greeter.sayHAsyncello(HelloRequest.newBuilder().setName("world!").build()); // ... } 二、高级配置 由于当前实现方式是直接集成了 gRPC-java SDK,因此很多配置还没有和 Dubbo 侧对齐,或者还没有以 Dubbo 的配置形式开放,因此,为了提供最大的灵活性,我们直接把 gRPC-java 的配置接口暴露了出来。 绝大多数场景下,你可能并不会用到以下扩展,因为它们更多的是对 gRPC 协议的拦截或者 HTTP/2 层面的配置。同时使用这些扩展点可能需要对 HTTP/2 或 gRPC 有基本的了解。 扩展点 目前支持的扩展点如下: org.apache.dubbo.rpc.protocol.grpc.interceptors.ClientInterceptor org.apache.dubbo.rpc.protocol.grpc.interceptors.GrpcConfigurator org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerInterceptor org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerTransportFilter GrpcConfigurator 是最通用的扩展点,我们以此为例来说明一下,其基本定义如下: public interface GrpcConfigurator { // 用来定制 gRPC NettyServerBuilder default NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) { return builder; } // 用来定制 gRPC NettyChannelBuilder default NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url) { return builder; } // 用来定制 gRPC CallOptions, 定义某个服务在每次请求间传递数据 default CallOptions configureCallOptions(CallOptions options, URL url) { return options; } } 以下是一个示例扩展实现: public class MyGrpcConfigurator implements GrpcConfigurator { private final ExecutorService executor = Executors .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true)); @Override public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) { return builder.executor(executor); } @Override public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url) { return builder.flowControlWindow(10); } @Override public CallOptions configureCallOptions(CallOptions options, URL url) { return options.withOption(CallOptions.Key.create("key"), "value"); } } 配置为 Dubbo SPI,`resources/META-INF/services 增加配置文件 default=org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator 指定 Provider 端线程池 默认用的是 Dubbo 的线程池,有 fixed (默认)、cached、direct 等类型。以下演示了切换为业务自定义线程池。 private final ExecutorService executor = Executors .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true)); public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) { return builder.executor(executor); } 指定 Consumer 端限流值 设置 Consumer 限流值为 10 @Override public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url) { return builder.flowControlWindow(10); } 传递附加参数 DemoService 服务调用传递 key @Override public CallOptions configureCallOptions(CallOptions options, URL url) { if (url.getServiceInterface().equals("xxx.DemoService")) { return options.withOption(CallOptions.Key.create("key"), "value"); } else { return options; } } 三、双向流式通信代码中还提供了一个支持双向流式通信的示例,同时提供了拦截流式调用的 Interceptor 扩展示例实现。 * MyClientStreamInterceptor,工作在 client 端,拦截发出的请求流和接收的响应流 * MyServerStreamInterceptor,工作在 server 端,拦截收到的请求流和发出的响应流 四、TLS 配置 配置方式和 Dubbo 提供的通用的 TLS 支持一致,具体请参见 Dubbo 官方文档或示例 demo 五、使用 Reactive Stream 风格 RPC 编程当前 Dubbo 支持 Reactor、RxJava 两个 Reactive Stream library,具体使用请参见Reactor 示例、RxJava 示例 示例 2, 使用 Protobuf 开发 Dubbo 服务 下面,我们以一个具体的示例来看一下基于 Protobuf 的 Dubbo 服务开发流程。 1. 定义服务 通过标准 Protobuf 定义服务 syntax = "proto3"; option java_multiple_files = true; option java_package = "org.apache.dubbo.demo"; option java_outer_classname = "DemoServiceProto"; option objc_class_prefix = "DEMOSRV"; package demoservice; // The demo service definition. service DemoService { rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } 这里定义了一个 DemoService 服务,服务只包含一个 sayHello 方法,同时定义了方法的入参和出参。 2. Compiler 编译服务 引入 Protobuf Compiler Maven 插件,同时指定 protoc-gen-dubbo-java RPC 扩展 <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} </protocArtifact> <pluginId>dubbo-grpc-java</pluginId> <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact> <outputDirectory>build/generated/source/proto/main/java</outputDirectory> <clearOutputDirectory>false</clearOutputDirectory> <pluginParameter>dubbo</pluginParameter> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> 注意,这里与 Dubbo 对 gRPC 支持部分的区别在于: dubbo 生成 RPC stub # 运行以下 maven 命令 $mvn clean compile 生成的 Java 类如下: DemoServiceDubbo 为 Dubbo 定制的 stub public final class DemoServiceDubbo { private static final AtomicBoolean registered = new AtomicBoolean(); private static Class<?> init() { Class<?> clazz = null; try { clazz = Class.forName(DemoServiceDubbo.class.getName()); if (registered.compareAndSet(false, true)) { org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( org.apache.dubbo.demo.HelloRequest.getDefaultInstance()); org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( org.apache.dubbo.demo.HelloReply.getDefaultInstance()); } } catch (ClassNotFoundException e) { // ignore } return clazz; } private DemoServiceDubbo() {} public static final String SERVICE_NAME = "demoservice.DemoService"; /** * Code generated for Dubbo */ public interface IDemoService { static Class<?> clazz = init(); org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request); java.util.concurrent.CompletableFuture<org.apache.dubbo.demo.HelloReply> sayHelloAsync( org.apache.dubbo.demo.HelloRequest request); } } 最值得注意的是 IDemoService 接口,它会作为 Dubbo 服务定义基础接口。 3. 开发业务逻辑 从这一步开始,所有开发流程就和直接定义 Java 接口一样了。实现接口定义业务逻辑。 public class DemoServiceImpl implements DemoServiceDubbo.IDemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public HelloReply sayHello(HelloRequest request) { logger.info("Hello " + request.getName() + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); return HelloReply.newBuilder() .setMessage("Hello " + request.getName() + ", response from provider: " + RpcContext.getContext().getLocalAddress()) .build(); } @Override public CompletableFuture<HelloReply> sayHelloAsync(HelloRequest request) { return CompletableFuture.completedFuture(sayHello(request)); } } 4. 配置 Provider 暴露 Dubbo 服务 <dubbo:application name="demo-provider"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo"/> <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/> <dubbo:service interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService" ref="demoService"/> public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml"); context.start(); System.in.read(); } 5. 配置 Consumer 引用 Dubbo 服务 <dubbo:application name="demo-consumer"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService"/> public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml"); context.start(); IDemoService demoService = context.getBean("demoService", IDemoService.class); HelloRequest request = HelloRequest.newBuilder().setName("Hello").build(); HelloReply reply = demoService.sayHello(request); System.out.println("result: " + reply.getMessage()); System.in.read(); } 总结与展望 HTTP/2 相比于 Dubbo/HSF 直接构建与 TCP 之上,无疑具有更好的穿透性和通用性,得益于网络基础设施的支持,HTTP 能方便的做到使用同一协议打通 Mobile、网关、Mesh、后端服务的整个链路。另外,HTTP/2 引入的 Stream、Flow-Control 等语义,也为 RPC 框架层实现 Reactive Stream、Back Pressure 提供了天然的传输层支持。当前 Dubbo 对 gRPC 的支持也是对未来 Dubbo/HSF 内置 HTTP/2 协议的一个探索。 RPC 协议是实现微服务体系互通的核心组件,通常采用不同的微服务通信框架则意味着绑定某一个特定的协议,如 Spring Cloud 基于 HTTP、gRPC 提供 gRPC over HTTP/2、Thrift Hessian 等都是自定义私有协议。 Dubbo 自身同样提供了私有的 Dubbo 协议,这样你也能基于 Dubbo 协议构建微服务。但除了单一协议之外,和以上所有框架不同的,Dubbo 最大的优势在于它能同时支持多协议的暴露和消费,再配合 Dubbo 多注册订阅的模型,可以让 Dubbo 成为桥接多种不同协议的微服务体系的开发框架,轻松的实现不同微服务体系的互调互通或技术栈迁移。 这篇文章详细讲解了 Dubbo 对 gRPC 协议的支持,再加上 Dubbo 之前已具备的对 REST、Hessian、Thrift 等的支持,使 Dubbo 在协议互调上具备了基础。我们只需要在服务发现模型上也能实现和这些不同体系的打通,就能解决不同技术栈互调和迁移的问题。关于这部分的具体应用场景以及工作模式,我们将在接下来的文章中来具体分析。