GRPC是一個高性能、通用的開源RPC架構,基于HTTP/2協定标準和Protobuf序列化協定開發,支援衆多的開發語言。
文章目錄
- 1 簡介
-
- 1.1 http2
-
- 1.1.1二進制傳輸
- 1.1.2 Header壓縮
- 1.1.3 多路複用
- 1.1.4 伺服器推送
- 1.2 Protobuf
-
- 1.2.1 優點
- 1.2.2 缺點
- 1.2.3 與其他序列化方式對比
- 2 grpc四種模式
- 3 內建springcloud
-
- 3.1 實作公共protobuf,通過java grpc來遠端調用
-
-
- 優缺點分析
-
- 3.2 每次使用protobuf定義接口(內建net.devh.grpc)
- 4 服務發現與負載均衡
-
- 4.1 dubbo
- 4.2 springcloud
- 4.3 istio
- 5 grpc之攔截器
-
- 5.1 server攔截器
- 5.2 client攔截器
- 6 grpc監控之全鍊路
- 7 grpc監控之prometheus
- 8 grpc斷路器
- 9 grpc高并發優化之預熱
- 10 grpc報錯集錦以及解決方案
1 簡介
在GRPC架構中,用戶端可以像調用本地對象一樣直接調用位于不同機器的服務端方法,如此我們就可以非常友善的建立一些分布式的應用服務。
在服務端,我們實作了所定義的服務和可供遠端調用的方法,運作一個gRPC server來處理用戶端的請求;在用戶端,gRPC實作了一個stub(可以簡單了解為一個client),其提供跟服務端相同的方法。

gRPC使用protocol buffers作為接口描述語言(IDL)以及底層的資訊交換格式,一般情況下推薦使用 proto3因為其能夠支援更多的語言,并減少一些相容性的問題。
由于gRPC涉及到幾個比較重要的技術點http2、protobuf,正是這幾個技術點才使得gRPC得到廣泛應用,這裡也順帶講一下這幾個技術點
1.1 http2
HTTP/2是最新的HTTP協定,提高了資源通路效率。通過本篇科普小文,可以了解HTTP/2協定的概念以及優勢。
HTTP/2也被稱為HTTP 2.0,相對于HTTP 1.1新增多路複用、壓縮HTTP頭、劃分請求優先級、服務端推送等特性,解決了在HTTP 1.1中一直存在的問題,優化了請求性能,同時相容了HTTP 1.1的語義。
2015年,HTTP/2 釋出。HTTP/2是現行HTTP協定(HTTP/1.1)的替代,但它不是重寫,HTTP方法、狀态碼、語義都與HTTP/1.1一樣。HTTP/2 相比于 HTTP/1.1,可以說是大幅度提高了網頁的性能,隻需要更新到該協定就可以減少很多之前需要做的性能優化工作。HTTP/2基于SPDY,專注于性能,最大的一個目标是在使用者和網站間隻用一個連接配接(connection)。
HTTP/2新特性
1.1.1二進制傳輸
HTTP/2傳輸資料量的大幅減少,主要有兩個原因:以二進制方式傳輸和Header 壓縮。先來介紹一下二進制傳輸,HTTP/2 采用二進制格式傳輸資料,而非HTTP/1.1 裡純文字形式的封包 ,二進制協定解析起來更高效。HTTP/2 将請求和響應資料分割為更小的幀,并且它們采用二進制編碼。HTTP/2所有性能增強的核心在于新的二進制分幀層,它定義了如何封裝http消息并在用戶端與伺服器之間傳輸。
1.1.2 Header壓縮
HTTP/1.1的header帶有大量資訊,而且每次都要重複發送,HTTP/2并沒有使用傳統的壓縮算法,而是開發了專門的“HPACK”算法,在用戶端和伺服器兩端建立“字典”,用索引号表示重複的字元串,還采用哈夫曼編碼來壓縮整數和字元串,可以達到50%~90%的高壓縮率。
1.1.3 多路複用
多路複用允許同時通過單一的HTTP/2連接配接發起多重的請求-響應資訊,很好的解決了浏覽器限制同一個域名下的請求數量的問題,同時也更容易實作全速傳輸。
1.1.4 伺服器推送
HTTP2還在一定程度上改變了傳統的“請求-應答”工作模式,伺服器不再是完全被動地響應請求,也可以建立“流”主動向用戶端發送消息。比如,在浏覽器剛請求HTML的時候就提前把可能會用到的JS、CSS檔案發給用戶端,減少等待的延遲,這被稱為”伺服器推送”( Server Push,也叫 Cache push)。
1.2 Protobuf
Protocol buffers 是一種語言中立,平台無關,可擴充的序列化資料的格式,可用于通信協定,資料存儲等。
Protocol buffers 在序列化資料方面,它是靈活的,高效的。相比于 XML 來說,Protocol buffers 更加小巧,更加快速,更加簡單。一旦定義了要處理的資料的資料結構之後,就可以利用 Protocol buffers 的代碼生成工具生成相關的代碼。甚至可以在無需重新部署程式的情況下更新資料結構。隻需使用 Protobuf 對資料結構進行一次描述,即可利用各種不同語言或從各種不同資料流中對你的結構化資料輕松讀寫。
Protocol buffers 很适合做資料存儲或 RPC 資料交換格式。可用于通訊協定、資料存儲等領域的語言無關、平台無關、可擴充的序列化結構資料格式。
1.2.1 優點
1、性能好/效率高
時間開銷: XML格式化(序列化)的開銷還好;但是XML解析(反序列化)的開銷就不敢恭維了。 但是protobuf在這個方面就進行了優化。可以使序列化和反序列化的時間開銷都減短。
空間開銷:也減少了很多
2、有代碼生成機制
比如你你寫個一下類似結構體的内容
message testA
{
required int32 m_testA = 1;
}
像寫一個這樣的結構,protobuf可以自動生成它的.h 檔案和點.cpp檔案。
protobuf将對結構體testA的操作封裝成一個類。
3、支援向後相容和向前相容
當用戶端和伺服器同僚使用一塊協定的時候, 當用戶端在協定中增加一個位元組,并不會影響用戶端的使用
4、支援多種程式設計語言
在Google官方釋出的源代碼中包含了c++、java、Python三種語言
1.2.2 缺點
1、二進制格式導緻可讀性差
為了提高性能,protobuf采用了二進制格式進行編碼。這直接導緻了可讀性差。這個直接影響開發測試時候的效率。當然,一般情況下,protobuf非常可靠,并不會出現太大的問題。
2、缺乏自描述
一般來說,XML是自描述的,而protobuf格式則不是。 給你一段二進制格式的協定内容,不配合你寫的結構體是看不出來什麼作用的。
3、通用性差
protobuf雖然支援了大量語言的序列化和反序列化,但仍然并不是一個跨平台和語言的傳輸标準。在多平台消息傳遞中,對其他項目的相容性并不是很好,需要做相應的适配改造工作。相比json 和 XML,通用性還是沒那麼好。
1.2.3 與其他序列化方式對比
protobuf處理整型特别快,如果需要了解原理可以檢視高效的資料壓縮編碼方式 Protobuf
至于與其他序列化方式以及使用場景的對比可以參考下面的文章
Protobuf有沒有比JSON快5倍?用代碼來擊破pb性能神話
全方位評測:Protobuf性能到底有沒有比JSON快5倍?
2 grpc四種模式
1,簡單模式:簡單模式隻是使用參數和傳回值作為伺服器與用戶端傳遞資料的方式,最簡單。
2,用戶端流模式:即從用戶端往伺服器端發送資料使用的是流,即伺服器端的參數為流類型,然而在伺服器相應後返還資料給用戶端,使用的也是流的send方法。一般在伺服器端的代碼,需要先recv再send,而用戶端與此相反。但是在後面的雙向模式中可以使用go的協程協作。
3,伺服器端流模式:即伺服器端傳回結果的時候使用的是流模式,即傳入的資料是通過參數形式傳入的。但是在往用戶端發送資料時使用send方法,與用戶端傳回資料的方式大同小異。
4,雙向模式:用戶端如果不适用協程,那麼發送必須在接收之前。如果使用協程,發送與接收并沒有先後順序。為了保證協程的同步,可以使用互斥量進行限制。
如果想要了解詳細demo,可以檢視gRPC 官方文檔中文版
3 內建springcloud
這裡內建grpc建議有兩種方案,
3.1 實作公共protobuf,通過java grpc來遠端調用
這個方案就是不用編寫protobuf了,可以直接類似dubbo一樣提供java api就可以實作rpc調用,這裡詳情可以參考github上的demo
ttps://github.com/ChinaSilence/spring-boot-starter-grpc
- facade:獨立的 Maven 子產品,依賴
,需要遠端調用的方法,都定義在此子產品,形式可以為接口(interface) 或者抽象類(abstract class)spring-boot-starter-grpc
- server:服務提供方,依賴
子產品,需實作facade
子產品定義的接口或者抽象類的抽象方法facade
- client:服務調用方,依賴
子產品,使用時,直接調用即可facade
優缺點分析
優點:
- 不需要編寫probuff檔案,以java api形式來定義接口
- 不依賴于eureka,完美适用于k8s
缺點:
- 隻支援java,如果要支援異構語言需要使用springcloudsidecar 或者 手動注冊到eureka
- eureka支援有限,不支援負載均衡
3.2 每次使用protobuf定義接口(內建net.devh.grpc)
這裡内容還比較多,詳情可以參考我的部落格
springcloud內建grpc(一)
這種方式內建每次都需要編寫proto接口檔案并自動生成代碼,用戶端和服務端都需要另外組裝參數。
不過優勢是,有詳細的接口規範(protobuf),并且可以支援異構語言調用。
後面會介紹隻有java語言調用,但是不用每次都編寫proto檔案的內建方式。
4 服務發現與負載均衡
gRPC開源元件官方并未直接提供服務注冊與發現的功能實作,但其設計文檔已提供實作的思路,并在不同語言的gRPC代碼API中已提供了命名解析和負載均衡接口供擴充。
其基本實作原理:
服務啟動後gRPC用戶端向命名伺服器發出名稱解析請求,名稱将解析為一個或多個IP位址,每個IP位址标示它是伺服器位址還是負載均衡器位址,以及标示要使用那個用戶端負載均衡政策或服務配置。
用戶端執行個體化負載均衡政策,如果解析傳回的位址是負載均衡器位址,則用戶端将使用grpclb政策,否則用戶端使用服務配置請求的負載均衡政策。
負載均衡政策為每個伺服器位址建立一個子通道(channel)。
當有rpc請求時,負載均衡政策決定那個子通道即grpc伺服器将接收請求,當可用伺服器為空時用戶端的請求将被阻塞。
根據gRPC官方提供的設計思路,基于程序内LB方案(即第2個案,阿裡開源的服務架構 Dubbo 也是采用類似機制),結合分布式一緻的元件(如Zookeeper、Consul、Etcd),可找到gRPC服務發現和負載均衡的可行解決方案。接下來以GO語言為例,簡單介紹下基于Etcd3的關鍵代碼實作:
4.1 dubbo
上面有描述,dubbo支援grpc,方案類似本文3.1提出的方案,不過是阿裡提供了一整套微服務體系,包括注冊中心nacas、dubbo、sentinel都支援grpc
4.2 springcloud
這個方案同樣是類似3.2章,上面是內建了net.devh.grpc,
該插件最新版本,同樣支援springcloud全家桶,詳細可以參考github:
https://github.com/yidongnan/grpc-spring-boot-starter。
如果是異構語言則需要內建springcloud中的sidecar來實作服務發現,這裡也僅僅是支援java調用其他異構語言,如果需要其他語言也支援互相調用,則需要對應語言按照開源元件官方說的方案二來實作相應元件
4.3 istio
Istio是什麼:Istio是Google/IBM/Lyft聯合開發的開源項目,2017年5月釋出第一個release 0.1.0, 官方定義為:
Istio:一個連接配接,管理和保護微服務的開放平台。
按照isito文檔中給出的定義:
Istio提供一種簡單的方式來建立已部署的服務的網絡,具備負載均衡,服務到服務認證,監控等等功能,而不需要改動任何服務代碼。
簡單的說,有了Istio,你的服務就不再需要任何微服務開發架構(典型如Spring Cloud,Dubbo),也不再需要自己動手實作各種複雜的服務治理的功能(很多是Spring Cloud和Dubbo也不能提供的,需要自己動手)。隻要服務的用戶端和伺服器可以進行簡單的直接網絡通路,就可以通過将網絡層委托給Istio,進而獲得一系列的完備功能。
Istio的關鍵功能:
- HTTP/1.1,HTTP/2,gRPC和TCP流量的自動區域感覺負載平衡和故障切換。
- 通過豐富的路由規則,容錯和故障注入,對流行為的細粒度控制。
- 支援通路控制,速率限制和配額的可插拔政策層和配置API。
- 叢集内所有流量的自動量度,日志和跟蹤,包括叢集入口和出口。
- 安全的服務到服務身份驗證,在叢集中的服務之間具有強大的身份辨別。
可以近似的了解為:Istio = 微服務架構 + 服務治理
以下是 Istio 的官方拓撲圖:
這裡的前提是使用了k8s,在k8s中可以無縫內建istio。具體操作步驟我這裡不做較長的描述,詳情可以參考下面連結使用Istio和Envoy實踐服務網格gRPC度量
5 grpc之攔截器
grpc自帶認證,不過有時候也需要在調用前,或者調用後做一些操作,比如說記錄監控資訊、或者透傳header等等,這時就需要用到grpc的攔截器,這裡僅以java語言來說一下攔截器用法。
5.1 server攔截器
public class MyServerInsterceptor implements ServerInterceptor{
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
Metadata headers, ServerCallHandler<ReqT, RespT> next) {
return next.startCall(call,headers);
}
}
這裡可以設定全局攔截器
@Configuration
public class GrpcOpenConfig {
//grpc-spring-boot-starter provides @GrpcGlobalInterceptor to allow server-side interceptors to be registered with all
//server stubs, we are just taking advantage of that to install the server-side gRPC tracer.
@Bean
ServerInterceptor grpcServerInterceptor() {
return new MyServerInterceptor();
}
@Bean
public GlobalServerInterceptorConfigurer globalInterceptorConfigurerAdapter(ServerInterceptor grpcServerInterceptor) {
return registry -> registry.addServerInterceptors(grpcServerInterceptor);
}
}
同時如果內建了grpc-spring-boot-starter,也可以使用@GrpcGlobalInterceptor來增加全局攔截器,hi需要将該注解加到類名上
5.2 client攔截器
public class MyClientInterceptor implements ClientInterceptor {
Metadata.Key<String> token = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions, Channel next) {
return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
//此處為你登入後獲得的token的值
headers.put(token, "A2D05E5ED2414B1F8C6AEB19F40EF77C");
super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
@Override
public void onHeaders(Metadata headers) {
super.onHeaders(headers);
}
}, headers);
}
};
}
}
這裡可以設定全局攔截器
@Order(Ordered.LOWEST_PRECEDENCE)
@Configuration
@Slf4j
public class GrpcOpenTracingConfig {
//We also create a client-side interceptor and put that in the context, this interceptor can then be injected into gRPC clients and
//then applied to the managed channel.
@Bean
ClientInterceptor grpcClientInterceptor() {
return new MyclientInterceptor();
}
@Bean
public GlobalClientInterceptorConfigurer globalInterceptorConfigurerAdapter(ClientInterceptor grpcClientInterceptor) {
return registry -> registry.addClientInterceptors(grpcClientInterceptor);
}
}
同時如果內建了grpc-spring-boot-starter,也可以使用@GrpcGlobalInterceptor來增加全局攔截器,hi需要将該注解加到類名上
6 grpc監控之全鍊路
關于全鍊路監控,可以參考我的全鍊路系列部落格
微服務全鍊路跟蹤:grpc內建zipkin
微服務全鍊路跟蹤:jaeger內建grpc
微服務全鍊路跟蹤:jaeger內建istio,并相容uber-trace-id與b3
7 grpc監控之prometheus
如果是內建了 grpc-spring-boot-starter,則隻需要通路 http://服務域名/actuator/prometheus,可以看到grpc相關監控名額
如果是內建了grafana,就能看到下面的效果:
8 grpc斷路器
grpc斷路器又幾種方案:
1、istio斷路器
參考相關部落格istio-斷路器示例
2、hystrix
參考我的部落格grpc斷路器之hystrix
3、sentinel
grpc斷路器之sentinel
9 grpc高并發優化之預熱
這裡可以參考我的部落格springcloud線上釋出逾時之grpc優化
10 grpc報錯集錦以及解決方案
參考我的部落格:grpc報錯合集以及解決方案
grpc坑之Could not find TLS ALPN provider; no working netty-tcnative
未完待續