大纲
前言
本文将整合 SpringBoot 与 Dubbo 3,并结合 IDL + Protobuf 使用 Triple 协议,使用的注册中心是 Nacos。值得一提的是,本文实现的 RPC 服务可以与谷歌官方原生的 gRPC 客户端(协议)互相调用,适合开发多编程语言业务。
特别注意
- 由于 gRPC 仅支持 Protobuf 模式(使用
.proto 文件定义服务),因此基于 IDL + Protobuf + Triple 协议实现的 RPC 服务可以与谷歌官方原生的 gRPC 客户端(协议)互相调用,适合开发多编程语言业务。 - 基于 IDL + Protobuf + Triple 协议实现的 RPC 服务,默认不可以通过标准的 HTTP 工具(比如
curl)直接调用;若需要通过标准的 HTTP 工具直接调用 RPC 服务,可以改为 配合 Java 接口使用 Triple 协议。
学习资源
版本对应关系
SpringBoot 与 Dubbo 的版本必须互相匹配,否则不同版本之间可能会存在兼容性问题,最终导致服务无法正常运行。两者的版本对应关系如下:
| Dubbo 分支 | 最新版本 | JDK | SpringBoot | 详细说明 |
|---|
| 3.3.x | 3.3.0 | 8, 17, 21 | 2.x、3.x | 生产可用(推荐,长期维护)! 最新 Triple 协议升级,内置 Metrics、Tracing、GraalVM 支持等 |
| 3.2.x | 3.2.10 | 8, 17 | 2.x、3.x | 生产可用(长期维护)! |
| 3.1.x | 3.1.11 | 8, 17 | 2.x、3.x | 仅修复安全漏洞! |
| 3.0.x | 3.0.15 | 8 | 2.x | 停止维护! |
| 2.7.x | 2.7.23 | 8 | 2.x | 停止维护! |
| 2.6.x | 2.6.20 | 6, 7 | - | 停止维护! |
| 2.5.x | 2.5.10 | 6, 7 | - | 停止维护! |
如果仍然使用版本低于 2.7.0 的旧版 Dubbo,请使用以下 Spring Boot 启动器:
| Dubbo Spring Boot Starter | Dubbo | Spring Boot |
|---|
| 0.2.1.RELEASE | 2.6.5+ | 2.x |
| 0.1.2.RELEASE | 2.6.5+ | 1.x |
Triple 协议介绍
提示
从 Dubbo 3.2 开始,Dubbo 官方已经废弃原有的 gRPC 协议,使用 Triple 协议进行替代。Triple 协议完全兼容 gRPC 协议,更详细的协议介绍请看 这里。
Triple 协议的核心概念
- Triple 协议是 Dubbo 3 引入的新一代 RPC 通信协议,旨在替代经典 Dubbo 协议(基于 TCP 长连接),支持更加现代化、开放的服务互通标准。
- Triple 协议本质上是基于 gRPC/HTTP/2 规范的 RPC 协议,但是它做了兼容性扩展,支持 Dubbo 的接口声明风格,保留了 Dubbo 的生态优势。
- Triple 协议是一个基于 HTTP 传输层协议的 RPC 协议,它完全兼容 gRPC,可运行在 HTTP/1、HTTP/2 之上,并同时结合了 HTTP/2、Protobuf 和 Dubbo 的能力。
Triple 协议的诞生背景
为什么 Dubbo 3 推出 Triple 协议呢?主要原因有以下几个:
- 经典 Dubbo 协议是私有的 TCP 协议,无法与多语言、云原生环境很好兼容。
- 微服务逐步走向云原生 + 多语言 + Service Mesh + 网关接入。
- gRPC 成为业界事实标准,Dubbo 社区需要更开放的通信协议。
Triple 协议的核心特点
| 特点 | 说明 |
|---|
| 基于 HTTP/2 | 多路复用、长连接、低延迟 |
| 多语言互操作性 | 兼容 gRPC,支持跨语言调用 |
| IDL 支持灵活 | 可选 Protobuf 或 Java 接口定义(兼容 Dubbo 2 的 API 方式) |
| 支持流式调用 | 实现了 gRPC 的流式通信能力 |
| 安全性更好 | 支持 HTTP/2 的 TLS/SSL |
| 更好的网关兼容性 | 能直接被 Envoy、APISIX 等 API 网关代理 |
| 保留 Dubbo 的服务治理 | 如注册中心、路由、限流、熔断等 |
| 支持零侵入升级 | Dubbo 2 可以平滑过渡到 Triple |
Triple 协议的应用场景
- 多语言 RPC 通信(Java ⇄ Go ⇄ Node)
- 云原生环境(与 Envoy Mesh 兼容)
- 高并发长连接应用(HTTP/2 多路复用)
- 数据流实时传输(流式调用)
- Dubbo 2 平滑升级到 Dubbo 3
Triple 协议发布 REST 服务
Dubbo 3 为 Triple 协议发布 REST 风格的服务提供了内置支持,具体使用方式取决于开发者使用的是基于 Protobuf 的服务定义模式,还是基于 Java 接口的服务定义模式:
- (1)
Java 接口的服务定义模式:通过直接为 Java 接口增加注解,可以发布 REST 风格的服务,目前支持 Spring Web 注解与 JAX-RS 注解。 - (2)
Protobuf 的服务定义模式:通过配合 Protobuf 使用 Triple 协议,使用 IDL 定义服务,并使用 Protobuf 编码业务数据,最后可以选择使用 gRPC-Gateway 发布 REST 风格的服务。
值得一提的是,Protobuf 与 gRPC-Gateway 的简单介绍如下:
- (1)
Protobuf(Protocol Buffers):Google 开发的一种轻量级、高效的数据序列化协议,常用于跨语言、跨平台的数据交换。它类似于 JSON 或 XML,但具有更高的性能和更小的数据体积,适用于网络通信、配置文件、数据存储等场景。 - (2)
gRPC-Gateway:一个基于 Go 语言的开源协议转换工具,它能够将 RESTful 请求(基于 HTTP/JSON)转换为 gRPC 调用(基于 HTTP/2 + Protobuf)。这样,客户端就可以使用普通的 HTTP 请求(如浏览器)来访问 gRPC 服务,而无需专门使用 gRPC 客户端。
提示
若希望 Dubbo 3 使用 Triple 协议 + Java 接口(基于 Spring Web 注解)的服务定义模式来发布 REST 风格的服务,可以看 这里 的教程。
Triple 协议支持的调用模型
| RPC 模式 | 说明 |
|---|
| Unary RPC | 单次请求 - 响应 |
| Server Streaming | 服务端流式返回 |
| Client Streaming | 客户端流式请求 |
| Bi-directional Streaming | 双向流式 |
Triple 与经典 Dubbo 协议的区别
| 特性 | Triple(Dubbo 3) | Dubbo 协议(Dubbo 2) |
|---|
| 传输协议 | HTTP/2 | 自定义 TCP 协议(单连接) |
| 序列化协议 | Protobuf / Hessian2 / JSON | Hessian2(默认) |
| 多语言支持 | 是,天然支持 gRPC 互通 | 较差(主要 Java) |
| 网关支持 | 支持 Envoy、APISIX、Kong | 不兼容 |
| 流式通信 | 支持(Streaming RPC) | 不支持 |
| 扩展性 | 兼容 gRPC 插件生态 | 自定义扩展(Dubbo Filter) |
| 可观测性 | 标准化 Trace、Metrics | 自行扩展 |
| 连接复用 | HTTP/2 多路复用 | 单连接单请求 |
| 协议标准化程度 | 标准协议,开放生态 | 私有协议 |
Triple 协议原理
Triple 协议的序列化机制
Triple 协议的本质:
- 传输层:HTTP/2
- 编解码:
- 默认支持
application/grpc+proto,完全兼容 gRPC 标准 - 兼容 HTTP/JSON 调用(但严格来说,gRPC 标准并没有支持
application/grpc+json,而是 Dubbo 自行扩展了 JSON 兼容功能)
Triple 接口类型与序列化逻辑:
- 如果是
.proto 生成的接口- 完全走 Protobuf 序列化
- 客户端和服务端都用
.proto 定义的结构进行 Protobuf 编解码 - 兼容标准 gRPC 协议
- 如果是 Java 接口(非
.proto 生成)- HTTP/JSON 调用(如
curl + application/json):- 完全走 Hessian2Json 或 Jackson 等 JSON 序列化
- 传输层是 HTTP/1.1
- 不会经过 Protobuf 序列化
Triple 协议为什么这样设计:
- Triple 协议本质上是 gRPC 兼容协议
- gRPC 强制要求底层用 Protobuf 编解码
- Dubbo 为兼容老用户,支持「Java 接口直发」
- 如果是 HTTP/JSON 调用,仍保持兼容 JSON(非 Protobuf 序列化)
Triple 协议的序列化机制总结
| 调用方式 | 序列化 | 服务定义模式 |
|---|
gRPC 标准调用(HTTP/2 + application/grpc+proto) | 直接走 Protobuf 序列化 | 基于 .proto 文件定义服务 |
HTTP/JSON 调用(HTTP/1 + application/json) | 直接走 Hessian2Json 或 Jackson 序列化(不经过 Protobuf 序列化) | 基于 Java 接口(纯 POJO 接口)定义服务 |
Triple 协议的两种调用方式
| 服务定义方式 | 是否支持 HTTP + JSON 调用 | 是否支持 gRPC 客户端(协议)调用 | 是否支持跨编程语言调用 |
|---|
基于 .proto 文件的服务定义 | ❌ 不支持 HTTP + JSON 调用 | ✅ 支持 gRPC 客户端(协议)调用,由于必须用 Protobuf 二进制,通常用 gRPC 调用 | ✅ 支持跨编程语言调用 |
| 基于 Java 接口的服务定义(纯 POJO 接口) | ✅ 支持 HTTP + JSON 调用,内部直接走 Hessian2Json 或 Jackson 序列化(不经过 Protobuf 序列化) | ❌ 不支持 gRPC 客户端(协议)调用,由于 gRPC 仅支持 Protobuf 模式(使用 .proto 文件定义服务),因此 Java 接口 + Triple 协议的模式无法与谷歌官方原生的 gRPC 客户端(协议)互相调用 | ❌ 不支持跨编程语言调用 |
Dubbo 使用案例
本节将整合 SpringBoot 与 Dubbo 3,并配合 IDL + Protobuf 使用 Triple 协议,使用的注册中心是 Nacos,完整的项目目录结构如图所示。值得一提的是,本教程的内容也适用于 Spring Cloud 项目。
版本说明
| 组件 | 版本 | 说明 |
|---|
| SpringBoot | 3.4.2 | |
| Dubbo Spring Boot Starter | 3.3.2 | 依赖 Dubbo 3.3.2 |
| Nacos Server | 2.5.0 | Nacos 服务器,作为服务注册中心 |
| JDK | 17 | 支持 JDK 17 及以上版本 |
模块说明
api:抽取出来的公共模块,存放 .proto 文件、公用的实体类和接口provider:服务提供者,实现 api 模块中的接口customer:服务消费者,调用服务提供者中的接口
案例代码
API 模块
提示
- 这里不需要在本地操作系统手动安装 Protobuf,因为只要使用
protobuf-maven-plugin 插件,并正确配置了 protocArtifact,Maven 就会自动下载 protoc 编译器,不再需要手动安装。 - 通常只有以下场景才需要在本地安装 Protobuf,比如手工使用
protoc 命令编译 .proto 文件、跨语言开发(如生成 C++、Python、Go 代码)、Maven 插件支持不足等。
Proto 协议文件
这里使用 IDL 定义跨语言服务,而不是直接使用 Java 接口,其中 .proto 文件(比如 UserService.proto)的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| // 指定 proto 文件的语法版本 syntax = "proto3";
// 是否将每个 message / service / enum 生成到单独的 Java 文件中 option java_multiple_files = true;
// 指定生成的 Java 包名 option java_package = "com.clay.dubbo.api";
// 如果没有开启 java_multiple_files,所有代码会生成在 UserServiceProto.java 文件中 option java_outer_classname = "UserServiceProto";
// iOS Objective-C 的类名前缀(可选,只有 iOS 需要,防止类名冲突) option objc_class_prefix = "USERSRV";
// Proto 的包名(逻辑命名空间,不影响 Java 包) package userservice;
// 请求消息体 message HelloRequest { string name = 1; }
// 响应消息体 message HelloReply { string message = 1; }
// 定义 RPC 服务接口 service UserService { // 定义一个 RPC 方法 SayHello rpc SayHello (HelloRequest) returns (HelloReply) {} }
|
特别注意
为了共用 protoc 根据 .proto 文件自动生成的代码,这里将 .proto 文件存放在 api 模块中(比如存放在 /src/main/proto/ 目录),然后其他业务模块可以直接依赖 api 模块,以此实现重用代码的目的。
Maven 配置文件
配置文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <dubbo.version>3.3.2</dubbo.version> <protoc.version>3.25.3</protoc.version> </properties>
<dependencies> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protoc.version}</version> </dependency> </dependencies>
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.7.0</version> </extension> </extensions>
<plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <clearOutputDirectory>false</clearOutputDirectory> <protoSourceRoot>${basedir}/src/main/proto</protoSourceRoot> <outputDirectory>${basedir}/build/generated/source/proto/main/java</outputDirectory> <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact> <protocPlugins> <protocPlugin> <id>dubbo</id> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-compiler</artifactId> <version>${dubbo.version}</version> <mainClass>org.apache.dubbo.gen.tri.Dubbo3TripleGenerator</mainClass> </protocPlugin> </protocPlugins> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>${basedir}/build/generated/source/proto/main/java</source> </sources> </configuration> </execution> </executions> </plugin> </plugins> </build>
|
配置文件说明
1
| <protoSourceRoot>${basedir}/src/main/proto</protoSourceRoot>
|
- 自定义
.proto 文件所在的路径,默认可以不指定,因为 Protobuf 的 Maven 插件会自动扫描项目中的 .proto 文件。
1
| <outputDirectory>${basedir}/build/generated/source/proto/main/java</outputDirectory>
|
- 自定义 protoc 生成 Java 文件的存放目录,默认可以不指定,因为默认会存放在当前模块的
/target/generated-sources/protobuf/ 目录中。
1
| <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
|
- 这是用来根据
.proto 文件生成 Protobuf 的 Java 类(DTO、Message)代码的可执行文件,其中 exe 只是 Maven 的打包类型(classifier),并不代表文件扩展名是 .exe,即不代表是 Windows 平台的可执行文件。
1 2 3 4 5 6 7 8
| <protocPlugin> <id>dubbo</id> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-compiler</artifactId> <version>${dubbo.version}</version> <mainClass>org.apache.dubbo.gen.tri.Dubbo3TripleGenerator</mainClass> </protocPlugin>
|
- 这是用来给
protoc 添加自定义代码生成插件(比如 Dubbo Triple 的 Stub 代码生成器)。也就是说,除了生成 Protobuf 的 Java 类(DTO、Message)、还会生成 Dubbo Triple 协议专用的服务接口(Stub)代码。
编译生成代码
代码生成的位置
- 根据
.proto 文件,通过 Protobuf 的 Maven 插件自动生成代码后,相关的代码源文件默认会存放在当前模块的 /target/generated-sources/protobuf/ 目录中。 - 一般不需要指定
protoc 生成 Java 文件的输出路径,因为 IDEA 会自动将 /target/generated-sources/protobuf/ 加入到编译路径(之后可以正常引用生成的代码),Maven 会自动编译这些自动生成的代码,但不会污染 src/main/java。 - 如果将
protoc 自动生成的代码指定输出到 src/main/java 目录中,那么生成代码会持久化保存,即使 .proto 文件删除了,旧代码还会残留;而且 mvn clean 不会自动删除 src/main/java 里自动生成的文件,容易积累垃圾代码。 - 如果需要指定
protoc 生成 Java 文件的输出路径,那么建议指定为 ${basedir}/build/generated/source/proto/main/java,然后再配置 Maven 插件 build-helper-maven-plugin 来添加指定的源码目录路径。
- 执行以下 Maven 命令,可以根据
.proto 文件编译生成 Protobuf 的 Java 类(DTO、Message)、Dubbo Triple 协议专用的服务接口(Stub)代码
1
| mvn clean protobuf:compile
|
- 比如,执行
mvn clean compile 命令后,根据 .proto 文件自动生成的代码源文件列表如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| build └── generated └── source └── proto └── main └── java └── com └── clay └── dubbo └── api ├── DubboUserServiceTriple.java ├── HelloRequest.java ├── HelloRequestOrBuilder.java ├── HelloResponse.java ├── HelloResponseOrBuilder.java ├── UserService.java └── UserServiceProto.java
|
提示
除了可以使用 Maven 命令根据 .proto 文件自动生成代码,还可以通过 IDEA 的可视化工具来生成代码(如图所示),运行效果类似于执行 mvn clean compile 命令,前提是已配置好 Protobuf 的 Maven 插件。
使用新的插件
- Dubbo
3.3.0 之前的版本可以使用上面的 Maven 插件 protobuf-maven-plugin(实测 Dubbo 3.3.0 之后的版本也可以正常使用),但 Dubbo 3.3.0 之后的版本提供了新的 dubbo-maven-plugin 插件,使用起来更方便,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <dubbo.version>3.3.2</dubbo.version> <protoc.version>3.25.3</protoc.version> </properties>
<dependencies> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protoc.version}</version> </dependency> </dependencies>
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.7.0</version> </extension> </extensions>
<plugins> <plugin> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-maven-plugin</artifactId> <version>${dubbo.version}</version> <configuration> <protocVersion>${protoc.version}</protocVersion> <protoSourceDir>${basedir}/src/main/proto</protoSourceDir> <outputDir>${basedir}/build/generated/source/proto/main/java</outputDir> <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact> <dubboGenerateType>tri</dubboGenerateType> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> <phase>generate-sources</phase> </execution> </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>${basedir}/build/generated/source/proto/main/java</source> </sources> </configuration> </execution> </executions> </plugin> </plugins> </build>
|
- 其中
configuration 可配置参数如下表所示:
| 参数 | 必填参数 | 默认值 | 说明 | 备注 |
|---|
outputDir | 否 | ${project.build.directory}/generated-sources/protobuf/java | 生成的 Java 文件存放目录 | |
protoSourceDir | 否 | ${basedir}/src/main/proto | .proto 文所在的目录 | |
protocArtifact | 否 | com.google.protobuf:protoc:3.25.0:exe:操作系统名:操作系统架构 | protoc 编译器组件 | |
protocVersion | 否 | 3.25.0 | protoc 编译器的版本 | |
dubboGenerateType | 否 | tri | 代码生成类型 | 可填 tri 或者 tri_reactor |
- 执行以下 Maven 命令,可以根据
.proto 文件编译生成 Protobuf 的 Java 类(DTO、Message)、Dubbo Triple 协议专用的服务接口(Stub)代码
- 比如,执行
mvn clean compile 命令后,根据 .proto 文件自动生成的代码源文件列表如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| build └── generated └── source └── proto └── main └── java └── com └── clay └── dubbo └── api ├── DubboUserServiceTriple.java ├── HelloRequest.java ├── HelloRequestOrBuilder.java ├── HelloResponse.java ├── HelloResponseOrBuilder.java ├── UserService.java └── UserServiceProto.java
|
- 除了可以使用 Maven 命令根据
.proto 文件自动生成代码,还可以通过 IDEA 的可视化工具来生成代码(如图所示),前提是已配置好 Dubbo 的 Maven 插件。
Provider 模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <properties> <spring-boot.version>3.4.2</spring-boot.version> <dubbo.version>3.3.2</dubbo.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>com.clay.dubbo</groupId> <artifactId>dubbo-lesson-01-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-nacos</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
|
- 接口实现类(继承了由
protoc 根据 .proto 文件自动生成的 DubboUserServiceTriple.UserServiceImplBase 类),@DubboService 注解主要用于暴露服务,使其能够被 Dubbo 框架识别并注册到服务注册中心
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org.apache.dubbo.config.annotation.DubboService;
@Slf4j @DubboService public class UserServiceImpl extends DubboUserServiceTriple.UserServiceImplBase {
@Override public HelloResponse sayHello(HelloRequest request) { log.info("Hello, " + request.getName()); return HelloResponse.newBuilder().setMessage("Hello, " + request.getName() + "!").build(); }
}
|
特别注意
- 这里的
UserServiceImpl 继承了由 .proto 文件自动生成的 DubboUserServiceTriple.UserServiceImplBase 类,而不是实现由 .proto 文件自动生成的 UserService 接口。 - 如果
UserServiceImpl 直接实现由 .proto 文件自动生成的 UserService 接口,效果相当于使用普通 Java 接口来定义服务,其他 gRPC 客户端(协议)不能直接调用 RPC 服务,即无法实现跨语言调用。 - 如果
UserServiceImpl 直接实现由 .proto 文件自动生成的 UserService 接口,其运行效果相当于 配合 Java 接口使用 Triple 协议,可以通过标准的 HTTP 工具(比如 curl + application/json)调用 RPC 服务,命令如下所示:1
| curl -H "Content-Type: application/json" -d '{"name":"dubbo"}' http://127.0.0.1:50052/com.clay.dubbo.api.UserService/sayHello
|
1 2 3 4 5 6 7 8
| @SpringBootApplication public class ProviderApplication {
public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| server: port: 9090
spring: application: name: dubbo-provider-application
dubbo: application: name: ${spring.application.name} qos-enable: false qos-port: 22222 qos-accept-foreign-ip: false registry: address: nacos://192.168.2.235:8848 protocol: name: tri port: 50052 preferred-protocol: tri scan: base-packages: com.clay.dubbo.provider
|
提示
若不希望在 YML 配置文件中指定 dubbo.scan.base-packages 参数,那么可以在主启动类上标注 @EnableDubbo(scanBasePackages = "xxx") 注解或者 @DubboComponentScan(basePackages = "xxx") 注解来替代。
Consumer 模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <properties> <spring-boot.version>3.4.2</spring-boot.version> <dubbo.version>3.3.2</dubbo.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>com.clay.dubbo</groupId> <artifactId>dubbo-lesson-01-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-nacos</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
|
- 业务测试类,
@DubboReference 注解主要用于在服务消费者端引用远程服务提供者的服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import org.apache.dubbo.config.annotation.DubboReference;
@Slf4j @RestController @RequestMapping("/system") public class SystemController {
@DubboReference private UserService userService;
@GetMapping("/sayHello/{name}") public String getUser(@PathVariable("name") String name) { HelloRequest request = HelloRequest.newBuilder().setName(name).build(); HelloResponse response = userService.sayHello(request); log.info("result: {}", response.getMessage()); return response.getMessage(); }
}
|
1 2 3 4 5 6 7 8
| @SpringBootApplication public class ConsumerApplication {
public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| server: port: 9095
spring: application: name: dubbo-consumer-application
dubbo: application: name: ${spring.application.name} qos-enable: false qos-port: 22222 qos-accept-foreign-ip: false registry: address: nacos://192.168.2.235:8848 scan: base-packages: com.clay.dubbo.consumer consumer: check: false retries: 0 timeout: 1000
|
提示
若不希望在 YML 配置文件中指定 dubbo.scan.base-packages 参数,那么可以在主启动类上标注 @EnableDubbo(scanBasePackages = "xxx") 注解或者 @DubboComponentScan(basePackages = "xxx") 注解来替代。
测试代码
测试案例一
测试案例二
提示
- 基于 IDL + Protobuf + Triple 协议实现的 RPC 服务,可以通过 gRPC 客户端、Dubbo 的客户端 Stub、
grpcurl(命令行 gRPC 测试工具)等直接调用。 - 下面将演示如何使用
grpcurl(命令行 gRPC 测试工具)直接调用 IDL + Protobuf + Triple 协议实现的 RPC 服务,grpcurl 工具可以从 GitHub 下载得到。
(1) 启动 Nacos 服务器
(2) 在 IDEA 开发工具内,单独启动 Provider 模块
(3) 切换到 .proto 文件所在的目录,执行以下 grpcurl 命令直接调用 Provider 模块提供的 RPC 服务,若可以正常返回 JSON 结果 {"message":"Hello, Dubbo!"},则说明 Dubbo Triple 协议与 Protobuf(IDL)正常工作
1 2 3 4
| grpcurl -plaintext \ -proto ./UserService.proto \ -d '{"name":"Dubbo"}' \ 127.0.0.1:50052 userservice.UserService/SayHello
|
特别注意:为了可以通过 gRPC 客户端(比如 grpcurl 工具)直接调用 RPC 服务,必须注意以下事项
- Dubbo 使用的协议必须指定为
tri - 服务必须是由
.proto 文件生成的接口来定义,而不是纯 Java POJO 接口 - 参数格式是 JSON 对象,比如
{"name":"Dubbo"} - 指定
.proto 文件的路径:比如 -proto ./UserService.proto - 指定 gRPC 服务的地址和端口:
host:port,端口必须是 Triple 协议占用的端口(比如 50052),而不是 Tomcat 占用的端口 - 指定要调用的 gRPC 方法,格式是:
<proto包名>.<proto服务名>/<proto方法名>,大小写敏感,比如 userservice.UserService/SayHello
下载代码
- 完整的案例代码可以直接从 GitHub 下载对应章节
dubbo-lesson-05。
尝试支持 HTTP/JSON 调用服务
- Dubbo 3 基于 IDL + Protobuf + Triple 协议实现的 RPC 服务,默认只能通过 gRPC 客户端、Dubbo 的客户端 Stub 等直接调用。如果希望通过标准的 HTTP 工具调用(比如
curl),可以在前面的 API 模块中额外引入 protobuf-java-util 依赖来实现:
1 2 3 4 5 6
| <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java-util</artifactId> <version>${protoc.version}</version> </dependency>
|
- 之后可以尝试通过
curl 命令直接调用 RPC 接口,如下所示:
1 2 3 4
| curl \ --header "Content-Type: application/json" \ --data '{"name":"Dubbo"}' \ http://127.0.0.1:50052/com.clay.dubbo.api.UserService/SayHello
|
实测无效
实测在前面的 API 模块中引入 protobuf-java-util 依赖,同时 UserServiceImpl 类继承了由 .proto 文件自动生成的 DubboUserServiceTriple.UserServiceImplBase 类,发现无法通过 curl + application/json 的方式来调用 RPC 服务,返回错误信息 {"message":"Invoker not found","status":"404"}。如果 UserServiceImpl 类实现了由 .proto 文件自动生成的 UserService 接口,相当于基于 Java 接口 + Triple 协议提供 RPC 服务,虽然可以通过 curl + application/json 的方式来调用 RPC 服务,但无法通过 gRPC 客户端(协议)调用 RPC 服务,即失去了跨编程语言调用的功能。
使用插件生成 gRPC 的 Stub 代码
- 使用 Maven 插件
protobuf-maven-plugin 时,若希望生成 gRPC 的服务接口(Stub)代码,可以参考以下 Maven 配置内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <dubbo.version>3.3.2</dubbo.version> <protoc.version>3.25.3</protoc.version> <grpc.version>1.73.0</grpc.version> </properties>
<dependencies> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protoc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-api</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> </dependencies>
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.7.0</version> </extension> </extensions>
<plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <clearOutputDirectory>false</clearOutputDirectory> <protoSourceRoot>${basedir}/src/main/proto</protoSourceRoot> <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} </protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact> <protocPlugins> <protocPlugin> <id>dubbo</id> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-compiler</artifactId> <version>${dubbo.version}</version> <mainClass>org.apache.dubbo.gen.tri.Dubbo3TripleGenerator</mainClass> </protocPlugin> </protocPlugins> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>test-compile</goal> <goal>compile-custom</goal> <goal>test-compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
- 对比前面案例的 Maven 配置文件,可以发现增加了以下依赖组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <properties> <grpc.version>1.73.0</grpc.version> </properties>
<dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-api</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> </dependencies>
|
- 对比前面案例的 Maven 配置文件,可以发现还增加了以下
configuration 配置,并去除了 outputDirectory 配置,不再指定 protoc 生成 Java 文件的存放目录
1 2 3
| <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
|
- 最终执行
mvn clean compile 命令后,根据 .proto 文件自动生成的代码源文件列表如下(默认存放在当前模块的 /target/generated-sources/protobuf 目录中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| target/generated-sources ├── annotations └── protobuf ├── grpc-java │ └── com │ └── clay │ └── dubbo │ └── api │ └── UserServiceGrpc.java └── java └── com └── clay └── dubbo └── api ├── DubboUserServiceTriple.java ├── HelloRequest.java ├── HelloRequestOrBuilder.java ├── HelloResponse.java ├── HelloResponseOrBuilder.java ├── UserService.java └── UserServiceProto.java
|
- gRPC 的 Stub & Skeleton 代码存放在新生成的
UserServiceGrpc.java 文件中,代码完全不依赖 Dubbo。这个文件是 gRPC 的 Java 桥接文件,它主要起到 gRPC 客户端调用代码(Stub)和服务端注册实现(Skeleton)的作用,其主要包含以下内容:
| 内容 | 作用 |
|---|
| 服务定义 (Service Descriptor) | 描述 gRPC 服务,供框架底层反射、注册用 |
| Stub 类(异步 / 阻塞 / 未来模式) | 客户端调用 RPC 接口用 |
抽象基类(UserServiceImplBase) | 服务端继承,实现业务逻辑 |
参考教程