grpc 简介
grpc 是什么
grpc 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。
grpc 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
环境安装
-
首先安装 protobuf :
基于MAC环境,打开终端,执行如下命令:brew install protobuf
查看是否安装成功:
protoc --version
-
再编译安装 grpc-java 插件:
使用git下载源码:git clone https://github.com/grpc/grpc-java.git
进入源码 compiler 目录:
cd compiler
依次执行命令:
../gradlew java_pluginExecutable ../gradlew test ../gradlew install
可能需要翻墙,并执行成功为止,最后会生成插件 protoc-gen-grpc-java 。
使用示例
Maven 依赖
在服务端和客户端的 pom.xml 中添加相关依赖:
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.12.0</version>
</dependency>
编写服务端 proto 文件
hello.proto
syntax = "proto3";
option java_multiple_files = false;
option java_package = "com.hans.grpcserver.grpc";
option java_outer_classname = "HelloProto";
package hello;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
使用 proto 工具生成 java 代码
protoc --java_out=. hello.proto
使用 grpc-java 工具生成 java 代码
注意插件地址为以上编译生成的实际地址
protoc --plugin=protoc-gen-grpc-java=/Users/huangxun/workspace/java/grpc-java/compiler/build/exe/java_plugin/protoc-gen-grpc-java --grpc-java_out=. --proto_path=. hello.proto
编写服务端 proto 文件
修改 option java_package = "com.hans.grpcclient.grpc"; 后再次执行以上命令则生成客户端 java 代码
将代码放到对应目录
即 com.hans.grpcserver 子目录 grpc 下
编写服务端
package com.hans.grpcserver.grpc.server;
import com.hans.grpcserver.grpc.GreeterGrpc;
import com.hans.grpcserver.grpc.HelloProto.HelloReply;
import com.hans.grpcserver.grpc.HelloProto.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 HelloServer {
private static final Logger logger = Logger.getLogger(HelloServer.class.getName());
private int port = 50051;
private Server server;
private void start() throws IOException {
// 使用ServerBuilder来构建和启动服务,通过使用forPort方法来指定监听的地址和端口
// 创建一个实现方法的服务GreeterImpl的实例,并通过addService方法将该实例纳入
// 调用build() start()方法构建和启动rpcserver
server = ServerBuilder.forPort(port)
.addService(new GreeterImpl())
.build()
.start();
logger.info("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
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();
}
}
/**
* Main launches the server from the command line.
*/
public static void main(String[] args) throws IOException, InterruptedException {
final HelloServer server = new HelloServer();
server.start();
server.blockUntilShutdown();
}
// 我们的服务GreeterImpl继承了生成抽象类GreeterGrpc.GreeterImplBase,实现了服务的所有方法
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
// 使用响应监视器的onNext方法返回HelloReply
responseObserver.onNext(reply);
// 使用onCompleted方法指定本次调用已经完成
responseObserver.onCompleted();
}
}
}
编写客户端
package com.hans.grpcclient.grpc.client;
import com.hans.grpcclient.grpc.GreeterGrpc;
import com.hans.grpcclient.grpc.HelloProto.HelloReply;
import com.hans.grpcclient.grpc.HelloProto.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 HelloClient {
private static final Logger logger = Logger.getLogger(HelloClient.class.getName());
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
/** Construct client connecting to HelloWorld server at {@code host:port}. */
// 首先,我们需要为stub创建一个grpc的channel,指定我们连接服务端的地址和端口
// 使用ManagedChannelBuilder方法来创建channel
public HelloClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext(true)
.build();
// 使用我们从proto文件生成的GreeterGrpc类提供的newBlockingStub方法指定channel创建stubs
blockingStub = GreeterGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
// 调用服务端方法
/** Say hello to server. */
public void greet(String name) {
logger.info("Will try to greet " + name + " ...");
// 创建并定制protocol buffer对象,使用该对象调用服务端的sayHello方法,获得response
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
// 如果有异常发生,则异常被编码成Status,可以从StatusRuntimeException异常中捕获
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}
logger.info("Greeting: " + response.getMessage());
}
/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting.
*/
public static void main(String[] args) throws Exception {
HelloClient client = new HelloClient("localhost", 50051);
try {
/* Access a service running on the local machine on port 50051 */
String user = "hans";
if (args.length > 0) {
user = args[0]; /* Use the arg as the name to greet if provided */
}
client.greet(user);
} finally {
client.shutdown();
}
}
}
启动服务端
后台打印输出:
Server started, listening on 50051
执行客户端
后台打印输出:
Will try to greet hans ...
Greeting: Hello hans