gRPC之java語言的簡單Demo


最近由於項目需要,就簡單看了下gRPC入門,使用起來挺簡單的。這里就順便記錄一下,便於后面回顧。

RPC是什么

說到RPC(Remote Process Communication,遠程過程調用)就不得不說到進程間通信(Inter-process Communication,簡稱IPC),IPC是指多個進程之間傳送數據或信號的一些技術或方法。

而IPC又分為本地過程調用(LPC)和遠程過程調用(RPC),這兩者的區別就是 LPC的調用可以共享內存空間,比較方便;而RPC的調用雙方則不在同一個主機中,無法像LPC那樣方便。

說到底,RPC就是在本地來 調用遠程的方法。而RPC框架要做的就是 讓遠程調用 像本地調用一樣方便,而這個調用過程則是RPC框架需要做的工作。這個工作涉及到大概 兩個方面:序列化協議傳輸協議

1、常見的序列化協議有:
基於文本(text)的:XML、JSON
基於二進制(binary)的: Protocol Buffer、Thrift等

2、常見的傳輸協議有:
傳輸層的: TCP(基於Socket)、UDP
應用層的:HTTP1.1、HTTP2.0

而RPC框架中,通常使用的序列化協議包括Protocol Buffer、Thrift等,傳輸協議則常用TCP、HTTP2.0等。

更多的內容可以參考博客:【RPC簡介及框架選擇】

gRPC框架

上面簡單介紹了RPC機制的作用,現如今已經出現了許多優秀的RPC框架,比如:gRPC、Dubbo、Thrift等。這些框架在 序列化協議和傳輸協議兩部分都有不同的選擇,各有優劣。不過,沒必要全部都去學習一遍,可以簡單嘗試一種,大概了解運行機制即可。

下面是以gRPC的簡單使用為例,了解gRPC的運行過程和用法。

gRPC使用的序列化協議是 Protocol Buffer,使用的傳輸協議是 HTTP2.0協議。

需要清楚的是,
gRPC的通信方式需要先確定好proto文件,這個proto文件中定義了遠程api的具體服務接口、api方法的參數類型/返回值類型;
然后,將該proto文件編譯為 java類文件,這些類文件包含的就是 client端 和 server端都遵循的 遠程調用api的定義內容。
接着,client端 和 server端按照gRPC框架的方式來進行通信即可。

 

gRPC的java示例

具體步驟如下【參考源碼】:

1、創建一個maven項目,創建src/main/proto目錄,在其中添加定義好的遠程API接口hello.proto文件,如下:

// 使用該proto文件可以定義交互的服務接口,基於該文件編譯成的源文件可以分別復制到 client端和server端,便於兩者使用
​
syntax = "proto3"; // 定義語法類型
​
package hello; // 定義作用域
option java_multiple_files = false; // 表示下面的message不需要編譯成多個java文件
option java_outer_classname = "HelloMessage"; // 表示下面的message編譯成的java類文件的名字
option java_package = "grpc"; //指定該proto文件編譯成的java源文件的包名
​
service Hello {  // 定義服務
  rpc sayHello(HelloRequest) returns(HelloResponse) {}
}
​
message HelloRequest { // 定義請求的消息體
  string name = 1;
}
​
message HelloResponse { // 定義回復的消息體
  string message = 1;
}

 

2、在pom.xml文件中添加gRPC依賴與插件,參考grpc-java倉庫,如下:

依賴為: <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.31.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.31.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.31.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>annotations-api</artifactId>
            <version>6.0.53</version>
            <scope>provided</scope>
        </dependency>
    </dependencies> 插件為: <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.2</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.31.1:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin><plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin></plugins>
    </build>

 

3、執行 mvn clean compile 命令,然后在生成的 target/generated-sources/ptotobuf 文件夾中找到 服務接口對應的類文件接口參數/返回值對應的類文件

將這兩個文件分別復制到client端 和 server端的java源文件中。

 

4、編寫server端代碼,如下:

public class ServerDemo {
​
    // 定義一個Server對象,監聽端口來獲取rpc請求,以進行下面的處理
    private Server server;
​
    //使用main方法來測試server端
    public static void main(String[] args) throws IOException, InterruptedException {
​
        final ServerDemo serverDemo = new ServerDemo();
​
        //啟動server
        serverDemo.start();
​
        //block 一直到退出程序
        serverDemo.blockUntilShutdown();
    }
​
    /**
     * 啟動一個Server實例,監聽client端的請求並處理
     * @throws IOException
     */
    private void start() throws IOException {
​
        //server運行在的端口號
        int port = 50051;
​
        // 給server添加監聽端口號,添加 包含業務處理邏輯的類,然后啟動
        server = ServerBuilder.forPort(port)
                .addService(new HelloImpl())
                .build()
                .start();
​
    }
​
    /**
     * 阻塞server直到關閉程序
     * @throws InterruptedException
     */
    private void blockUntilShutdown() throws InterruptedException {
​
        if (server != null) {
            server.awaitTermination();
        }
​
    }
​
​
    /**
     * proto文件被編譯后,在生成的HelloGrpc的抽象內部類HelloImplBase中包含了 proto中定義的服務接口的簡單實現
     * 該HelloImpl類需要重寫這些方法,添加需要的處理邏輯
     */
    static class HelloImpl extends HelloGrpc.HelloImplBase {
​
        // proto文件中的sayHello服務接口被編譯后,在生成的HelloGrpc的抽象內部類HelloImplBase中有一個簡單的實現
        // 因此,在server端需要重寫這個方法,添加上相應的邏輯
        @Override
        public void sayHello(HelloMessage.HelloRequest req, StreamObserver<HelloMessage.HelloResponse> responseObserver) {
​
            HelloMessage.HelloResponse reply = HelloMessage.HelloResponse.newBuilder().setMessage("(server端的sayHello()方法處理結果) Hello," + req.getName()).build();
​
​
            // 調用onNext()方法來通知gRPC框架把reply 從server端 發送回 client端
            responseObserver.onNext(reply);
​
            // 表示完成調用
            responseObserver.onCompleted();
        }
    }
​
}

 

5、編寫client端代碼,如下:

public class ClientDemo {
​
    //使用main方法來測試client端
    public static void main(String[] args) throws Exception {
​
        ClientDemo clientDemo = new ClientDemo();
​
        try {
​
            //基於gRPC遠程調用對應的方法
            clientDemo.remoteCall("【zhongyuan】");
​
        } finally {
​
        }
    }
​
    /**
     * 基於gRPC框架的使用步驟,進行遠程調用
     * @param name
     */
    public void remoteCall(String name) {
​
        HelloMessage.HelloRequest request = HelloMessage.HelloRequest.newBuilder().setName(name).build();
        HelloMessage.HelloResponse response;
​
        try {
​
            // 基於訪問地址 創建通道
            Channel channel =  ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext().build();
​
            // 利用通道 創建一個樁(Stub)對象
            HelloGrpc.HelloBlockingStub blockingStub = HelloGrpc.newBlockingStub(channel);
​
            //通過樁對象來調用遠程方法
            response = blockingStub.sayHello(request);
​
        } catch (StatusRuntimeException e) {
            return;
        }
​
        System.out.println("client端遠程調用sayHello()的結果為:\n\n" + response.getMessage());
    }
​
}

 

6、接下來,先運行server端main()函數,再運行client端的main()函數,即可測試rpc遠程調用結果,如下:

 

 源碼詳見:https://github.com/zhongyuanzhao000/gRPC-demo

 

參考:

grpc官網:https://grpc.io/docs/languages/java/quickstart/

grpc官方文檔中文版:http://doc.oschina.net/grpc?t=60134

grpc-java倉庫:https://github.com/grpc/grpc-java

博客:

https://blog.csdn.net/sunsun314/article/details/73780169

https://blog.csdn.net/m0_38001814/article/details/108229637


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM