本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、概述
gRpc 是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。
gRPC基于HTTP/2标准设计,带来诸如双向流、流控、头部压缩、单TCP连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
gRpc默认使用的是Protocol Buffers,是Google开源的一种接口描述语言(IDL)
目前官方支持的语言以及平台如下:
| Language | OS | Compilers / SDK |
|---|---|---|
| C/C++ | Linux, Mac | GCC 6.3+, Clang 6+ |
| C/C++ | Windows 10+ | Visual Studio 2017+ |
| C# | Linux, Mac | .NET Core, Mono 4+ |
| C# | Windows 10+ | .NET Core, NET 4.5+ |
| Dart | Windows, Linux, Mac | Dart 2.12+ |
| Go | Windows, Linux, Mac | Go 1.13+ |
| Java | Windows, Linux, Mac | Java 8+ (KitKat+ for Android) |
| Kotlin | Windows, Linux, Mac | Kotlin 1.3+ |
| Node.js | Windows, Linux, Mac | Node v8+ |
| Objective-C | macOS 10.10+, iOS 9.0+ | Xcode 12+ |
| PHP | Linux, Mac | PHP 7.0+ |
| Python | Windows, Linux, Mac | Python 3.5+ |
| Ruby | Windows, Linux, Mac | Ruby 2.3+ |
二、入门使用(grpc-java)
gRpc的使用有三个基本步骤:
- 编写
IDL文件(.proto)- 将编写的
.proto描述文件生成为Java代码- 编写调用逻辑
2.1 编写 IDL 文件
-
我们新建一个
maven工程,命名为yiwenup-sample-grpc-protocol,这个模块用于根据.proto文件生成对应Java代码。后续无论是消费方还是服务方都依赖此协议:消费方根据接口本地产生远程调用代理;服务方根据接口编写具体的服务逻辑。 修改pom.xml文件,为当前模块引入grpc-all依赖以及对应的代码生成插件<?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"> <parent> <artifactId>yiwenup-sample-grpc</artifactId> <groupId>cloud.yiwenup.sample</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>yiwenup-sample-grpc-generator</artifactId> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.48.1</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.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <pluginId>grpc-java</pluginId> <protocArtifact>com.google.protobuf:protoc:3.21.5:exe:${os.detected.classifier}</protocArtifact> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.49.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> -
在
main目录下新建proto目录,用于存放.proto文件,此目录下的文件将会被转换为.java源文件 -
.proto文件内容参考如下,具体语法需要根据官方文档指导:syntax = "proto3"; // 协议版本 // 选项配置 option java_package = "cloud.yiwenup.sample.grpc.gen"; // 生成文件的包路径 option java_outer_classname = "HelloWorldProto"; // 名称 option java_multiple_files = true; // 生成多分java文件,便于管理 // 定义请求体 message SayHelloRequest { string serial = 1; } // 定义相应内容 message SayHelloResponse { string code = 1; string msg = 2; string data = 3; } // 服务接口.定义请求参数和相应结果 service SayHelloService { rpc sayHello (SayHelloRequest) returns (SayHelloResponse) { } } -
运行命令
protobuf:compile可以生成gRpc相关的Bean;运行命令protobuf:compile-custom可以生成gRpc的服务模块,对应生成的代码,在target/generated-sources/protobuf目录下
2.2 编写服务端(提供方)
服务端根据
.proto文件在协议层生成的代码将具体逻辑填充
-
首先需要编写服务端的启动部分,保证服务端可以启动并且阻塞等待
shutdown指令才可以关闭JVM停止服务。为此,新建一个maven工程yiwenup-sample-grpc-server,添加grpc-all依赖,服务端参考代码如下:public class GrpcServer { public static void main(String[] args) { try { // 启动服务端,直到接受到termination指令 Server server = ServerBuilder .forPort(7777) // 添加服务实现逻辑 .addService(new SayHelloServiceImpl()) .build() .start(); System.out.println("[服务端] 启动成功..."); // 当接收到shutdown信号的时候,停止服务端 Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("[服务端] 正在停止JVM..."); server.shutdown(); System.out.println("[服务端] 服务已关闭..."); })); server.awaitTermination(); } catch (Exception e) { throw new RuntimeException(e); } } } -
在服务启动代码中,需要添加一系列基于协议层实现的具体逻辑,譬如上述代码中的
SayHelloServiceImpl便是如此。因此本maven工程还需要添加协议层依赖yiwenup-sample-grpc-protocol,实现逻辑参考代码如下:public class SayHelloServiceImpl extends SayHelloServiceGrpc.SayHelloServiceImplBase { private AtomicInteger atomicInteger = new AtomicInteger(); @Override public void sayHello(SayHelloRequest request, StreamObserver<SayHelloResponse> responseObserver) { String serial = request.getSerial(); try { responseObserver.onNext(SayHelloResponse.newBuilder() .setCode("200") .setMsg("成功") .setData(serial + " <> " + atomicInteger.getAndIncrement()).build()); } catch (Exception e) { responseObserver.onError(e); } finally { responseObserver.onCompleted(); } } } -
至此,服务端就准备完毕了,启动服务端,可以看到控制台的打印信息,以及在关闭服务端的时候也有信息打印。
2.3 编写客户端(消费方)
客户端根据
.proto文件在协议层生成的代码直接本地调用,底层通过代理实现RPC
-
新建一个
maven工程,添加两个依赖:grpc-all以及yiwenup-sample-grpc-protocol,本示例在客户端启动完成之后立即远程调用服务,之后关闭客户端。public class GrpcClient { public static void main(String[] args) { // 建立和服务端的一个 channel ManagedChannel channel = ManagedChannelBuilder .forAddress("localhost", 7777) .usePlaintext() .build(); try { // 获取接口的代理对象 SayHelloServiceGrpc.SayHelloServiceBlockingStub sayHelloServiceBlockingStub = SayHelloServiceGrpc.newBlockingStub(channel); // 本地调用 for (int i = 0; i < 100000; i++) { SayHelloResponse sayHelloResponse = sayHelloServiceBlockingStub.sayHello(SayHelloRequest.newBuilder().setSerial(String.valueOf(i)).build()); System.out.println("[客户端 " + i + "] " + sayHelloResponse.getCode() + " => " + sayHelloResponse.getMsg() + " => " + sayHelloResponse.getData()); } } catch (Exception e) { throw new RuntimeException(e); } finally { if (channel != null) { channel.shutdown(); } } } } -
在服务端启动着的前提下,运行客户端程序。