SpringBoot 整合 gRPC
简介
gRPC是google开源的一个高性能、跨语言的RPC框架,基于HTTP2协议,基于protobuf 3.x,基于Netty 4.x +。
对于开发者而言:
1)需要使用protobuf定义接口,即.proto文件。
2)然后使用compile工具生成特定语言的执行代码,比如JAVA、C/C++、Python等。类似于thrift,为了解决跨语言问题。
3)启动一个Server端,server端通过侦听指定的port,来等待Client链接请求,通常使用Netty来构建,GRPC内置了Netty的支持。
4)启动一个或者多个Client端,Client也是基于Netty,Client通过与Server建立TCP常链接,并发送请求;Request与Response均被封装成HTTP2的stream Frame,通过Netty Channel进行交互。
proto3
编写proto3文件
文件命名为pb.proto。
syntax = "proto3";
option java_multiple_files = true;
option java_package = "org.example.entity";
option java_outer_classname = "UserServiceProto";
service UserService {
// 注册接口
rpc register(RegisterReq) returns (RegisterRsp);
}
message RegisterReq {
// 手机号
string Phone = 1;
// 用户ID
string UserId = 2;
}
message RegisterRsp {
// 业务状态码
int32 code = 1;
// 信息
string msg = 2;
}
根据proto生成代码
-
创建一个空的项目。
-
pom文件内容如下。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>proto</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jackson.version>2.8.3</jackson.version> <grpc.version>1.6.1</grpc.version> <os.plugin.version>1.5.0.Final</os.plugin.version> <protobuf.plugin.version>0.5.0</protobuf.plugin.version> <protoc.version>3.3.0</protoc.version> <grpc.netty.version>4.1.14.Final</grpc.netty.version> </properties> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-common</artifactId> <version>${grpc.netty.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>${os.plugin.version}</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>${protobuf.plugin.version}</version> <configuration> <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> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
-
java同级目录下添加proto目录,将编写好的proto文件放到该目录下。
-
依次执行mvn命令protobuf:compile和protobuf:compile-custom编译生成java文件。
-
生成后的文件存放在target目录下。
可以对比看出,生成的java文件和proto文件中定义的service和message是对应的。其中
option java_multiple_files = true;
配置决定是将message定义为一个文件还是内部类,将配置修改为false后再次执行mvn命令后会发现生成的文件变成以下这种,这就是把message定义为了内部类。不过这两种对功能上并无影响,开发者可以自行选择。推荐将生成代码的项目单独存放,不要合并到开发项目中。因为pom中引入的jar和插件只是为了生成代码,后续在SpringBoot中使用gRPC并不需要这些jar。示例项目放到了96XL/proto: proto文件生成 (github.com),需要的可以自取。
SpringBoot整合
生成代码的项目、服务端项目、客户端项目三者可以完全分离,单独使用哪个项目都可以。例如使用服务端给外部系统提供gRPC接口,或者使用客户端调用外部系统的gRPC接口。
服务端
-
添加maven依赖。
<dependency> <groupId>net.devh</groupId> <artifactId>grpc-server-spring-boot-starter</artifactId> <version>2.13.1.RELEASE</version> </dependency>
-
服务端代码(记得将生成的类加到项目中)。
@Slf4j @GrpcService public class UserGrpcServer extends UserServiceGrpc.UserServiceImplBase { public void register(UserServiceProto.RegisterReq request, StreamObserver<UserServiceProto.RegisterRsp> responseObserver) { // 接收到的参数 log.info(request.toString()); // 响应 UserServiceProto.RegisterRsp reply = UserServiceProto.RegisterRsp.newBuilder().setCode(0).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } }
-
配置文件添加配置。
grpc: server: port: 7777
客户端
- 添加maven依赖。
<dependency> <groupId>net.devh</groupId> <artifactId>grpc-client-spring-boot-starter</artifactId> <version>2.13.1.RELEASE</version> </dependency>
- 客户端代码(记得将生成的类加到项目中)。
@Service public class UserGrpcClient { @GrpcClient("userServiceGrpc") private UserServiceGrpc.UserServiceBlockingStub userServiceBlockingStub; public void register(String phone, String userId) { UserServiceProto.RegisterReq registerReq = UserServiceProto.RegisterReq.newBuilder().setPhone(phone).setUserId(userId).build(); userServiceBlockingStub.register(registerReq); } }
- 配置文件添加配置。这里的IP就是服务端的IP,端口号要和服务端指定的端口号保持一致。userServiceGrpc就是客户端代码
@GrpcClient("userServiceGrpc")
中指定的值,两者保持一致即可,和服务端没有关系。grpc: client: userServiceGrpc: address: static://localhost:7777 negotiationType: PLAINTEXT
- 接口中正常调用方法即可。