grpc簡單教程

什麼啊什麼Q發表於2020-11-03

簡介:

  grpc是谷歌的一個開源的rpc(遠端服務呼叫)框架,可以讓各個語言按照指定的規則通過http2協議相互呼叫,這個規則是用Protocol Buffer(谷歌的一個資料描述語言)寫的一個.proto檔案,grpc的目的就是為了讓服務呼叫更方便。

目前支援的語言有C, C++,C#,Java, Node.js, Python,Go等,大部分語言都是通過外掛根據.proto檔案生成對應的程式碼,用生成好的程式碼,建立或呼叫grpc服務。

 

這是grpc的官方文件

 

   grpc的介面呼叫分為四類

    1.普通呼叫

    2.請求流呼叫

    3.響應流呼叫

    4.雙向流呼叫

 

從.proto檔案開始

常用的關鍵字
syntax指定語言版本
option修改配置選項
service宣告一個服務
rpc宣告一個方法
resturns方法的返回值
message定義一個訊息型別
repeated陣列
stream用流來互動

 


 

 這是proto的語法教程

 

一個例子:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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;

}

  

指定一個版本:

1

syntax = "proto3";

  

針對java的程式碼生成的一些配置

1

2

option java_package = "java_test";

option java_multiple_files = true;

  

用 message 定義了一個請求訊息,和一個返回訊息

1

2

3

4

5

6

7

8

9

10

11

message Request

{

    string request1 = 1;

    string request2 = 2;

}

 

message Result

{

    string result1 = 1;

    string result2 = 2;

}

  

用 service 宣告瞭一個服務,用 rpc 宣告一個方法

1

2

3

4

service TestService

{

    rpc method(Request) returns (Result){}

}

  

說正經的:

想使用grpc要先做一些配置

 

新增grpc的包

1

2

3

4

5

<dependency>

    <groupId>io.grpc</groupId>

    <artifactId>grpc-all</artifactId>

    <version>1.10.1</version>

</dependency>

  

新增編譯.proto檔案用的外掛

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<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檔案的編譯工具

1

2

3

4

5

6

<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對應的類

1

<protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact>

  

protoc-gen-grpc-java工具通過.proto檔案生成grpc的工具類

1

<pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact>

  

這是生成grpc工具類存放的資料夾的名字

1

<pluginId>grpc</pluginId>

  

要編輯的.proto檔案的路徑

1

<protoSourceRoot>src/main/resources/proto</protoSourceRoot>

  

這個是為下載上面工具用的,他可以提供一些變數,

os.detected.classifier變數可以根據當前系統的型別來下載對應的工具

1

2

3

4

5

<extension>

    <groupId>kr.motd.maven</groupId>

    <artifactId>os-maven-plugin</artifactId>

    <version>1.4.1.Final</version>

</extension>

  

這是上面兩個編譯工具用到的命令,當用maven編譯專案時會執行這兩個命令

1

2

<goal>compile</goal>

<goal>compile-custom</goal>

  

真的,說正經的:

用maven編譯一下

 

會生成兩個資料夾

  

java資料夾是protoc編譯工具生成的程式碼

grpc資料夾是protoc-gen-grpc-java編譯工具生成的工具類

 

這兩個檔案就是我們在.proto檔案中定義的訊息型別(經常被用到)

 

這兩個是為訊息型別的一個介面,裡面有get方法(不會被用到)

 

 這個是對訊息的一個描述(更不會被用到)

 

這個是grpc的工具類(會被用到)

 

這次真的要說正經的了,我們要用這些grpc為我們生成出來的奇怪的東西,寫奇怪的東西了:

 

1.普通介面

 

1.1.服務端

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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方法

1

public class JavaGrpcServer extends TestServiceGrpc.TestServiceImplBase

  

TestServiceGrpc.TestServiceImplBase 就是我們在.proto檔案中定義的服務

 

用 ServerBuilder 的 forProt 方法來指定一個埠,用 addService 來新增一個服務類,也就是當前類

1

2

3

4

ServerBuilder.forPort(2)

                .addService(new JavaGrpcServer())

                .build()

                .start();

  

grpc生成的訊息類有點獨特,他們沒有set方法,只有get方法,想要賦值,要用他們的一個內部類Builder來間接賦值

1

Result result = Result.newBuilder().setResult1("result1").setResult2("result2").build();

  

新增返回值,完成呼叫

1

2

responseObserver.onNext(result);

responseObserver.onCompleted();

StreamObserver(流觀察者) 這個介面會在後面詳細說,這裡只需要知道 onNext 是新增返回值,onCompleted 是完成呼叫即可

 

這裡利用了spring的 InitializingBean 介面和 Component 註解在bean初始化的時候建立服務

好了,服務端搞完了,下一個

 

1.2.客戶端

先寫一個叫 Functional 的函式式介面,方便呼叫

1

2

3

4

5

6

package com.gutousu.grpc_client_java_test;

 

public interface Functional<Arg,Result>

{

    Result run(Arg arg);

}

  

建一個叫 JavaGrpcClient 的類 來呼叫介面

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

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的意思是使用明文不加密(應該可以加密)

1

2

3

4

5

6

7

private Channel channel()

{

    return ManagedChannelBuilder

            .forAddress("192.168.0.31",2)

            .usePlaintext(true)

            .build();

}

  

用 TestServiceGrpc.newBlockingStub 來建立一個例項

1

2

TestServiceGrpc.TestServiceBlockingStub testServiceBlockingStub =

                TestServiceGrpc.newBlockingStub(channel);

  

 再搞一個測試

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

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));

    }

}

  

讓我們把這兩個專案跑起來,看一下

 

看!斷點經過了建立服務那裡,而且沒有報錯,服務端跑起來了!

 

 看!客戶端要!

 

他進來了,連線了服務端,建立了例項,馬上就要....

 

他帶著引數過來了,被斷點攔住了

 

 給他一個返回值,結束

 

 走你!

 

拿到了返回值,完結!撒花!

 

 

等等!

這只是普通的介面

還有三種介面呢!

 

相關文章