使用 Protobuf(IDL) 开发 triple 通信服务

Triple 协议支持使用 Protocol Buffers (Protobuf) 定义服务,对于因为多语言、gRPC、安全性等场景选型protobuf 的用户更友好。

使用方式

POM依赖

  1. <plugin>
  2. <groupId>org.apache.dubbo</groupId>
  3. <artifactId>dubbo-maven-plugin</artifactId>
  4. <version>${dubbo.version}</version> <!-- 3.3.0及以上版本 -->
  5. <configuration>
  6. <outputDir>build/generated/source/proto/main/java</outputDir> <!-- 参考下文可配置参数 -->
  7. </configuration>
  8. </plugin>

configuration可配置参数

参数必填参数默认值说明备注
outputDir${project.build.directory}/generated-sources/protobuf/java生成的java文件存放目录
protoSourceDir${basedir}/src/main/protoproto文件目录
protocArtifactcom.google.protobuf:protoc:3.25.0:exe:操作系统名:操作系统架构proto编译器组件
protocVersion3.25.0protobuf-java的版本
dubboGenerateTypetri代码生成类型可填tri或者tri_reactor

服务定义

使用 Protocol Buffers 定义 Greeter 服务

  1. syntax = "proto3";
  2. option java_multiple_files = true;
  3. package org.apache.dubbo.samples.tri.unary;
  4. message GreeterRequest {
  5. string name = 1;
  6. }
  7. message GreeterReply {
  8. string message = 1;
  9. }
  10. service Greeter{
  11. rpc greet(GreeterRequest) returns (GreeterReply);
  12. }

请注意以上 package 定义 package org.apache.dubbo.samples.tri.unary;,在此示例中 package 定义的路径将同时作为 java 包名和服务名前缀。这意味着 rpc 服务的完整定义是:org.apache.dubbo.samples.tri.unary.Greeter,与生成代码的路径完全一致。

但保持一致并不是必须的,你也可以将 java 包名与服务名前缀定义分开定义,对于一些跨语言调用的场景比较有用处。如以下 IDL 定义中:

  • 完整服务名是 greet.Greeter,rpc 调用及服务发现过程中会使用这个值
  • java 包名则是 java_package 定义的 org.apache.dubbo.samples.tri.unary,生成的 java 代码会放在这个目录。
  1. package greet;
  2. option java_package = "org.apache.dubbo.samples.tri.unary;"
  3. option go_package = "github.com/apache/dubbo-go-samples/helloworld/proto;greet";

服务实现

在运行 mvn clean compile 后可生成 Dubbo 桩代码。接下来继承生成的基础类 DubboGreeterTriple.GreeterImplBase,添加具体的业务逻辑实现:

  1. public class GreeterImpl extends DubboGreeterTriple.GreeterImplBase {
  2. @Override
  3. public GreeterReply greet(GreeterRequest request) {
  4. LOGGER.info("Server {} received greet request {}", serverName, request);
  5. return GreeterReply.newBuilder()
  6. .setMessage("hello," + request.getName())
  7. .build();
  8. }
  9. }

注册服务到 server,其中 protocol 设置为 tri 代表开启 triple 协议。

  1. public class TriUnaryServer {
  2. public static void main(String[] args) throws IOException {
  3. ServiceConfig<Greeter> service = new ServiceConfig<>();
  4. service.setInterface(Greeter.class);
  5. service.setRef(new GreeterImpl());
  6. DubboBootstrap bootstrap = DubboBootstrap.getInstance();
  7. bootstrap.protocol(new ProtocolConfig(CommonConstants.TRIPLE, 50052))
  8. .service(service)
  9. .start().await();
  10. }
  11. }

编写 client 逻辑

  1. public class TriUnaryClient {
  2. public static void main(String[] args) throws IOException {
  3. DubboBootstrap bootstrap = DubboBootstrap.getInstance();
  4. ReferenceConfig<Greeter> ref = new ReferenceConfig<>();
  5. ref.setInterface(Greeter.class);
  6. ref.setUrl("tri://localhost:50052");
  7. bootstrap.reference(ref).start();
  8. Greeter greeter = ref.get();
  9. final GreeterReply reply = greeter.greet(GreeterRequest.newBuilder().setName("name").build());
  10. }
  11. }

常见问题

protobuf 类找不到

由于 Triple 协议底层需要依赖 protobuf 协议进行传输,即使定义的服务接口不使用 protobuf 也需要在环境中引入 protobuf 的依赖。

  1. <dependency>
  2. <groupId>com.google.protobuf</groupId>
  3. <artifactId>protobuf-java</artifactId>
  4. <version>3.19.4</version>
  5. </dependency>

同时,为了支持 application/json 格式请求直接访问,还需要增加如下依赖。

  1. <dependency>
  2. <groupId>com.google.protobuf</groupId>
  3. <artifactId>protobuf-java-util</artifactId>
  4. <version>3.19.4</version>
  5. </dependency>

生成的代码无法编译

在使用 Protobuf 时,请尽量保持 dubbo 核心库版本与 protoc 插件版本一致,并运行 mvn clean compile 重新生成代码。

3.3.0 版本之前的POM依赖

3.3.0 之前的版本使用 protobuf-maven-plugin 配置 protoc 插件,其中 dubbo-compiler 必须保持和使用的内核 dubbo 版本一致:

  1. <plugin>
  2. <groupId>org.xolstice.maven.plugins</groupId>
  3. <artifactId>protobuf-maven-plugin</artifactId>
  4. <version>0.6.1</version>
  5. <configuration>
  6. <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
  7. <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
  8. <protocPlugins>
  9. <protocPlugin>
  10. <id>dubbo</id>
  11. <groupId>org.apache.dubbo</groupId>
  12. <artifactId>dubbo-compiler</artifactId>
  13. <version>${dubbo.version}</version>
  14. <mainClass>org.apache.dubbo.gen.tri.Dubbo3TripleGenerator</mainClass>
  15. </protocPlugin>
  16. </protocPlugins>
  17. </configuration>
  18. <executions>
  19. <execution>
  20. <goals>
  21. <goal>compile</goal>
  22. </goals>
  23. </execution>
  24. </executions>
  25. </plugin>

运行示例

本示例演示如何使用 Protocol Buffers 定义服务,并将其发布为对外可调用的 triple 协议服务。如果你有多语言业务互调、gRPC互通,或者熟悉并喜欢 Protobuf 的开发方式,则可以使用这种模式,否则可以考虑上一篇基于Java接口的 triple 开发模式。

可在此查看 本示例的完整代码

注意

本文使用的示例是基于原生 API 编码的,这里还有一个 Spring Boot 版本的示例 供参考,同样是 protobuf+triple 的模式,但额外加入了服务发现配置。

首先,可通过以下命令下载示例源码:

  1. git clone --depth=1 https://github.com/apache/dubbo-samples.git

进入示例源码目录:

  1. cd dubbo-samples/1-basic/dubbo-samples-api-idl

编译项目,由 IDL 生成代码,这会调用 dubbo 提供的 protoc 插件生成对应的服务定义代码:

  1. mvn clean compile

生成代码如下

  1. ├── build
  2. └── generated
  3. └── source
  4. └── proto
  5. └── main
  6. └── java
  7. └── org
  8. └── apache
  9. └── dubbo
  10. └── samples
  11. └── tri
  12. └── unary
  13. ├── DubboGreeterTriple.java
  14. ├── Greeter.java
  15. ├── GreeterOuterClass.java
  16. ├── GreeterReply.java
  17. ├── GreeterReplyOrBuilder.java
  18. ├── GreeterRequest.java
  19. └── GreeterRequestOrBuilder.java

启动Server

运行以下命令启动 server。

  1. mvn compile exec:java -Dexec.mainClass="org.apache.dubbo.samples.tri.unary.TriUnaryServer"

访问服务

有两种方式可以访问 Triple 服务:

  • 以标准 HTTP 工具访问
  • 以 Dubbo client sdk 访问

cURL 访问

  1. curl \
  2. --header "Content-Type: application/json" \
  3. --data '{"name":"Dubbo"}' \
  4. http://localhost:50052/org.apache.dubbo.samples.tri.unary.Greeter/greet/

Dubbo client 访问

运行以下命令,启动 Dubbo client 并完成服务调用

  1. mvn compile exec:java -Dexec.mainClass="org.apache.dubbo.samples.tri.unary.TriUnaryClient"

最后修改 October 11, 2024: Update idl usage document (#3041) (e2c3c45727c)