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