RPC之gRPC的使用(Java+Nodejs)

本文介紹在Java中使用gRPC的過程。一般來說,主要包含以下的三個步驟
1)在.proto文件中定義提供的服務
2)使用protocol buffer編譯器編譯文件
3)使用gRPC API來創建服務端和客戶端,並進行調用。

下載和安裝需要的軟件

1)Protocol Buffers
結構化數據序列化機制
https://github.com/protocolbuffers/protobuf/releases
使用示例:

protoc --java_out=./ *.proto

2)protoc-gen-grpc-java
用於生成RPC通信代碼
http://jcenter.bintray.com/io/grpc/protoc-gen-grpc-java/
使用示例【使用Protobuf-Plugin機制】:

protoc --plugin=protoc-gen-grpc-java=protoc-gen-grpc-java-1.19.0-windows-x86_64.exe --grpc-java_out=./ *.proto

使用Maven構建gRPC

1)修改pmo.xml

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <protobuf.version>3.7.0</protobuf.version>
        <grpc.version>1.19.0</grpc.version>
    </properties>

    <dependencies>      
        <dependency>
          <groupId>io.grpc</groupId>
          <artifactId>grpc-netty-shaded</artifactId>
          <version>${grpc.version}</version>
        </dependency>
        <dependency>
          <groupId>io.grpc</groupId>
          <artifactId>grpc-protobuf</artifactId>
          <version>${grpc.version}</version>
        </dependency>
        <dependency>
          <groupId>io.grpc</groupId>
          <artifactId>grpc-stub</artifactId>
          <version>${grpc.version}</version>
        </dependency>
        <!-- 確保運行時的版本號與protoc的版本號匹配(或更新) -->
        <dependency>
          <groupId>com.google.protobuf</groupId>
          <artifactId>protobuf-java</artifactId>
          <version>${protobuf.version}</version>
        </dependency>
        <!-- 如果您想使用像JsonFormat這樣的特性 ,則添加以下依賴-->
        <dependency>
          <groupId>com.google.protobuf</groupId>
          <artifactId>protobuf-java-util</artifactId>
          <version>${protobuf.version}</version>
        </dependency>
    </dependencies>
    <build>
        <extensions>
            <extension>
                <!-- 該插件的說明參考 https://github.com/trustin/os-maven-plugin -->
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.1</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <!-- proto文件的所在路徑 -->
                    <!-- <protoSourceRoot>${project.basedir}/src/main/resources/proto</protoSourceRoot> -->
                    <!-- 編譯後java文件的輸出路徑,默認爲${project.build.directory}/generated-sources/protobuf/java -->
                    <!--<outputDirectory> ${project.build.directory}/generated-sources/protobuf/java</outputDirectory> -->
                    <!-- 制定protoc編譯器路徑 -->
                    <!-- <protocExecutable></protocExecutable> -->
                    <!-- 定義生成的java文件輸出路徑 -->
                    <!--<outputDirectory>${project.build.sourceDirectory}</outputDirectory> -->
                    <!--設置是否在生成java文件之前清空outputDirectory的文件,默認值爲true,設置爲false時也會覆蓋同名文件 -->
                    <clearOutputDirectory>false</clearOutputDirectory>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

默認情況下,該插件會查找 src/main/proto路徑下的proto文件。該在路徑下的任何子文件夾都會作爲proto文件中使用的包路徑。

2)在src/main/proto下創建proto文件(HarLog.proto)

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.ultrapower.ioss.proto";
option java_outer_classname = "HarLogProto";

package com.ultrapower.grpc.har;

message HarLogResovleRequest{
    string url = 1 ;
    string file_name = 2;
    string file_dir = 3;
    string context = 4;
}

message HarLogResovleResponse{
    int32 code = 1 ;
}

service HarLogService{
    rpc ResovleHarLog(HarLogResovleRequest) returns (HarLogResovleResponse);
    rpc BatchResovleHarLog(HarLogResovleRequest) returns (HarLogResovleResponse);

}

3)編譯文件

mvn protobuf:compile 
或
mvn compile

在target\generated-sources\protobuf下會生成以下兩個文件夾
RPC之gRPC的使用(Java+Nodejs)
其中java文件夾下面包含了我們定義的message,而grpc-java下存放的是服務端和客戶端都要使用的service。
基於上面的proto文件,生成了兩個java文件

1,HarLogServiceGrpc.java   位於grpc-java下
2,HarLogProto.java等其他文件位於java下

4)服務端服務實現類代碼

package com.ultrapower.ioss.proto;

public class HarLogServiceImpl extends HarLogServiceGrpc.HarLogServiceImplBase {

    @Override
    public void resovleHarLog(com.ultrapower.ioss.proto.HarLogResovleRequest request,
            io.grpc.stub.StreamObserver<com.ultrapower.ioss.proto.HarLogResovleResponse> responseObserver) {
        System.out.println(request.getUrl());
        System.out.println(request.getFileName());
        System.out.println(request.getFileDir());
        System.out.println(request.getContext());

        responseObserver.onNext(HarLogResovleResponse.newBuilder().setCode(200).build());
        responseObserver.onCompleted();
    }

    @Override
    public void batchResovleHarLog(com.ultrapower.ioss.proto.HarLogResovleRequest request,
            io.grpc.stub.StreamObserver<com.ultrapower.ioss.proto.HarLogResovleResponse> responseObserver) {
        responseObserver.onNext(HarLogResovleResponse.newBuilder().setCode(200).build());
        responseObserver.onCompleted();
    }
}

5)服務端啓動類

package com.ultrapower.ioss.proto;

import java.io.IOException;

import io.grpc.Server;
import io.grpc.ServerBuilder;

public class RpcServerApp {

    private Server server;
    private int port = 50051;

    public void start() throws IOException {
        server = ServerBuilder.forPort(port)
                .addService(new HarLogServiceImpl())
                .build()
                .start();

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                RpcServerApp.this.stop();
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static void main(String[] args) throws Exception {
        final RpcServerApp server = new RpcServerApp();
        server.start();
        server.blockUntilShutdown();
    }

}

Nodejs端客戶端

1)添加依賴

cnpm install --save grpc-tools
cnpm install --save google-protobuf 
cnpm install --save grpc
cnpm install --save @grpc/proto-loader
cnpm install --save @grpc/grpc-js

2)客戶端代碼

var grpc = require('grpc');
var PROTO_PATH = __dirname + '/../grpc/protos/HarLog.proto';
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
//protoDescriptor + "文件中定義的package"
var harlog = protoDescriptor.com.ultrapower.grpc.har;

/*
    創建RPC客戶端
*/
function getGrpcClient(){
    //創建客戶端
    let client = new harlog.HarLogService('localhost:50051',grpc.credentials.createInsecure());
    return client ; 
}

/*
    請求JAVA端解析生成的某個HAR文件
*/
function resovleHarLog(request){
    let client = getGrpcClient() ;
    //請求數據
    /*
    let request = {
            url:'',
            file_name:'',
            file_dir:'',
            context:''
    } ;
    */
    client.resovleHarLog(request,function(error,response){
        //console.log('Greeting:', response.code);
        console.log('Greeting:', JSON.stringify(response));
    })
}
/*
    請求JAVA端解析指定目錄下所有的HAR文件
    @param dirPath 存放HAR文件的目錄路徑
*/
function batchResovleHarlog(request){
    let client = getGrpcClient() ;
    client.batchResovleHarLog(request,function(error,response){
        console.log('Greeting:', response.message);
    })
}

//測試
resovleHarLog({
            url:'www.baidu.com',
            file_name:'bai.jar',
            file_dir:'c://d',
            context:'dsfdfdsd'
    } ) ;

exports.resovleHarLog = resovleHarLog;
exports.batchResovleHarlog = batchResovleHarlog;

基於Java的客戶端

package com.ultrapower.ioss.proto;

import java.util.concurrent.TimeUnit;

import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;

public class Client {

    private final ManagedChannel channel;

    private final HarLogServiceGrpc.HarLogServiceBlockingStub blockingStub;

    /**
     * 創建服務端連接,並創建“樁”
     */
    public Client(String host, int port) {
        channel = NettyChannelBuilder.forAddress(host, port).negotiationType(NegotiationType.PLAINTEXT).build();
        blockingStub = HarLogServiceGrpc.newBlockingStub(channel);
    }

    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    /**
     * 向服務端發送請求
     */
    public void resovleHarLog() {
        try {
            HarLogResovleRequest request = HarLogResovleRequest.newBuilder().setUrl("www.baidu.com").setFileName("test")
                    .build();
            HarLogResovleResponse response = blockingStub.resovleHarLog(request);
            System.out.println("result from server: " + response.getCode());
        } catch (RuntimeException e) {
            System.out.println("RPC failed:" + e.getMessage());
            return;
        }
    }

    public static void main(String[] args) throws Exception {
        Client client = new Client("127.0.0.1", 50051);
        try {
            client.resovleHarLog();
        } finally {
            client.shutdown();
        }
    }
}

基於Nodejs的服務端

var grpc = require('grpc');
var PROTO_PATH = __dirname + '/../grpc/protos/HarLog.proto';
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
//protoDescriptor + "文件中定義的package"
var harlog = protoDescriptor.com.ultrapower.grpc.har;

/*
    遠程調用的resovleHarLog方法.
    @param call 請求, 通過call.request可以得到請求參數
    @param callback,調用完成後返回給調用端的結果
*/
function resovleHarLog(call, callback) {
    let req = call.request ;
    console.log(JSON.stringify(req)) ;
  //callback(null, {message: 'Hello ' + call.request.name});
  callback(null, {code: 300});
}

function batchResovleHarLog(call, callback) {
    let req = call.request ;
    console.log(JSON.stringify(req)) ;
  //callback(null, {message: 'Hello again, ' + call.request.name});
  callback(null, {code: 300});
}

/*
    創建並啓動服務端
*/
function startup() {
  var server = new grpc.Server();
  server.addProtoService(harlog.HarLogService.service,
                         {resovleHarLog: resovleHarLog, batchResovleHarLog: batchResovleHarLog});
  server.bind('0.0.0.0:50052', grpc.ServerCredentials.createInsecure());
  server.start();
}

//啓動服務
startup() ;

exports.startup = startup;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章