gRPC简单示例
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
依赖:
<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
路径下)
// hello_world.proto
syntax = "proto3";
option java_multiple_files = true; // 每个message类型是否生成独立的文件
option java_outer_classname = "HelloWordProto"; // 当java_multiple_files=false时生成的多个message类的包装类名
option java_package = "org.chench.extra.java.grpc.proto"; // 生成的java文件所在包名
message HelloRequest { // 通过message声明一个实体类
string greeting = 1; // 类对象属性
}
message HelloResponse {
string reply = 1;
}
service HelloService { // 通过service声明rpc类
rpc SayHello (HelloRequest) returns (HelloResponse); // rpc方法
}
编译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插件编译:
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<!-- 使用如下插件编译proto文件 -->
<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>
<!-- 指定proto文件位置 -->
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
<!-- 指定proto文件编译后生成的java文件位置 -->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<!--在执行mvn compile的时候会执行以下操作-->
<phase>compile</phase>
<goals>
<!--生成OuterClass类-->
<goal>compile</goal>
<!--生成Grpc类-->
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在项目根目录下执行:mvn compile
即可生成对应的java类。
简单rpc示例
服务端
// HelloWorldServer.java
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();
}
}
// 服务端实现rpc接口
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();
}
}
}
客户端
// HelloWorldClient.java
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);
}
}
}
// 调用远程RPC接口
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、镜像的方式
作者:编程随笔
出处:http://www.cnblogs.com/nuccch/
声明:本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接。