微服務開發手冊之GRPC

GRPC是一個高性能、通用的開源RPC框架,基於HTTP/2協議標準和Protobuf序列化協議開發,支持衆多的開發語言。

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 模塊,依賴 spring-boot-starter-grpc,需要遠程調用的方法,都定義在此模塊,形式可以爲接口(interface) 或者抽象類(abstract class)

  • 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

未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章