RPC是什么
說到RPC(Remote Process Communication,遠程過程調用)就不得不說到進程間通信(Inter-process Communication,簡稱IPC),IPC是指多個進程之間傳送數據或信號的一些技術或方法。
而IPC又分為本地過程調用(LPC)和遠程過程調用(RPC),這兩者的區別就是 LPC的調用可以共享內存空間,比較方便;而RPC的調用雙方則不在同一個主機中,無法像LPC那樣方便。
1、常見的序列化協議有: 基於文本(text)的:XML、JSON 基於二進制(binary)的: Protocol Buffer、Thrift等 2、常見的傳輸協議有: 傳輸層的: TCP(基於Socket)、UDP 應用層的:HTTP1.1、HTTP2.0
而RPC框架中,通常使用的序列化協議包括Protocol Buffer、Thrift等,傳輸協議則常用TCP、HTTP2.0等。
更多的內容可以參考博客:
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依賴與插件,參考
依賴為: <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
