gRPC概述
gRPC是一种跨语言的RPC框架,之所以它能跨语言,是因为它基于protobuf描述对象实体和方法,最后通过protobuf编译器生成指定语言的代码。
这样,就能通过一套protobuf声明生成多种语言的相同API,对于实现跨语言的RPC通信非常便利,同时也使用protobuf作为通信的序列化协议。
如下通过一个简单的示例展示如何在Java语言中基于gRPC实现一个C/S架构的通信模型。
使用步骤
安装protobuf编译器
下载并安装protobuf编译器,并将其bin路径添加到PATH变量中,如:D:\opt\protoc-3.13.0-win64\bin
。
添加protobuf-java依赖
在Maven项目中添加protobuf-java
依赖:
1 2 3 4 5
| <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.13.0</version> </dependency>
|
注:这里protobuf-java
的版本必须与protobuf编译器的版本保持一致!
编写protobuf描述文件
编写protobuf描述文件(在Maven项目中通常将proto文件放在src/main/proto
路径下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| syntax = "proto3";
option java_multiple_files = true; option java_outer_classname = "HelloWordProto"; option java_package = "org.chench.extra.java.grpc.proto";
message HelloRequest { string greeting = 1; }
message HelloResponse { string reply = 1; }
service HelloService { rpc SayHello (HelloRequest) returns (HelloResponse); }
|
编译protobuf描述文件
编译protobuf描述文件生成对应的Java类文件,有2种方式:
方式一:进入到protobuf描述文件路径,执行命令:protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/xxx.proto
(注:$SRC_DIR
和$DST_DIR
都必须是绝对路径,否则无法正确编译)
注:命令行编译的方式默认只会生成message
声明的实体类,不会生成service
声明的RPC类,解决办法参考:protoc不生成.proto中的service,只生成model相关类,求助。
方式二:通过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
| <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.13.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}</pluginArtifact> <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot> <outputDirectory>${project.basedir}/src/main/java</outputDirectory> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <phase>compile</phase> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
在项目根目录下执行:mvn compile
即可生成对应的java类。
简单rpc示例
服务端
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
| public class HelloWorldServer { private static int port = 8181; private Server server;
public static void main(String[] args) throws IOException, InterruptedException { HelloWorldServer helloWorldServer = new HelloWorldServer(); helloWorldServer.start(); helloWorldServer.blockUntilShutdown(); }
private void start() throws IOException { this.server = ServerBuilder.forPort(port) .addService(new HelloServiceImpl()) .build() .start(); logger.info(String.format("start server on port: %s", port)); Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run() { logger.info("do stop..."); try { HelloWorldServer.this.stop(); } catch (InterruptedException e) { e.printStackTrace(); } logger.info("stop done."); } }); }
private void stop() throws InterruptedException { if (this.server != null) { server.shutdown().awaitTermination(30, TimeUnit.SECONDS); } }
private void blockUntilShutdown() throws InterruptedException { if (this.server != null) { this.server.awaitTermination(); } }
class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { @Override public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) { String greeting = request.getGreeting(); logger.info(String.format("server receive greeting: %s", greeting)); String responseMsg = new StringBuilder().append("Hello: ").append(greeting).toString(); HelloResponse response = HelloResponse.newBuilder().setReply(responseMsg).build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } }
|
客户端
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
| public class HelloWorldClient { private HelloServiceGrpc.HelloServiceBlockingStub blockingStub;
public HelloWorldClient(Channel channel) { this.blockingStub = HelloServiceGrpc.newBlockingStub(channel); }
public static void main(String[] args) throws IOException, InterruptedException { String target = "localhost:8181"; ManagedChannel channel = ManagedChannelBuilder.forTarget(target) .usePlaintext() .build(); HelloWorldClient helloWorldClient = new HelloWorldClient(channel); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = null; while ((line = reader.readLine()) != null) { if ("quit".equals(line.trim())) { channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); System.exit(0); } else { helloWorldClient.greeting(line); } } }
private void greeting(String greeting) { HelloRequest request = HelloRequest.newBuilder().setGreeting(greeting).build(); HelloResponse response = this.blockingStub.sayHello(request); System.out.println(String.format("received response: %s", response.getReply())); } }
|
【参考】
grpc-java
java使用protobuf-maven-plugin的插件编译proto文件
java语言中生成gprc代码的三种方式:gradle、protoc、镜像的方式