三天三夜總算是搞懂了RPC遠(yuǎn)程過程調(diào)用,SpringCloud集成gRPC
Spring Cloud集成gRPC
gRPC本身的跨平臺(tái)特性及性能上的優(yōu)勢(shì)都促使很多大公司采用gRPC的RPC解決方案作為微服務(wù)交互的標(biāo)準(zhǔn)交互集成方式。
到目前為止,Spring Cloud官方并沒有支持gRPC,但是在GitHub上有非常多的第三方開源項(xiàng)目支持gRPC與Spring Cloud的集成,start數(shù) 目 最 多 的 開 源 項(xiàng) 目 是 grpc-spring-boot-starter 。該 項(xiàng) 目 也 是Spring Cloud社區(qū)推薦的gRPC項(xiàng)目。下面是這個(gè)項(xiàng)目的主要特性:
● 在Spring Boot應(yīng)用中,通過@GrpcService自動(dòng)配置并運(yùn)行一個(gè)嵌入式的gRPC服務(wù)。
● 使用@GrpcClient自動(dòng)創(chuàng)建和管理gRPC通道(Channels)和樁代碼(Stub)。
● 支持Spring Sleuth作為分布式鏈路跟蹤解決方案。
● 支持全局和自定義的gRPC服務(wù)端/客戶端攔截器。
● 支持Spring Security。
● 支持Metric(基于micrometer/actuator)。
● 適用于(non-shaded)grpc-netty。
Spring Boot中g(shù)RPC的接入gRPC接入Spring Cloud主要分為三個(gè)工程模塊,即服務(wù)定義模塊、服務(wù)提供模塊和服務(wù)消費(fèi)模塊。下面是接入gRPC的主要步驟。
1.服務(wù)定義
和其他RPC框架類似,gRPC需要做接口定義規(guī)范,默認(rèn)情況下,會(huì)使用Protocal Buffers作為接口定義語言(IDL)。
首先,引入Maven依賴:

然后,編寫一個(gè).proto文件,定義好服務(wù)端的請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù),執(zhí)行mvn clean install命令,protobuf-maven-plugin插件會(huì)根據(jù).proto文件生成對(duì)應(yīng)的Java代碼,Maven的install命令會(huì)將接口工程打包上傳到代碼中央倉庫,服務(wù)端和客戶端可以通過Maven將遠(yuǎn)程中央 倉 庫 加 載 到 本 地 并 打 包 到 各 自 的 工 程 中 。下 面 是 IDL 的 定 義(demo.proto):

2.gRPC Server實(shí)現(xiàn)
首先,引入Maven依賴:

其次,使用注解@GrpcService實(shí)現(xiàn)服務(wù)暴露:

然后,啟動(dòng)gRPC Server。默認(rèn)情況下,gRPC Server會(huì)監(jiān)聽9090端口,也可以使用grpc.server.前綴自定義配置。

3.gRPC Client實(shí)現(xiàn)
首先,引入Maven依賴:


其次,使用注解@GrpcClient(serverName)作為gRPC的樁代碼(Stub):

然后,實(shí)現(xiàn)gRPC Client的RestController遠(yuǎn)程調(diào)用:

gRPC的工作原理
gRPC的工作原理是先通過IDL文件定義服務(wù)接口的參數(shù)和返回值類型,然后通過代碼生成程序生成服務(wù)端和客戶端的具體實(shí)現(xiàn)代碼。
gRPC的主要特性包括三個(gè)方面。
(1)通信協(xié)議采用了HTTP 2,因?yàn)镠TTP 2提供了連接復(fù)用、雙向流、服務(wù)器推送、請(qǐng)求優(yōu)先級(jí)、首部壓縮等機(jī)制,所以在通信過程中可以節(jié)省帶寬、降低TCP連接次數(shù)、節(jié)省CPU資源,尤其對(duì)于移動(dòng)端應(yīng)用來說,可以延長電池壽命。
(2)IDL使用了ProtoBuf,ProtoBuf是由Google開發(fā)的一種數(shù)據(jù)序列化協(xié)議,它的壓縮和傳輸效率極高,語法也簡單,所以被廣泛應(yīng)用在數(shù)據(jù)存儲(chǔ)和通信協(xié)議上。
(3)多語言支持,能夠基于多種語言自動(dòng)生成對(duì)應(yīng)語言的客戶端和服務(wù)端代碼。
gRPC的核心概念
● 基于服務(wù)定義:ProtoBuffer IDL基于服務(wù)定義的思想,默認(rèn)情況下gRPC使用ProtoBuffer作為IDL(接口定義語言)進(jìn)行服務(wù)和消息的定義,示例代碼如下:

gRPC可以定義四種類型的服務(wù)方法。
○ Unary RPC:客戶端向服務(wù)端發(fā)送請(qǐng)求,并得到響應(yīng),類似于方法調(diào)用。

○ Server streaming RPC:客戶端可以向服務(wù)端發(fā)送請(qǐng)求,獲取服務(wù)端返回的流響應(yīng),客戶端可從流中讀取一組消息,客戶端可以持續(xù)讀取消息直至消息全部讀取完成,gRPC保證消息順序的正確性。

○ Client streaming RPC:客戶端會(huì)寫入一組消息,然后基于流的方式發(fā)送給服務(wù)端。當(dāng)客戶端寫完全部消息后,就等待服務(wù)端進(jìn)行消息的讀取并等待服務(wù)端響應(yīng),gRPC保證消息順序的正確性。

○ Bidirectional streaming RPC:服務(wù)端和客戶端都可以使用讀寫流發(fā)送一組消息。服務(wù)端的流和客戶端的流是相互獨(dú)立的,所以服務(wù)端和客戶端可以按照自己的方式進(jìn)行流的寫入和讀取。例如,服務(wù)端可以決定在全部接收完客戶端發(fā)送的消息后再進(jìn)行響應(yīng),或者它可以讀取一條消息,就寫入一條消息。同樣,在流中的消息的順序是可以保證的。

● HTTP 2
HTTP 2通過Stream支持了連接的多路復(fù)用,提高了連接的利用率。Stream的重要特性如下:
○ 一個(gè)連接可以包含多個(gè)Stream,多個(gè)Stream發(fā)送的數(shù)據(jù)互相不影響。
○ Stream可以被客戶端和服務(wù)端單方面或者共享使用。
○ Stream可以被任意一端關(guān)閉。
○ Stream會(huì)確定好發(fā)送Frame的順序,另一端會(huì)按照接收到的順序來處理。
○ Stream用一個(gè)唯一ID來標(biāo)識(shí)。
雖然看上去協(xié)議的格式和HTTP 1完全不同,實(shí)際上HTTP 2并沒有改變HTTP 1的語義,只是把原來HTTP 1的Header和Body部分用Frame重新封裝了一層而已,如下圖所示。

HTTP 2的優(yōu)勢(shì)如下。
○ 連 接 共 享 :HTTP 2 要 解 決 的 一 大 難 題 就 是 多 路 復(fù) 用(MultiPlexing),即連接共享。
○ Header壓縮:HTTP 2使用encoder來減少需要傳輸?shù)腍eader大小,通信雙方各自緩存(Cache)一份Header Fields表,既避免了重復(fù)Header的傳輸,又減小了需要傳輸Header的大小。高效的壓縮算法可以大幅度壓縮Header的大小,減少發(fā)送包的數(shù)量從而降低延遲。
○ 重置連接表現(xiàn)更好:HTTP 2引入RST_STREAM類型的Frame,可以在不斷開連接的前提下取消某個(gè)請(qǐng)求的Stream,表現(xiàn)更好。
○ 流量控制:每個(gè)HTTP 2流都擁有自己的公示的流量窗口,它可以限制另一端發(fā)送數(shù)據(jù)。對(duì)于每個(gè)Stream來說,兩端都必須告訴對(duì)方自己還有足夠的空間來處理新的數(shù)據(jù),而在該窗口被擴(kuò)大前,另一端只被允許發(fā)送那么多數(shù)據(jù)。
● 支持普通/流式RPC
○ 普通RPC調(diào)用:指客戶端發(fā)送一個(gè)請(qǐng)求并獲取一個(gè)響應(yīng)。當(dāng)客戶端調(diào)用本地的樁方法時(shí),服務(wù)端會(huì)得到一個(gè)RPC被調(diào)用的通知,通知中包含了關(guān)于此次調(diào)用的元數(shù)據(jù)信息(方法名、指定的合適的超時(shí)時(shí)間)。服務(wù)端可以立即返回一些它自己的初始化元數(shù)據(jù),或者等待客戶端的請(qǐng)求信息,當(dāng)然這兩種方式是和具體的應(yīng)用相關(guān)的。當(dāng)服務(wù)端接收到客戶端的請(qǐng)求信息后,它會(huì)執(zhí)行具體的邏輯以便產(chǎn)生一個(gè)響應(yīng)。響應(yīng)會(huì)和一個(gè)描述狀態(tài)的詳細(xì)信息及一個(gè)可選的附屬元數(shù)據(jù)一起被發(fā)送給客戶端。如果響應(yīng)的狀態(tài)是OK,則客戶端就得到了響應(yīng),完成了一次RPC調(diào)用。
○ 服務(wù)端Streaming模式:指客戶端發(fā)起1個(gè)請(qǐng)求,服務(wù)端返回N個(gè)響應(yīng),每個(gè)響應(yīng)可以單獨(dú)返回,它的原理如下圖所示。

○ 雙向流式RPC:客戶端發(fā)送N個(gè)請(qǐng)求,服務(wù)端返回N個(gè)或者M(jìn)個(gè)響應(yīng),利用該特性,可以充分利用HTTP 2的多路復(fù)用功能。在某個(gè)時(shí)刻,HTTP 2鏈路上可以既有請(qǐng)求也有響應(yīng),實(shí)現(xiàn)了全雙工通信,示例如下圖所示。

gRPC服務(wù)調(diào)用解析過程
gRPC的線程模型在Java實(shí)現(xiàn)中主要基于Netty底層網(wǎng)絡(luò)通信框架,它遵循一個(gè)基本原則:除了傳輸過程中的監(jiān)聽及解包相關(guān)流程,其他的邏輯處理都會(huì)放在業(yè)務(wù)線程池中。比如序列化與反序列化、攔截器邏輯、本地方法調(diào)用。這個(gè)設(shè)計(jì)符合Netty的線程模型實(shí)踐規(guī)范,最大化地保障傳輸框架的性能,提高服務(wù)資源的利用率。gRPC框架向業(yè)務(wù)層暴露了兩個(gè)入口,一個(gè)是攔截器,在進(jìn)入本地方法調(diào)用前攔截請(qǐng)求,用于處理一些前置邏輯;另一個(gè)就是本地服務(wù)。為了更清晰地表達(dá)業(yè)務(wù)線程池和Netty I/O線程池的分工,我們用下面的流程圖來示意。

(1)NettyServer實(shí)例創(chuàng)建:gRPC服務(wù)端創(chuàng)建,首先需要初始化NettyServer,它是gRPC基于Netty 4.1和HTTP 2協(xié)議棧之上封裝的HTTP 2服務(wù)端。
(2)NettyServerBuilder的buildTransportServer方法構(gòu)建:NettyServer構(gòu)建完成之后,監(jiān)聽指定的Socket地址。
(3)綁定IDL定義的服務(wù)接口實(shí)現(xiàn)類:gRPC與其他一些RPC框架的差異在于服務(wù)接口實(shí)現(xiàn)類的調(diào)用不是通過動(dòng)態(tài)代理和反射機(jī)制,而是通過proto工具生成代碼。在服務(wù)端啟動(dòng)時(shí),將服務(wù)接口實(shí)現(xiàn)類實(shí)例注冊(cè)到gRPC內(nèi)部的服務(wù)注冊(cè)中心上。請(qǐng)求消息接入之后,可以根據(jù)服
名和方法名,直接調(diào)用啟動(dòng)時(shí)注冊(cè)的服務(wù)實(shí)例,性能更優(yōu)。
(4)gRPC服務(wù)實(shí)例(ServerImpl)構(gòu)建:ServerImpl負(fù)責(zé)整個(gè)gRPC服務(wù)端消息的調(diào)度和處理,在創(chuàng)建ServerImpl實(shí)例的過程中,會(huì)對(duì)服務(wù)端依賴的對(duì)象進(jìn)行初始化。例如Netty的線程池資源、gRPC的線程 池 、 內(nèi) 部 的 服 務(wù) 注 冊(cè) 類 ( InternalHandlerRegistry ) 等 。
ServerImpl初始化完成之后,就可以調(diào)用NettyServer的start方法啟動(dòng)HTTP 2服務(wù)端,接收gRPC客戶端的服務(wù)調(diào)用請(qǐng)求。
grpc-spring-boot-starter源碼解析
grpc-spring-boot-stater的框架設(shè)計(jì)同樣遵循腳手架一章中自定義Starter的方式,以便融合到Spring Boot和Spring Cloud體系。自定義Starter步驟如下。
1.自定義配置

2.在配置文件中加載Bean并初始化

3.配置Bean,初始化GrpcService服務(wù)


4.配置GrpcServerLifecycle服務(wù)

5.啟動(dòng)gRPC服務(wù)


本文給大家講解的內(nèi)容是RPC遠(yuǎn)程過程調(diào)用,SpringCloud集成gRPC
下篇文章給大家講解的內(nèi)容是MOM異步通信,消息中間件(消息隊(duì)列?)
覺得文章不錯(cuò)的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;
感謝大家的支持!
本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學(xué)習(xí)更多的話可以到微信公眾號(hào)里找我,我等你哦。
