Java gRPC 小例子
|Word count:2k|Reading time:10min|Post View:
最近准备着手学习下gRPC,就先以一个小demo作为开始吧。
创建Maven项目
刚开始学习就没必要着急与SpringBoot结合了,先试试最传统的Java项目。其pom.xml
内容如下:
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 99
| <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>rpcdemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging>
<name>rpcdemo</name> <url>http://maven.apache.org</url>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <protobuf.version>3.21.12</protobuf.version> <grpc.version>1.53.0</grpc.version> </properties>
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</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>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protobuf.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.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <pluginId>grpc-java</pluginId> <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact> <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact> <protoSourceRoot>src/main/resources/proto</protoSourceRoot> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> </configuration> </plugin>
</plugins> </build> </project>
|
创建.proto文件
根据pom.xml
中指定的路径,在src/main/resources/proto
中创建一个名为helloworld.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
| syntax = "proto3";
package example; option java_package = "cn.net.dev.grpc";
option java_outer_classname = "HelloWorldServiceProto";
option java_multiple_files = true;
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} }
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
|
在IDE中,点击MAVEN侧边栏
->plugins
->protobuf
->protobuf:compile
,protobuf:compile-custom
,或者直接在控制台中输入mvn compile
,mvn compile-custom
。
protobuf:compile // 编译消息对象
protobuf:compile-custom // 依赖消息对象,生成接口服务
具体可参考 Maven Protocol Buffers Plugin – protobuf:compile (xolstice.org)
编译完成后,会在target/generated-sources/protobuf/java/cn/net/dev/grpc
文件夹中生成helloworld.proto对应消息
的Java代码,在target/generated-sources/protobuf/grpc-java/cn/net/dev/grpc
文件夹中生成helloworld.proto对应服务
的Java代码。如果想把这些代码的生成路径放在源码(src目录)中,则打开outputDirectory
注释就行,注意clearOutputDirectory
要设置为false,避免清空其他代码。我看了很多例子,很少由把代码生成到源码目录的,所以我也打算保持这个习惯。
创建Server端
先创建一个gRPC Server
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
| package cn.net.dev.grpc.server;
import cn.net.dev.grpc.GreeterGrpc; import cn.net.dev.grpc.HelloReply; import cn.net.dev.grpc.HelloRequest; import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.util.logging.Logger;
public class HelloWorldServer { private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName()); private int port = 50051; private Server server;
public HelloWorldServer() { }
private void start() throws IOException { this.server = ServerBuilder.forPort(this.port).addService(new GreeterImpl()).build().start(); logger.info("Server started, listening on " + this.port); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { System.err.println("*** shutting down gRPC server since JVM is shutting down"); HelloWorldServer.this.stop(); System.err.println("*** server shut down"); } }); }
private void stop() { if (this.server != null) { this.server.shutdown(); }
}
private void blockUntilShutdown() throws InterruptedException { if (this.server != null) { this.server.awaitTermination(); }
}
public static void main(String[] args) throws IOException, InterruptedException { HelloWorldServer server = new HelloWorldServer(); server.start(); server.blockUntilShutdown(); }
static class GreeterImpl extends GreeterGrpc.GreeterImplBase { GreeterImpl() { }
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } } }
|
创建Client端
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
| package cn.net.dev.grpc.client;
import cn.net.dev.grpc.GreeterGrpc; import cn.net.dev.grpc.HelloReply; import cn.net.dev.grpc.HelloRequest; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger;
public class HelloWorldClient { private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName()); private final ManagedChannel channel; private final GreeterGrpc.GreeterBlockingStub blockingStub;
public HelloWorldClient(String host, int port) { this.channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); this.blockingStub = GreeterGrpc.newBlockingStub(this.channel); }
public void shutdown() throws InterruptedException { this.channel.shutdown().awaitTermination(5L, TimeUnit.SECONDS); }
public void greet(String name) { logger.info("Will try to greet " + name + " ..."); HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response; try { response = this.blockingStub.sayHello(request); } catch (StatusRuntimeException var5) { logger.log(Level.WARNING, "RPC failed: {0}", var5.getStatus()); return; }
logger.info("Greeting: " + response.getMessage()); }
public static void main(String[] args) throws Exception { HelloWorldClient client = new HelloWorldClient("localhost", 50051);
try { String user = "world"; if (args.length > 0) { user = args[0]; }
client.greet(user); } finally { client.shutdown(); }
} }
|
由于protobuf的代码生产到target目录了,所以刚开始我一直找不到包,但可以点击过去,在我重启idea以后这个问题解决了,有这类问题的可以试试这个办法。
然后先执行服务端,再执行客户端,客户端会收到如下信息:
1 2 3 4 5 6 7 8
| Connected to the target VM, address: '127.0.0.1:61011', transport: 'socket' 3月 19, 2023 11:16:59 上午 cn.net.dev.grpc.client.HelloWorldClient greet 信息: Will try to greet world ... 3月 19, 2023 11:17:00 上午 cn.net.dev.grpc.client.HelloWorldClient greet 信息: Greeting: Hello world Disconnected from the target VM, address: '127.0.0.1:61011', transport: 'socket'
Process finished with exit code 0
|
项目结构
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
| D:. │ pom.xml ├─src │ ├─main │ │ ├─java │ │ │ ├─cn │ │ │ │ └─net │ │ │ │ └─dev │ │ │ │ └─grpc │ │ │ │ ├─client │ │ │ │ │ HelloWorldClient.java │ │ │ │ │ │ │ │ │ └─server │ │ │ │ HelloWorldServer.java │ │ │ │ │ │ │ └─org │ │ │ └─example │ │ │ App.java │ │ │ │ │ └─resources │ │ └─proto │ │ helloworld.proto │ │ │ └─test │ └─java │ └─org │ └─example │ AppTest.java │ └─target │ ├─generated-sources │ ├─annotations │ └─protobuf │ ├─grpc-java │ │ └─cn │ │ └─net │ │ └─dev │ │ └─grpc │ │ GreeterGrpc.java │ │ │ └─java │ └─cn │ └─net │ └─dev │ └─grpc │ HelloReply.java │ HelloReplyOrBuilder.java │ HelloRequest.java │ HelloRequestOrBuilder.java │ HelloWorldServiceProto.java │ │ └─test-classes └─org └─example AppTest.class
|