gRPC入門

1、gRPC簡介

在介紹gRPC以前先說一下RPC(Remote Procedure Call),也叫遠程過程調用協議,它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。相比HTTP協議來講,它主要是基於TCP/IP協議的的,傳輸效率更高,可以跨語言,典型的RPC框架有RMI、Hessian、Dubbo等。想要深刻了解它們之間區別的話能夠看這篇博客html


gRPC由 google 開發,是一款語言中立、平臺中立、開源的遠程過程調用(RPC)系統。具有如下特性:java

  1. 基於HTTP/2,HTTP/2 提供了鏈接多路複用、雙向流、服務器推送、請求優先級、首部壓縮等機制。能夠節省帶寬、下降TCP連接次數、節省CPU,幫助移動設備延長電池壽命等。gRPC 的協議設計上使用了HTTP2 現有的語義,請求和響應的數據使用HTTP Body 發送,其餘的控制信息則用Header 表示。
  2. IDL使用ProtoBuf ,gRPC使用ProtoBuf來定義服務,ProtoBuf是由Google開發的一種數據序列化協議(相似於XML、JSON、hessian)。ProtoBuf可以將數據進行序列化,並普遍應用在數據存儲、通訊協議等方面。壓縮和傳輸效率高,語法簡單,表達力強。
  3. 多語言支持(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java),gRPC支持多種語言,並可以基於語言自動生成客戶端和服務端功能庫。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它語言的版本正在積極開發中,其中,grpc支持C、C++、Node.js、Python、Ruby、Objective-C、PHP和C#等語言,grpc-java已經支持Android開發。

2、認識protocol buffers

2.1簡介

protocol buffers(簡稱protobuf),是由google開源的,在 RPC (遠程方法調用)裏很是流行的二進制編解碼格式,相似xml,json等。主要有如下幾個優勢:編程

  1. 性能好/效率高
  2. 代碼生成機制,好比能夠將proto文件編譯爲Java文件
  3. 支持多種編程語言
  4. 支持「向後兼容」和「向前兼容」

2.2語法

2.2.1 定義一個消息類型

syntax = "proto3";

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3;

}

第一行指定了使用proto3語法,若是沒有指定,編譯器會使用proto2。這個指定語法行必須是文件的非空非註釋的第一個行。json

SearchRequest至關於咱們Java中的一個實體類,裏面有三個字段分別爲String 類型query,int類型page_number和int類型result_per_page。服務器

後面的數字1,2,3是標識號,必須從1開始。這些標識符是用來在消息的二進制格式中識別各個字段的,一旦開始使用就不可以再改 變。注:[1,15]以內的標識號在編碼的時候會佔用一個字節。[16,2047]以內的標識號則佔用2個字節。因此應該爲那些頻繁出現的消息元素保留 [1,15]以內的標識號微信

字段的第一個是修飾符,必須是required,optional,repeated其中一個網絡

  1. required:一個格式良好的消息必定要含有1個這種字段。表示該值是必需要設置的;
  2. optional:表示可選的,能夠有值也能夠沒有。
  3. repeated:在一個格式良好的消息中,這種字段能夠重複任意屢次(包括0次)。重複的值的順序會被保留。表示該值能夠重複,至關於java中的List;

字段類型與Java類型對應關係以下圖:
enter description here框架

2.2.2 定義一個服務(service)

若是想要將消息類型用在RPC(遠程方法調用)系統中,能夠在.proto文件中定義一個RPC服務接口,protocol buffer編譯器將會根據所選擇的不一樣語言生成服務接口代碼及存根。如,想要定義一個RPC服務並具備一個方法,該方法可以接收 SearchRequest並返回一個SearchResponse,此時能夠在.proto文件中進行以下定義:maven

service SearchService {

  rpc Search (SearchRequest) returns (SearchResponse);

}

更多相關語法能夠查看這篇博客編程語言

3、完成Hello World

項目結構如圖:
enter description here

3.1新建項目

新建一個maven項目grpc_demo,而後在pom文件引入grpc和代碼生成插件,內容以下。

  1. <properties> 
  2. <grpc.version>1.0.3</grpc.version> 
  3. </properties> 
  4.  
  5. <dependencies> 
  6. <dependency> 
  7. <groupId>io.grpc</groupId> 
  8. <artifactId>grpc-netty</artifactId> 
  9. <version>${grpc.version}</version> 
  10. </dependency> 
  11. <dependency> 
  12. <groupId>io.grpc</groupId> 
  13. <artifactId>grpc-protobuf</artifactId> 
  14. <version>${grpc.version}</version> 
  15. </dependency> 
  16. <dependency> 
  17. <groupId>io.grpc</groupId> 
  18. <artifactId>grpc-stub</artifactId> 
  19. <version>${grpc.version}</version> 
  20. </dependency> 
  21. </dependencies> 
  22.  
  23. <build> 
  24. <extensions> 
  25. <extension> 
  26. <groupId>kr.motd.maven</groupId> 
  27. <artifactId>os-maven-plugin</artifactId> 
  28. <version>1.4.1.Final</version> 
  29. </extension> 
  30. </extensions> 
  31. <plugins> 
  32. <plugin> 
  33. <groupId>org.xolstice.maven.plugins</groupId> 
  34. <artifactId>protobuf-maven-plugin</artifactId> 
  35. <version>0.5.0</version> 
  36. <configuration> 
  37. <protocArtifact>com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}</protocArtifact> 
  38. <pluginId>grpc-java</pluginId> 
  39. <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact> 
  40. </configuration> 
  41. <executions> 
  42. <execution> 
  43. <goals> 
  44. <goal>compile</goal> 
  45. <goal>compile-custom</goal> 
  46. </goals> 
  47. </execution> 
  48. </executions> 
  49. </plugin> 
  50. </plugins> 
  51. </build> 

3.2編寫proto文件

syntax = "proto3";


option java_multiple_files = true;
//定義包名
option java_package = "cn.sp.helloworld";
//定義生成的類名稱
option java_outer_classname = "HelloWorldProto";

option objc_class_prefix = "HLW";



package helloworld;

// The greeting service definition.

service Greeter {
  
    // Sends a greeting
  
    rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1;

}


// The response message containing the greetings

message HelloReply {
  
    string message = 1; } 

運行命令mvn clean compile,就能夠看到編譯後生成的Java文件。
控制檯輸出:
enter description here
目錄結構
enter description here
將這些代碼複製到src/main/java/cn/sp目錄下

3.3完成HelloWorldClient和HelloWorldServer

客戶端代碼

  1. package cn.sp.client; 
  2.  
  3. import cn.sp.GreeterGrpc; 
  4. import cn.sp.HelloReply; 
  5. import cn.sp.HelloRequest; 
  6. import io.grpc.ManagedChannel; 
  7. import io.grpc.ManagedChannelBuilder; 
  8. import io.grpc.StatusRuntimeException; 
  9.  
  10. import java.util.concurrent.TimeUnit; 
  11.  
  12. public class HelloWorldClient
  13.  
  14. //一個gRPC信道 
  15. private final ManagedChannel channel; 
  16.  
  17.  
  18. private final GreeterGrpc.GreeterBlockingStub blockingStub;//阻塞/同步 存根 
  19.  
  20. //初始化信道和存根 
  21. public HelloWorldClient(int port,String host)
  22. this(ManagedChannelBuilder.forAddress(host,port).usePlaintext(true)); 
  23.  
  24. private HelloWorldClient(ManagedChannelBuilder<?> channelBuilder)
  25. channel = channelBuilder.build(); 
  26. blockingStub = GreeterGrpc.newBlockingStub(channel); 
  27.  
  28. public void shutDown()throws InterruptedException
  29. channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); 
  30.  
  31. //客戶端方法 
  32. public void greet(String name)
  33. HelloRequest request = HelloRequest.newBuilder().setName(name).build(); 
  34. HelloReply response; 
  35. try
  36. response = blockingStub.sayHello(request); 
  37. }catch (StatusRuntimeException e){ 
  38. System.out.println("RPC調用失敗:"+e.getMessage()); 
  39. return
  40.  
  41. System.out.println("服務器返回信息:"+response.getMessage()); 
  42.  
  43. public static void main(String[] args)throws Exception
  44. HelloWorldClient client = new HelloWorldClient(50051,"127.0.0.1"); 
  45. try
  46. for (int i=0;i<5;i++){ 
  47. client.greet("world:"+i); 
  48. }finally
  49. client.shutDown(); 
  50.  
  51.  
  52.  

服務器端代碼

  1. package cn.sp.server; 
  2.  
  3. import cn.sp.GreeterGrpc; 
  4. import cn.sp.HelloReply; 
  5. import cn.sp.HelloRequest; 
  6. import io.grpc.Server; 
  7. import io.grpc.ServerBuilder; 
  8. import io.grpc.stub.StreamObserver; 
  9.  
  10. public class HelloWorldServer
  11.  
  12.  
  13. private int port = 50051
  14. private Server server; 
  15.  
  16. /** 
  17. * 啓動服務 
  18. * @throws Exception 
  19. */ 
  20. private void start()throws Exception
  21. server = ServerBuilder.forPort(port) 
  22. .addService(new GreeterImpl()) 
  23. .build().start(); 
  24. System.out.println("service start ...."); 
  25. Runtime.getRuntime().addShutdownHook(new Thread(){ 
  26. @Override 
  27. public void run()
  28. System.err.println("*** shutting down gRPC server since JVM is shutting down"); 
  29. HelloWorldServer.this.stop(); 
  30. System.err.println("*** server shut down"); 
  31. }); 
  32.  
  33. private void stop()
  34. if (server != null){ 
  35. server.shutdown(); 
  36. // block 一直到程序退出 
  37. private void blockUntilShutDown()throws InterruptedException
  38. if (server != null){ 
  39. server.awaitTermination(); 
  40.  
  41. // 定義一個實現服務接口的類 
  42. private class GreeterImpl extends GreeterGrpc.GreeterImplBase
  43.  
  44. public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver)
  45. System.out.println("收到的信息:"+req.getName()); 
  46.  
  47. //這裏能夠放置具體業務處理代碼 start 
  48. //這裏能夠放置具體業務處理代碼 end 
  49.  
  50. //構造返回 
  51. HelloReply reply = HelloReply.newBuilder().setMessage("Hello: " + req.getName()).build(); 
  52. responseObserver.onNext(reply); 
  53. responseObserver.onCompleted(); 
  54.  
  55. public static void main(String[] args)throws Exception
  56. HelloWorldServer server = new HelloWorldServer(); 
  57. server.start(); 
  58. server.blockUntilShutDown(); 
  59.  

3.4運行測試

先運行服務端的main方法,再運行客戶端,能夠看到服務端控制檯輸出信息以下:

service start ....
收到的信息:world:0
收到的信息:world:1
收到的信息:world:2
收到的信息:world:3
收到的信息:world:4

客戶端控制檯輸出信息:

服務器返回信息:Hello: world:0
服務器返回信息:Hello: world:1
服務器返回信息:Hello: world:2
服務器返回信息:Hello: world:3
服務器返回信息:Hello: world:4

說明客戶端發出的五次問候請求服務端都接收到了,而且返回了響應。

4、總結

gRPC中proto文件的編寫就顯得十分重要了,要多加註釋至關於接口文檔,目前代碼調用過程看着貌似有些複雜,後面整合SpringBoot以後就很簡單了,不過我仍是喜歡HTTP。。。。

相關文章
相關標籤/搜索