天天看點

Java 啟動rpc server_gRPC建立Java RPC服務

1.說明

本文介紹使用gRPC建立Java版本的RPC服務,

包括通過.proto檔案生成Java代碼的方法,

以及服務端和用戶端代碼使用示例。

2.建立生成代碼工程

建立Maven工程,grpc-compile。

2.1.修改pom.xml

引入生成代碼需要的jar包依賴,

以及建構依賴配置:

io.grpc

grpc-protobuf

1.32.1

io.grpc

grpc-stub

1.32.1

kr.motd.maven

os-maven-plugin

1.6.2

org.xolstice.maven.plugins

protobuf-maven-plugin

0.6.1

com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}

grpc-java

io.grpc:protoc-gen-grpc-java:1.32.1:exe:${os.detected.classifier}

compile

compile-custom

2.2.建立.proto檔案

在src\main下面建立proto目錄,

在裡面建立helloworld.proto檔案:

// 顯示聲明使用proto3, 否則使用預設的proto2

syntax = "proto3";

// 生成類的包名

option java_package = "com.asiainfo.yuwen.grpc.helloworld";

// 生成類的檔案名,否則預設生成的類名為proto檔案名的駝峰命名

option java_outer_classname = "HelloWorldProto";

// 定義的所有消息、枚舉和服務生成對應的多個類檔案,而不是以内部類的形式出現

option java_multiple_files = false;

// greeting服務定義

service Greeter {

// sayHello方法,格式為"方法名 請求參數 傳回參數"

rpc SayHello (HelloRequest) returns (HelloReply) {}

// 另一個sayHello方法

rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}

}

// 方法請求,包含使用者名

message HelloRequest {

string name = 1;

}

// 方法響應,包含響應的消息

message HelloReply {

string message = 1;

}

2.3.生成Java代碼

在grpc-compile下執行Maven編譯指令:

mvn clean compile

會在target目錄下生成代碼:

Java 啟動rpc server_gRPC建立Java RPC服務

主要關注generated-sources目錄:

generated-sources\protobuf\grpc-java目錄下生成了

com\asiainfo\yuwen\grpc\helloworld\GreeterGrpc.java;

generated-sources\protobuf\java目錄下生成了

com\asiainfo\yuwen\grpc\helloworld\HelloWorldProto.java。

其中GreeterGrpc.java對應greeting服務定義,

如果有多個服務,會對應生成多個java類,

HelloWorldProto.java對應消息、枚舉等對象的定義。

由于生成的代碼内容太多了,這裡就不貼出來了。

3.建立服務端工程

建立Maven工程,grpc-server。

3.1.修改pom.xml

引入使用gRPC需要依賴的jar包:

io.grpc

grpc-netty-shaded

1.32.1

io.grpc

grpc-protobuf

1.32.1

io.grpc

grpc-stub

1.32.1

3.2.拷貝生成的代碼

把上面生成的Java代碼,

GreeterGrpc.java和HelloWorldProto.java,

拷貝到grpc-server的src/main/java目錄下,

可以從com這一層目錄開始拷貝,

這樣就不用再手動建立包路徑了。

3.3.建立greeting服務實作類

為了實作greeting服務定義的兩個sayHello方法,

需要繼承GreeterGrpc.GreeterImplBase接口,

建立實作類GreeterImpl.java:

package com.asiainfo.yuwen.grpc.helloworld.controller.impl;

import com.asiainfo.yuwen.grpc.helloworld.GreeterGrpc;

import com.asiainfo.yuwen.grpc.helloworld.HelloWorldProto.HelloReply;

import com.asiainfo.yuwen.grpc.helloworld.HelloWorldProto.HelloRequest;

import io.grpc.stub.StreamObserver;

/**

*

* greeting服務實作類

*

*/

public class GreeterImpl extends GreeterGrpc.GreeterImplBase {

@Override

public void sayHello(HelloRequest req, StreamObserver responseObserver) {

HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();

responseObserver.onNext(reply);

responseObserver.onCompleted();

}

@Override

public void sayHelloAgain(HelloRequest req, StreamObserver responseObserver) {

HelloReply reply = HelloReply.newBuilder().setMessage("Hello again " + req.getName()).build();

responseObserver.onNext(reply);

responseObserver.onCompleted();

}

}

3.4.建立服務端啟動類

為了對外提供gRPC服務,

需要建立服務端啟動類GRPCServer.java:

package com.asiainfo.yuwen.grpc.server;

import java.io.IOException;

import java.util.concurrent.TimeUnit;

import com.asiainfo.yuwen.grpc.helloworld.controller.impl.GreeterImpl;

import io.grpc.Server;

import io.grpc.ServerBuilder;

public class GRPCServer {

private Server server;

private void start() throws IOException {

// 服務運作端口

int port = 50051;

// 注冊對外提供的服務

server = ServerBuilder.forPort(port).addService(new GreeterImpl()).build().start();

System.out.println("Server started, listening on " + port);

Runtime.getRuntime().addShutdownHook(new Thread() {

@Override

public void run() {

// 使用标準錯誤輸出,因為日志記錄器有可能在JVM關閉時被重置

System.err.println("*** shutting down gRPC server since JVM is shutting down");

try {

GRPCServer.this.stop();

} catch (InterruptedException e) {

e.printStackTrace(System.err);

}

System.err.println("*** server shut down");

}

});

}

private void stop() throws InterruptedException {

if (server != null) {

server.shutdown().awaitTermination(30, TimeUnit.SECONDS);

}

}

private void blockUntilShutdown() throws InterruptedException {

if (server != null) {

server.awaitTermination();

}

}

public static void main(String[] args) throws IOException, InterruptedException {

final GRPCServer server = new GRPCServer();

server.start();

server.blockUntilShutdown();

}

}

3.5.服務端啟動

通過執行GRPCServer的main方法,

就能啟動服務了,

啟動成功列印日志:

Server started, listening on 50051

4.建立服務端工程

建立Maven工程,grpc-client。

4.1.修改pom.xml

操作步驟和上面服務端相同。

4.2.拷貝生成的代碼

操作步驟和上面服務端相同。

由于用戶端和服務端都依賴了生成的代碼,

以及依賴相同的jar包,

可以提取到common公共工程裡面,

這樣友善其他工程依賴和後續修改。

4.3.建立用戶端調用類

建立一個簡單的用戶端,

用于測試greeting服務,

建立GRPCClient.java:

package com.asiainfo.yuwen.grpc.client;

import java.util.concurrent.TimeUnit;

import com.asiainfo.yuwen.grpc.helloworld.GreeterGrpc;

import com.asiainfo.yuwen.grpc.helloworld.HelloWorldProto.HelloReply;

import com.asiainfo.yuwen.grpc.helloworld.HelloWorldProto.HelloRequest;

import io.grpc.Channel;

import io.grpc.ManagedChannel;

import io.grpc.ManagedChannelBuilder;

import io.grpc.StatusRuntimeException;

public class GRPCClient {

private final GreeterGrpc.GreeterBlockingStub greeterBlocStub;

public GRPCClient(Channel channel) {

// 'channel'在這裡是Channel,而不是ManagedChannel,所有它負責關閉

// 向代碼傳遞通道可以使代碼更容易測試,也可以更容易地重用通道。

// 同時這裡建立的是同步(阻塞)RPC服務。

greeterBlocStub = GreeterGrpc.newBlockingStub(channel);

}

public void greet(String name) {

System.out.println("Will try to greet " + name + " ...");

HelloRequest request = HelloRequest.newBuilder().setName(name).build();

HelloReply response;

try {

response = greeterBlocStub.sayHello(request);

} catch (StatusRuntimeException e) {

System.out.println("WARNING, RPC failed: Status=" + e.getStatus());

return;

}

System.out.println("Greeting: " + response.getMessage());

}

public static void main(String[] args) throws Exception {

String user = "world";

// 通路本機在50051端口上運作的服務

String target = "localhost:50051";

// 指令行用法幫助,允許将使用者名和目标伺服器作為指令行參數傳入。

if (args.length > 0) {

if ("--help".equals(args[0])) {

System.err.println("Usage: [name [target]]");

System.err.println("");

System.err.println(" name The name you wish to be greeted by. Defaults to " + user);

System.err.println(" target The server to connect to. Defaults to " + target);

System.exit(1);

}

user = args[0];

}

if (args.length > 1) {

target = args[1];

}

// 建立到伺服器的通信通道,通道是線程安全的和可重用的。

// 通常在應用程式開始時建立通道,并重用直到應用程式關閉。

ManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build();

try {

GRPCClient client = new GRPCClient(channel);

client.greet(user);

} finally {

// ManagedChannel使用像線程和TCP連接配接這樣的資源。

// 為了防止洩漏這些資源,通道應該在不再使用時關閉。

channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);

}

}

}

4.4.用戶端啟動

通過執行GRPCClient的main方法,

就能啟動用戶端了,

通過gRPC調用服務端成功後列印日志:

Will try to greet world ...

Greeting: Hello world

5.參考文檔