原文連接: https://zhuanlan.zhihu.com/p/...
微服務架構是近期比較流行的架構,知乎也實現了服務化。要實現服務化,則首先要解決各個服務之間的通訊問題。那麼就會面臨數據序列化、反序列化、鏈接管理、收發線程、超時處理等問題。若是本身實現一套這樣的機制,不但重複勞動,性能和效率也難以保證。rpc框架的出現解決了這些問題,讓調用者沒必要關心底層細節。目前主流的rpc框架有Apache Thrift、gRPC、Netty等。Apache Thrift和gRPC都是跨語言的rpc框架,他們採用了IDL來描述數據類型和接口,使用編譯器編譯出特定語言的代碼從而實現跨語言的rpc。本文主要介紹gRPC的規範及實現。html
rpc框架通常基於tcp或者http協議實現。基於http的rpc框架有許多優勢,HTTP/1.x協議簡單明瞭,是目前最流行的應用層協議,有着很是成熟且完善的各類基礎設施,如負載均衡、監控、代理等,適用性普遍,各個設備系統均有實現。可是缺點也很明顯,就是HTTP/1.x採用的是文本協議,解析速度慢,帶寬佔用高。並且request/response的通訊方法致使總體效率不高。gRPC基於HTTP2協議,HTTP2 使得grpc 可以更好的適用於移動客戶端和服務端通訊的使用場景,而且鏈接多路複用也保證了RPC 的效率。grpc 的協議設計上很好的使用了HTTP2 現有的語義,請求和響應的數據使用HTTP Body 發送,其餘的控制信息則用Header 表示。nginx
HTTP2基本概念git
流(Stream)github
流是服務器和客戶端在HTTP/2鏈接內用於交換幀數據的獨立雙向序列,邏輯上可看作一個較爲完整的交互處理單元,即表達一次完整的資源請求-響應數據交換流程;一個業務處理單元,在一個流內進行處理完畢,這個流生命週期完結。web
特色以下:算法
幀(Frame)chrome
HTTP/2拋棄HTTP/1的文本協議改成二進制協議,HTTP2的基本傳輸單元爲幀,每一個幀都從屬於某個流。json
幀的格式以下:api
幀的類型有:瀏覽器
流是爲了實現多路復而提出的邏輯概念,在一個鏈接上能夠同時存在多個流。而流是由一個個的幀組成,在一個流裏面的幀是有序的,多個流之間的幀能夠混雜在一塊兒傳輸。
gRPC over HTTP2
gRPC在HTTP2的基礎上定義了request和response的規範。使用header幀描述meta信息如超時時間、payload的壓縮算法等等。使用data幀傳輸具體的rpc參數和返回結果。
request header
scheme: http或者https
method: 固定爲POST
path: 服務名/rpc方法名
content-type: 目前取值都是application/grpc+proto,或json或其餘自定義協議
grpc-encoding 能夠有gzip, deflate, snappy 等取值,表示採用的壓縮方法。
grpc-timeout 表示調用的超時時間,單位有Hour(H), Minute(M), Second(S), Millisecond(m), Microsecond(u), Nanosecond(n) 等。
response header
response有兩種,通常狀況下response都要包含有header幀、數據幀和trailer。若是服務端發生了錯誤,則能夠只返回一個trailer即 Trailers-Only 。
Response-Headers 主要包括 HTTP-Status,Content-Type 以及 Custom-Metadata 等。Trailers-Only 也有 HTTP-Status ,Content-Type 和 Trailers。Trailers 包括了 Status 以及 0 或者多個 Custom-Metadata。
HTTP-Status 就是咱們一般的 HTTP 200,301,400 這些,很通用就再也不解釋。Status 也就是 gRPC 的 status, 而 Status-Message 則是 gRPC 的 message。Status-Message 採用了 Percent-Encoded 的編碼方式,具體參考這裏。
若是在最後收到的 HEADERS frame 裏面,帶上了 Trailers,而且有 END_STREAM 這個 flag,那麼就意味着 response 的 EOS。
Request例子:
HEADERS (flags = END_HEADERS) :method = POST :scheme = http :path = /google.pubsub.v2.PublisherService/CreateTopic :authority = pubsub.googleapis.com grpc-timeout = 1S content-type = application/grpc+proto grpc-encoding = gzip authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v DATA (flags = END_STREAM) <Delimited Message>
Response例子:
HEADERS (flags = END_HEADERS) :status = 200 grpc-encoding = gzip DATA <Delimited Message> HEADERS (flags = END_STREAM, END_HEADERS) grpc-status = 0 # OK trace-proto-bin = jher831yy13JHy3hc
從圖中能夠看出,客戶端的每次rpc調用都發起了一個流,而後在這個流中發送header幀和數據幀,而服務端的返回結過結果也是使用同一個流進行傳輸。
雖然說gRPC使用HTTP/2協議,不少瀏覽器也開始支持HTTP2,可是目前還不能使用瀏覽器做爲客戶端訪問gRPC服務。由於gRPC的response在數據幀以後還有一個trailer header幀(這個幀內包含了grpc-status和grpc-message頭),這會致使chrome的崩潰。爲了解決這一問題一個新項目grpc-web正在開發之中,還處於early access階段,參見這裏。grpc-web在grpc over HTTP2的基礎上修改和新增了一些規範,使得可以經過瀏覽器和JavaScript調用gRPC服務,參見這裏。
Nginx目前也沒法代理gRPC服務。雖然nginx從1.9.5版本開始支持HTTP2,可是還不支持反向代理HTTP2的upstreaming。參見這裏。
目前可行的經過瀏覽器訪問grpc的方法是使用一個gateway,在grpc服務和瀏覽器之間做爲中介,接受瀏覽器的Restful請求轉換成grpc標準的請求發送到服務端。參見這個項目