grpc 在java中的使用 ---1
簡介:
grpc是谷歌的一個開源的rpc(遠端服務呼叫)框架,可以讓各個語言按照指定的規則通過http2協議相互呼叫,這個規則是用Protocol Buffer(谷歌的一個數據描述語言)寫的一個.proto檔案,grpc的目的就是為了讓服務呼叫更方便,更快。
grpc的介面呼叫分為四類
1.普通呼叫
2.請求流呼叫
3.響應流呼叫
4.雙向流呼叫
從.proto檔案開始
syntax | 指定語言版本 |
option | 修改配置選項 |
service | 宣告一個服務 |
rpc | 宣告一個方法 |
resturns | 方法的返回值 |
message | 定義一個訊息型別 |
repeated | 陣列 |
stream | 用流來互動 |
這是proto的語法教程
https://www.jianshu.com/p/da7ed5914088
一個栗子:
syntax = "proto3"; option java_package = "java_test"; option java_multiple_files = true; service TestService { rpc method(Request) returns (Result){} } message Request { string request1 = 1; string request2 = 2; } message Result { string result1 = 1; string result2 = 2; }
指定一個版本:
syntax = "proto3";
針對java的程式碼生成的一些配置
option java_package = "java_test"; option java_multiple_files = true;
用 message 定義了一個請求訊息,和一個返回訊息
message Request { string request1 = 1; string request2 = 2; } message Result { string result1 = 1; string result2 = 2; }
用 service 聲明瞭一個服務,用 rpc 宣告一個方法
service TestService { rpc method(Request) returns (Result){} }
說正經的:
想使用grpc要先做一些配置
新增grpc的包
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.10.1</version> </dependency>
新增編譯.proto檔案用的外掛
<plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.0</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact> <pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact> <pluginId>grpc</pluginId> <protoSourceRoot>src/main/resources/proto</protoSourceRoot> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin>
新增.proto檔案的編譯工具
<configuration> <protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact> <pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact> <pluginId>grpc</pluginId> <protoSourceRoot>src/main/resources/proto</protoSourceRoot> </configuration>
protoc工具通過.proto檔案生成對應的java對應的類
<protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact>
protoc-gen-grpc-java工具通過.proto檔案生成grpc的工具類
<pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact>
這是生成grpc工具類存放的資料夾的名字
<pluginId>grpc</pluginId>
要編輯的.proto檔案的路徑
<protoSourceRoot>src/main/resources/proto</protoSourceRoot>
這個是為下載上面工具用的,他可以提供一些變數,
os.detected.classifier變數可以根據當前系統的型別來下載對應的工具
<extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.4.1.Final</version> </extension>
這是上面兩個編譯工具用到的命令,當用maven編譯專案時會執行這兩個命令
<goal>compile</goal> <goal>compile-custom</goal>
真的,說正經的:
用maven編譯一下
會生成兩個資料夾
java資料夾是protoc編譯工具生成的程式碼
grpc資料夾是protoc-gen-grpc-java編譯工具生成的工具類
這兩個檔案就是我們在.proto檔案中定義的訊息型別(經常被用到)
這兩個是為訊息型別的一個介面,裡面有get方法(不會被用到)
這個是對訊息的一個描述(更不會被用到)
這個是grpc的工具類(會被用到)
這次真的要說正經的了,我們要用這些grpc為我們生成出來的奇怪的東西,寫奇怪的東西了:
1.普通介面
1.1.服務端
package com.gutousu.grpc_service_java_test.service; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; import java_test.Request; import java_test.Result; import java_test.TestServiceGrpc; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; @Component public class JavaGrpcServer extends TestServiceGrpc.TestServiceImplBase implements InitializingBean { @Override public void method(Request request, StreamObserver<Result> responseObserver) { Result result = Result.newBuilder().setResult1("result1").setResult2("result2").build(); responseObserver.onNext(result); responseObserver.onCompleted(); } @Override public void afterPropertiesSet() throws Exception { ServerBuilder.forPort(2) .addService(new JavaGrpcServer()) .build() .start(); } }
首先建立一個服務類叫JavaGrpcServer 繼承 TestServiceGrpc.TestServiceImplBase 重寫裡面的method方法
public class JavaGrpcServer extends TestServiceGrpc.TestServiceImplBase
TestServiceGrpc.TestServiceImplBase 就是我們在.proto檔案中定義的服務
用 ServerBuilder 的 forProt 方法來指定一個埠,用 addService 來新增一個服務類,也就是當前類
ServerBuilder.forPort(2) .addService(new JavaGrpcServer()) .build() .start();
grpc生成的訊息類有點獨特,他們沒有set方法,只有get方法,想要賦值,要用他們的一個內部類Builder來間接賦值
Result result = Result.newBuilder().setResult1("result1").setResult2("result2").build();
新增返回值,完成呼叫
responseObserver.onNext(result); responseObserver.onCompleted();
StreamObserver(流觀察者) 這個介面會在後面詳細說,這裡只需要知道 onNext 是新增返回值,onCompleted 是完成呼叫即可
這裡利用了spring的 InitializingBean 介面和 Component 註解在bean初始化的時候建立服務
好了,服務端搞完了,下一個
1.2.客戶端
先寫一個叫 Functional 的函式式介面,方便呼叫
package com.gutousu.grpc_client_java_test; public interface Functional<Arg,Result> { Result run(Arg arg); }
建一個叫 JavaGrpcClient 的類 來呼叫介面
package com.gutousu.grpc_client_java_test.client; import com.gutousu.grpc_client_java_test.Functional; import io.grpc.Channel; import io.grpc.ManagedChannelBuilder; import java_test.TestServiceGrpc; import org.springframework.stereotype.Component; @Component public class JavaGrpcClient { private Channel channel = channel(); public <Result> Result run(Functional<TestServiceGrpc.TestServiceBlockingStub,Result> functional) { TestServiceGrpc.TestServiceBlockingStub testServiceBlockingStub = TestServiceGrpc.newBlockingStub(channel); return functional.run(testServiceBlockingStub); } private Channel channel() { return ManagedChannelBuilder .forAddress("192.168.0.31",2) .usePlaintext(true) .build(); } }
用 ManagedChannelBuilder 的 forAddress 方法來連線服務端,usePlaintext的意思是使用明文不加密(應該可以加密)
private Channel channel() { return ManagedChannelBuilder .forAddress("192.168.0.31",2) .usePlaintext(true) .build(); }
用 TestServiceGrpc.newBlockingStub 來建立一個例項
TestServiceGrpc.TestServiceBlockingStub testServiceBlockingStub = TestServiceGrpc.newBlockingStub(channel);
再搞一個測試
package com.gutousu.grpc_client_java_test; import com.gutousu.grpc_client_java_test.client.JavaGrpcClient; import java_test.Request; import java_test.Result; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class GrpcClientJavaTestApplicationTests { @Autowired private JavaGrpcClient javaGrpcClient; @Test public void contextLoads() { Request request = Request.newBuilder().setRequest1("test1").setRequest2("test2").build(); Result result = javaGrpcClient.run(o -> o.method(request)); } }
讓我們把這兩個專案跑起來,看一下
看!斷點經過了建立服務那裡,而且沒有報錯,服務端跑起來了!
看!客戶端要!
他進來了,連線了服務端,建立了例項,馬上就要....
他帶著引數過來了,被斷點攔住了
給他一個返回值,結束
走你!
拿到了返回值,完結!撒花!
等等!
這只是普通的介面
還有三種介面呢!
好,那繼續
2.請求流介面
等等!
部落格寫的有點長了,下一篇吧^_^
不要走開,廣告之後更精彩!