1. 程式人生 > >GRPC學習總結

GRPC學習總結

一.概念普及:

protocolbuffers(protobuf):結構資料序列化機制

主要思想是:定義一個服務, 指定其可以被遠端呼叫的方法及其引數和返回型別。用來實現不同程序間的通訊。

gRPC 預設使用 protocol buffers,用 proto files 建立 gRPC 服務,用 protocol buffers 訊息型別來定義方法引數和返回型別。

GRPC:

Google 開發的基於HTTP/2和Protocol Buffer 3的RPC 框架

基於以下理念:定義一個服務,指定其能夠被遠端呼叫的方法(包含引數和返回型別)。在服務端實現這個介面,並執行一個 GRPC 伺服器來處理客戶端呼叫。在客戶端擁有一個存根能夠像服務端一樣的方法。

Stub存根:

為遮蔽客戶呼叫遠端主機上的物件,必須提供某種方式來模擬本地物件,這種本地物件稱為存根(stub),存根負責接收本地方法呼叫,並將它們委派給各自的具體實現物件。

二.GRPC JAVAdemo實現

針對你選擇的語言構建和安裝 gRPC 外掛和相關工具。 Java gRPC 除了 JDK 外不需要其他工具。

a)         在一個.proto檔案內定義服務

b)         用protocol buffer編譯器生成指定程式語言的伺服器和客戶端程式碼(maven compile + deploy)

c)         使用grpc的對應的語言(grpc-java)的API,實現服務端和客戶端的程序間的通訊。

1.protobuf使用包括以下幾個步驟

a)       定義資料結構。

b)       編譯成你喜歡的開發語言的相關工具類。

c)       在程式中呼叫工具類來處理資料。

一旦定義好服務,可以使用 protocol buffer 編譯器 protoc 來生成建立應用所需的特定客戶端和服務端的程式碼,生成的程式碼同時包括客戶端的存根和服務端要實現的抽象介面。

需要支援:Idea 外掛 protobuf support

通過:Maven compile+deploy

生成java版本的資料結構類:RPCDateRequest、RPCResponse的java類,我們在服務端、客戶端直接使用即可。

RPCDateRequest.java,RPCResponse.java和其他檔案包含所有 protocol buffer 用來填充、序列化和提取 RPCDateRequest和 RPCResponse訊息型別的程式碼。

RPCDateServiceGrpc.java:包含 (還有其他有用的程式碼)呼叫RPCDateService服務,服務端需要實現的抽象類

        public static abstract class RPCDateServiceImplBase implements io.grpc.BindableService {
public void getDate(com.grpc.api.RPCDateRequest request,
      io.grpc.stub.StreamObserver<com.grpc.api.RPCDateResponse> responseObserver) {
    asyncUnimplementedUnaryCall(METHOD_GET_DATE, responseObserver);
  }

客戶端用來與 RPCDateService服務端進行對話的存根類。

public static final class RPCDateServiceBlockingStub extends io.grpc.stub.AbstractStub<RPCDateServiceBlockingStub> {
  public com.grpc.api.RPCDateResponse getDate(com.grpc.api.RPCDateRequest request) {
    return blockingUnaryCall(
        getChannel(), METHOD_GET_DATE, getCallOptions(), request);
  }
}

服務端的主要工作

1.       實現定義的服務介面函式

2.       開啟服務端,監聽來自客戶端的請求並響應客戶端的請求。

服務實現

RPCDateServiceImpl.java 繼承自RPCDateServiceGrpc.RPCDateServiceImplBase,實現其定義的getDate方法。。

public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) {
    RPCDateResponse rpcDateResponse = null;
    Date now = new Date();
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("今天是"+"yyyyMMdd E kkmm");
    String nowTime = simpleDateFormat.format(now);
    try {
        rpcDateResponse = RPCDateResponse
                .newBuilder()
                .setServerDate("hello " + request.getUserName() + ", " + nowTime)
                .build();
    } catch (Exception e) {
        responseObserver.onError(e);
    } finally {
        responseObserver.onNext(rpcDateResponse);
    }
    responseObserver.onCompleted();
}

getDate有兩個引數:

·        RPCDateRequest:請求。

·        StreamObserver<RPCDateResponse>應答觀察者,一個特殊的介面,伺服器用應答來呼叫它。

為了返回給客戶端應答並且完成呼叫:

1.進行訊息構建並填充一個在我們介面定義的RPCDateResponse應答物件。

2.RPCDateResponse返回給客戶端,然後表明我們已經完成了對 RPC 的處理。

public class GRPCServer {
    private static final int port = 9999;
    public static void main(String[] args) throws Exception {
        Server server = ServerBuilder
                .forPort(port)              //設定服務埠
                .addService(new RPCDateServiceImpl())       //新增服務
                .build().start();
        System.out.print("grpc服務端啟動成功,埠=" + port);
        server.awaitTermination();
    }
}

客戶端的主要工作:

1.         建立Client例項並連線server

2.         呼叫服務端方法(請求)並獲取服務端迴應。

連線服務

需要建立一個 gRPC 頻道,指定我們要連線的主機名和伺服器埠。然後我們用這個頻道建立存根例項

呼叫RPC

1.         我們建立並填充一個RPCDateRequest傳送給服務。

2.         我們用請求呼叫存根的getDate(),如果 RPC 成功,會得到一個填充的RPCDateResponse,從其中我們可以獲得響應資料。

public class GRPCClient {
    private static final String host = "localhost";
    private static final int serverPort  = 9999;

    public static void main(String[] args) throws Exception {
        //根據埠和IP連線服務端,建立grpc channel
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, serverPort).usePlaintext().build();
        try {
            //建立存根,存根是根據.proto檔案中生成的類RPCDateServiceGrpc的代理。
            //blockingStub為阻塞式,需要阻塞等待服務端的迴應。    另有asyncStub為非阻塞,可以非同步執行。
            RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService = RPCDateServiceGrpc.newBlockingStub(managedChannel);
            //構造請求物件
            RPCDateRequest rpcDateRequest = RPCDateRequest
                    .newBuilder()
                    .setUserName("ChaiShuai")
                    .build();
            RPCDateResponse rpcDateResponse = rpcDateService.getDate(rpcDateRequest);
            System.out.println(rpcDateResponse.getServerDate());
        } finally {
            managedChannel.shutdown();
        }
    }
}

三.FAQ

在建立.proto檔案時,通過new>file,命名為.proto格式建立。添加了protobuf support外掛後,idea自動識別,前面的顯示圖示也變了。

通過maven compile後,相應的Java類會生成到target>generated-sources>java和grpc-java+ java_package(自主命名目錄)下。

在服務端和客戶端需要使用通過.proto定義的服務,要新增相應的maven依賴,這時由.proto生成的java類要進行釋出(maven deploy).

四.GRPC原理學習

RPC是什麼

通用RPC呼叫流程

GRPC

gRPC 是一個高效能、開源和通用的 RPC 框架,面向服務端和移動端,基於 HTTP/2 設計

語言特點:

語言中立,支援多種語言。

基於 IDL 檔案定義服務,通過 proto3 工具生成指定語言的資料結構、服務端介面以及客戶端 Stub。

通訊協議基於標準的 HTTP/2 設計,支援雙向流、訊息頭壓縮、單 TCP 的多路複用、服務端推送等特性,這些特性使得 gRPC 在移動端裝置上更加省電和節省網路流量。

序列化支援 PB(Protocol buffer)和 JSON,PB 是一種語言無關的高效能序列化框架,基於 HTTP/2 + PB, 保障了 RPC 呼叫的高效能。

gRPC服務端建立和呼叫原理解析:

一.gRPC 服務端建立關鍵流程分析

a.NettyServer 例項建立:gRPC 服務端建立,首先需要初始化 NettyServer,它是 gRPC 基於 Netty4.1 HTTP/2 協議棧之上封裝的 HTTP/2 服務端。NettyServer 例項由 NettyServerBuilder 的 buildTransportServer 方法構建。NettyServer 構建完成之後,監聽指定的 Socket 地址,即可實現基於 HTTP/2 協議的請求訊息接入。

b.繫結 IDL 定義的服務介面實現類:gRPC 與其它一些 RPC 框架的差異點是服務介面實現類的呼叫並不是通過動態代理和反射機制,而是通過 proto 工具生成程式碼。在服務端啟動時,將服務介面實現類例項註冊到 gRPC 內部的服務註冊中心上。請求訊息接入之後,可以根據服務名和方法名,直接呼叫啟動時註冊的服務例項,而不需要通過反射的方式進行呼叫,效能更優。

c.gRPC 服務例項(ServerImpl)構建:ServerImpl 負責整個 gRPC 服務端訊息的排程和處理,建立 ServerImpl 例項過程中,會對服務端依賴的物件進行初始化。例如 Netty 的執行緒池資源、gRPC 的執行緒池、內部的服務註冊類(InternalHandlerRegistry)等。ServerImpl 初始化完成之後,就可以呼叫 NettyServer 的 start 方法啟動 HTTP/2 服務端,接收 gRPC 客戶端的服務呼叫請求。

二.服務端 service 呼叫流程

gRPC 的客戶端請求訊息由 Netty Http2ConnectionHandler 接入,由 gRPC 負責將 PB 訊息(或者 JSON)反序列化為 POJO 物件,然後通過服務定義查詢到該訊息對應的介面例項,發起本地 Java 介面呼叫,呼叫完成之後,將響應訊息反序列化為 PB(或者 JSON),通過 HTTP2 Frame 傳送給客戶端。

流程並不複雜,但是細節卻比較多,整個 service 呼叫可以劃分為如下四個過程:

gRPC 請求訊息接入

gRPC 訊息頭和訊息體處理

內部的服務路由和呼叫

響應訊息傳送

GRPC Server端,還有一個最終要的方法:addService。

在此之前,我們需要介紹一下bindService方法,每個GRPC生成的service程式碼中都有此方法,它以硬編碼的方式遍歷此service的方法列表,將每個方法的呼叫過程都與“被代理例項”繫結,這個模式有點類似於靜態代理,比如呼叫sayHello方法時,其實內部直接呼叫“被代理例項”的sayHello方法(參見MethodHandler.invoke方法,每個方法都有一個唯一的index,通過硬編碼方式執行);bindService方法的最終目的是建立一個ServerServiceDefinition物件,這個物件內部位置一個map,key為此Service的方法的全名(fullname,{package}.{service}.{method}),value就是此方法的GRPC封裝類(ServerMethodDefinition)。

三.Protobuf 上的 GRPC

用 protobuf 定義的服務介面可以通過 protoc 的程式碼生成擴充套件簡單地對映成 GRPC ,以下定義了所用的對映:

路徑→ / 服務名 / {方法名}

服務名 → ?( {proto 包名} "." ) {服務名}

訊息型別 → {全路徑 proto 訊息名}

內容型別 → "application/grpc+proto"

gRPC客戶端建立和呼叫原理解析

gRPC的客戶端呼叫主要包括基於Netty的HTTP/2客戶端建立、客戶端負載均衡、請求訊息的傳送和響應接收處理四個流程。

gRPC的客戶端呼叫總體流程如下圖所示

ManagedChannelBuilder來建立客戶端channel,ManagedChannelBuilder使用了provider機制,具體是建立了哪種channel有provider決定,可以參看META-INF下同類名的檔案中的註冊資訊。當前Channel有2種:NettyChannelBuilder與OkHttpChannelBuilder。當前版本中為NettyChannelBuilder;可以直接使用NettyChannelBuilder來構建channel。

ManagedChannel是客戶端最核心的類,它表示邏輯上的一個channel;底層持有一個物理的transport(TCP通道,參見NettyClientTransport),並負責維護此transport的活性;即在RPC呼叫的任何時機,如果檢測到底層transport處於關閉狀態(terminated),將會嘗試重建transport。(參見TransportSet.obtainActiveTransport())

通常情況下,我們不需要在RPC呼叫結束後就關閉Channel,Channel可以被一直重用,直到Client不再需要請求為止或者Channel無法真的異常中斷而無法繼續使用。

每個Service客戶端,都生成了2種stub:BlockingStub和FutureStub;這兩個Stub內部呼叫過程幾乎一樣,唯一不同的是BlockingStub的方法直接返回Response, 而FutureStub返回一個Future物件。BlockingStub內部也是基於Future機制,只是封裝了阻塞等待的過程。

Client端關鍵元件:

關於Client負載均衡

Grpc分層設計

參考文章:


相關推薦

GRPC學習總結

一.概念普及:protocolbuffers(protobuf):結構資料序列化機制主要思想是:定義一個服務, 指定其可以被遠端呼叫的方法及其引數和返回型別。用來實現不同程序間的通訊。gRPC 預設使用 protocol buffers,用 proto files 建立 gR

設計模式學習總結(八)策略模式(Strategy)

isp 筆記本 override div ont 角色 write stat 通過   策略模式,主要是針對不同的情況采用不同的處理方式。如商場的打折季,不同種類的商品的打折幅度不一,所以針對不同的商品我們就要采用不同的計算方式即策略來進行處理。   一、示例展示:   以

設計模式學習總結(七)適配器模式(Adapter)

實現接口 國外 手機 額外 sed ges program ebe 通過   適配器模式主要是通過適配器來實現接口的統一,如要實現國內手機在國外充電,則需要在不同的國家采用不同的適配器來進行兼容!   一、示例展示:   以下例子主要通過給筆記本電腦添加類似手機打電話和發短

Linux下常用命令之sed學習總結

linux sed sed命令 正則表達式 sed總結 Sed功能說明:Sed是linux下一個強大的文本文件處理工具,通過對文件增加、刪除、查找、查詢操作,配合正則表達式以實現工作中的各種需求。同時也是一名運維人員必須掌握的核心技能。---------------------------

Paxos 學習總結

max 更強 分開 由於 zab ted 偽代碼 big commit 近期學習了分布式領域的重要算法Paxos,這裏羅列下關鍵點當作總結。自己水平有限,難免存在謬誤,懇請讀者指正。本篇不包含Paxos的基本理論介紹。Paxos基礎能夠參考以下的學習資料

Java IO流學習總結

系統 指針 數組 rar amr redo 修改 接收 學習 Java IO流學習總結 Java流操作有關的類或接口: Java流類圖結構: 流的概念和作用 流是一組有順序的,有起點和終點的字節集合,是對數據傳輸的總稱或抽象。即數據在兩設備間的傳輸稱為流,流

201521044091 《Java程序設計》第11周學習總結

概念 ray 本周 art pre sign 繼續 not 生產 1. 本章學習總結 2. 書面作業 Q1.1.互斥訪問與同步訪問完成題集4-4(互斥訪問)與4-5(同步訪問) 1.1 除了使用synchronized修飾方法實現互斥同步訪問,還有什麽辦法實現互斥同步訪

201521123087 《Java程序設計》第11周學習總結

syn finally ktr comm 解鎖 cer 存取 ren 進行 1. 本周學習總結 2. 書面作業 本次PTA作業題集多線程 互斥訪問與同步訪問完成題集4-4(互斥訪問)與4-5(同步訪問)1.1 除了使用synchronized修飾方法實現互斥同步訪

201521123105 第11周Java學習總結

同步互斥 生產 tor 什麽 signal 源代碼 final 本周 執行 1. 本周學習總結 1.1 以你喜歡的方式(思維導圖或其他)歸納總結多線程相關內容。 2. 書面作業 本次PTA作業題集多線程 1. 互斥訪問與同步訪問 完成題集4-4(互斥訪問)與4-5(同步訪

201521123089 《Java程序設計》第11周學習總結

提交 trac 消費者 生產者消費者問題 start nal 出現 選擇 查詢 1. 本周學習總結 1.1 以你喜歡的方式(思維導圖或其他)歸納總結多線程相關內容。 2. 書面作業 本次PTA作業題集多線程 Q1.互斥訪問與同步訪問 1.1 除了使用synchroni

《構建之法》第八、九章學習總結

快速 需求 獲取 利益相關者 軟件需求 用戶需求 估計 bcd abcd 第八章:需求分析 這一章主要講述了軟件需求的類型、利益相關者、獲取用戶需求的常用方法和步驟、競爭性需求分析的框架NABCD、四象限方法、項目計劃和估計的技術。 確認軟件需求有以下步驟:1.獲取和引導需

《網絡攻防》第十周學習總結

log 64位 位置 uid 操作 fff 指令 攻擊 匯編 緩沖區溢出漏洞實踐 由於實驗樓提供的是64位操作系統,而本次實驗為了方便觀察匯編語句,采用32位操作系統,所以先按照要求進行一些必要的準備 先按順序輸入下面的三個命令安裝32位操作系統 安裝好之

MSFVENOM學習總結

免殺 erp ack 文件格式 bash axis2 per tab 字符 MSFVENOM常規選項 1. –p (- -payload-options) 添加載荷payload。 載荷這個東西比較多,這個軟件就是根據對應的載荷payload生成對應平臺下的後門,所

學習總結——JMeter做http接口功能測試

相同 關聯 訪問 對話框 表達式 分享 tor 我們 連接 JMeter對各種類型接口的測試 默認做接口測試前,已經給出明確的接口文檔(如,http://test.nnzhp.cn/wiki/index.php?doc-view-59);本地配好了JMeter 3.x的運行

學習總結——JMeter做http接口壓力測試

方便 請求 jmeter 場景 學習總結 結果 log 添加 如果 JMeter做http接口壓力測試 測前準備 用JMeter做接口的壓測非常方便,在壓測之前我們需要考慮這幾個方面: 場景設定 場景分單場景和混合場景。針對一個接口做壓力測試就是單場景,針對一個流程做壓

學習總結——JMeter做WebService接口功能測試

ice width wsdl image rpc 獲取 請求 url 學習總結 用JMeter作WebService接口功能測試(可以借助SoapUI來完成) SoapUI裏面的操作: Wsdl文件或鏈接導入或添加到SoapUI打開待測請求;運行請求;取URL SOA

2014025665《嵌入式系統程序設計》第三、四周學習總結

-i 源代碼 nss gcc 一個 usr 交叉開發 64位 操作系統 第三周1.我們在帶三周的嵌入式程序設計中學到了如何搭建嵌入式Linux交叉開發環境。其實主要是解決64位系統下如何安裝32位程序的問題。①若聯網:Syum install Id-Linux.so.2②若

近一個月的學習總結(4.8—5.12)

line spa java泛型 思想 習題 cti mysql 數組 對象 Java-se基礎知識的學習已經告一段落,對自己這一個月的知識體系做一個大致的總結: 1.Java語言基礎(基礎完成) 2.面向對象基礎(封裝、繼承、多態)(基礎完成) 3.抽象類、接口(基礎完成)

2014025635(09)《嵌入式程序設計》第三,四周學習總結

efi 必須 修改 update input 找不到 變量 pwd 都是 1.第三四周學習情況 本周學習進度不慢,上課也能跟著老師敲代碼了,老師說一些指令也知道是什麽意思了,這兩周我認為非常重要的快捷鍵就是tab鍵,補全代碼,我自己手敲肯定出現很多錯誤,必須Tab鍵!!!!

201521123116 《java程序設計》第十二周學習總結

textfile objects test tput bsp cti 目的 字節 指定 1. 本周學習總結 1.1 以你喜歡的方式(思維導圖或其他)歸納總結多流與文件相關內容。 2. 書面作業 Q1.字符流與文本文件:使用 PrintWriter(寫),BufferedR