本文将一步步完成一个gRPC的示例。文章从如下几个部分来加以说明:
下面就一步步来完成每一个部分~
gRPC 默认使用 protocol buffers 作为接口定义语言,来描述服务接口和有效载荷消息结构。 更多的proto3文档信息,参考https://developers.google.com/protocol-buffers/docs/proto3
syntax = "proto3";
package com.xxx.tutorial.demo.grpc;
option java_multiple_files = true;
option java_package = "com.xxx.tutorial.demo.model";
option java_outer_classname = "Hello";
message HelloRequest{
string name = 1;
int32 id = 2;
}
message HelloResponse{
string message = 1;
}
syntax = "proto3";
package com.xxx.tutorial.demo.grpc;
option java_multiple_files = true;
option java_package = "com.xxx.tutorial.demo.service";
option java_outer_classname = "GreetingService";
import "hello.proto";
service HelloService{
rpc sayHello(HelloRequest) returns (HelloResponse);
}
可以访问网址【https://github.com/google/protobuf/releases】查看不同版本代码生成器,本文使用的版本为protoc-3.1.0-win32.zip,可以访问如下地址下载,
【https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-win32.zip】
F:\JavaDeveloper\rpc\gRpc\protoc-3.1.0-win32\bin>protoc.exe --java_out=./ *.proto
执行上述命令之后,在bin目录下会生成代码(包含包名), 如:
具体model和service包中的代码截图如下:
注意:
上述生成了消息对象等相关代码,但是还不包含rpc服务所需要的通信代码。
rpc通信代码需要protoc-gen-grpc-java 插件来完成,可以访问如下网址下载:【protoc-gen-grpc-java-0.13.2-windows-x86_64.exe】
将下载的protoc-gen-grpc-java-0.13.2-windows-x86_64.exe放到protoc-3.1.0-win32下的bin目录中。如:
执行如下语句即可~
protoc.exe --plugin=protoc-gen-grpc-java=protoc-gen-grpc-java-0.13.2-windows-x86_64.exe --grpc-java_out=./ *.proto
如:
F:\JavaDeveloper\rpc\gRpc\protoc-3.1.0-win32\bin>protoc.exe --java_out=./ *.proto
F:\JavaDeveloper\rpc\gRpc\protoc-3.1.0-win32\bin>protoc.exe --plugin=protoc-gen-grpc-java=protoc-gen-grpc-java-0.13.2-windows-x86_64.exe --grpc-java_out=./ *.proto
F:\JavaDeveloper\rpc\gRpc\protoc-3.1.0-win32\bin>
可以看到在service包中多了一个HelloServiceGrpc.java文件。
该文件在后续服务实现的时候需要用到。
至此,根据proto文件产生Java代码部分就完成了。接下来,要做的就是编写服务实现、服务器代码以及客户端调用接口的代码等~
grpc-demo这个Maven工程主要包含四个模块,
pom文件如下:
<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>com.xxx.tutorial</groupId>
<artifactId>grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>grpc-demo-server</module>
<module>grpc-demo-client</module>
<module>grpc-demo-interface</module>
<module>grpc-demo-service</module>
</modules>
</project>
Eclipse的工程目录结构如下:
直接将上述生成的代码,拷贝到src/main/java中,如
遇到“红叉叉”是因为,没有导入相关的jar包~ 在该模块下的pom.xml文件中添加相关的依赖包即可:
<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>
<parent>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>grpc-demo-interface</artifactId>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.grpc/grpc-stub -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</project>
服务接口模块搞定,接下来要完成接口的实现。
<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>
<parent>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>grpc-demo-service</artifactId>
<dependencies>
<dependency>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
package com.xxx.tutorial.demo.service.impl;
import java.util.logging.Logger;
import com.xxx.tutorial.demo.model.HelloRequest;
import com.xxx.tutorial.demo.model.HelloResponse;
import com.xxx.tutorial.demo.service.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;
public class GreetingServiceImpl implements HelloServiceGrpc.HelloService {
private static final Logger logger = Logger.getLogger(GreetingServiceImpl.class.getName());
public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
logger.info(String.format("sayHello方法调用的请求参数信息: name={%s}, id={%d}", request.getName(), request.getId()));
HelloResponse reply = HelloResponse.newBuilder().setMessage(String.format("Hello, %s", request.getName()))
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
<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>
<parent>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>grpc-demo-server</artifactId>
<dependencies>
<dependency>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
package com.xxx.tutorial.demo.server;
import java.io.IOException;
import java.util.logging.Logger;
import com.xxx.tutorial.demo.service.HelloServiceGrpc;
import com.xxx.tutorial.demo.service.impl.GreetingServiceImpl;
import io.grpc.Server;
import io.grpc.ServerBuilder;
public class HelloServer {
private static final Logger logger = Logger.getLogger(HelloServer.class.getName());
private static final int DEFAULT_PORT = 50051;
private Server server = null;
private void start() throws IOException {
server = ServerBuilder.forPort(DEFAULT_PORT).addService(HelloServiceGrpc.bindService(new GreetingServiceImpl()))
.build().start();
logger.info("Server started, listening on " + DEFAULT_PORT);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.err.println("*** shutting down gRPC server since JVM is shutting down");
HelloServer.this.stop();
System.err.println("*** server shut down");
}
});
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
/**
* Await termination on the main thread since the grpc library uses daemon
* threads.
*/
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
final HelloServer server = new HelloServer();
server.start();
server.blockUntilShutdown();
}
}
<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>
<parent>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>grpc-demo-service</artifactId>
<dependencies>
<dependency>
<groupId>com.xxx.tutorial</groupId>
<artifactId>grpc-demo-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
package com.xxx.tutorial.demo.client;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.xxx.tutorial.demo.model.HelloRequest;
import com.xxx.tutorial.demo.model.HelloResponse;
import com.xxx.tutorial.demo.service.HelloServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
public class GreetingServiceClient {
private static final Logger logger = Logger.getLogger(GreetingServiceClient.class.getName());
private final ManagedChannel channel;
private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub;
public GreetingServiceClient(String host, int port) {
channel = NettyChannelBuilder.forAddress(host, port).negotiationType(NegotiationType.PLAINTEXT).build();
blockingStub = HelloServiceGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public void sayHello(String name) {
try {
System.out.println("Will try to say Hello " + name + " ...");
HelloRequest request = HelloRequest.newBuilder().setName(name).setId(12345678).build();
HelloResponse response = blockingStub.sayHello(request);
System.out.println("result from server: " + response.getMessage());
} catch (RuntimeException e) {
System.out.println("RPC failed:" + e.getMessage());
return;
}
}
public static void main(String[] args) throws Exception {
GreetingServiceClient client = new GreetingServiceClient("127.0.0.1", 50051);
try {
String name = "Eric";
logger.info(String.format("Client 调用RPC接口,参数为name = {%s}", name));
client.sayHello(name);
} finally {
client.shutdown();
}
}
}
至此,Maven工程中,4个模块的代码就简单写完了。接下来,测试一下。
直接运行HelloServer.java(其中包含main函数)即可。
直接运行GreetingServiceClient.java(其中包含main函数)即可。
我们可以看到,客户端调用接口,传值name为Eric,得到服务端的相应为Hello, Eric。
五月 26, 2017 6:41:20 下午 com.xxx.tutorial.demo.client.GreetingServiceClient main
信息: Client 调用RPC接口,参数为name = {Eric}
Will try to say Hello Eric ...
result from server: Hello, Eric
再看一下,服务端的输出日志,也包含了请求参数,其中,id的值因为没有用到,所以在Service实现的sayHello接口,直接设置了12345678。
五月 26, 2017 6:40:14 下午 com.xxx.tutorial.demo.server.HelloServer start
信息: Server started, listening on 50051
五月 26, 2017 6:41:20 下午 com.xxx.tutorial.demo.service.impl.GreetingServiceImpl sayHello
信息: sayHello方法调用的请求参数信息: name={Eric}, id={12345678}
从上面测试运行的结果可以看出,一个简单的grpc示例就完成了~
其中, grpc支持四种RPC类型
本示例完成的就是第一种“简单RPC”的方式,客户端发出单个请求,获得单个响应。后续会对其它几种RPC进行展示。