原文:https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880
作者:Luc Perkins
譯者:李琪
gRPC-Web作為gRPC的JavaScript客戶端庫,使Web應用可以不用自定義HTTP伺服器為中介,直接通過Envoy與gRPC服務互動。經過了約兩年的活躍開發,上週(2018年10月底,譯者注)gRPC團隊在CNCF部落格宣佈gRPC-Web的GA版本正式釋出。
自從在Improbable engineering blog讀到了這篇博文,我個人就對gRPC-Web很感興趣。之前一直很看好gRPC的效能、可擴充性和IDL(介面描述語言)驅動的服務互動方式,而且特別想在服務呼叫鏈中去掉REST部分。我很高興gRPC-Web釋出正式版本,它在 Web 開發領域開闢了新的方式。
我覺得gRPC-Web的優勢就是自Web端向下構建了完整的端到端gRPC服務架構。在以前,如果你想讓web端與gRPC服務互動,就必須自己開發REST介面處理HTTP和gRPC之間的轉換。而使用gRPC-Web,我們不再需要自己寫額外的HTTP介面,可以直接用Protocol Buffers封裝所有資料介面(這裡借用了Envoy,在下文我會詳細解釋)。
REST 方式
下圖展示了基於gRPC服務架構構建Web App的兩種方式。左邊是傳統的REST方式。右邊是gRPC-Web方式。
左圖所示,REST API只是作為Web App和後端gRPC服務的連線點。在大部分場景下,REST 服務就是簡單的將HTTP呼叫轉換成gRPC呼叫。
舉個例子:客戶端需要驗證服務於是用POST
請求提交 JSON資料到HTTP伺服器的/auth
。然後HTTP端把JSON資料轉換成Protobuf訊息 AuthRequest
,並將訊息傳送給gRPC認證服務,最後從gRPC服務獲取到 AuthResponse
響應並將其轉換成JSON資料返回給前端。正如我在CNCF部落格中文章中說的一樣,這種方法本身並沒有錯,它是一種解決方案,而且很多開發者都用的很好,如果它能滿足你,你可以繼續這樣用。
更好的方案:如果可以去掉HTTP中介我們會少做很多工作(試想一下,JavaScript 端直接傳送AuthRequest
訊息給gRPC服務並獲得 AuthResponse
響應)。這意味著我們不需要關心HTTP狀態碼、JSON解析和HTTP服務本身帶來的部署和管理問題。
上圖右半部分是使用gRPC-Web的替代方案。它的架構更加清晰,一個protocol貫穿整個gRPC服務呼叫的始終。不再有額外的HTTP邏輯,所有的資料介面都在 .proto
檔案中定義。整個呼叫過程就是客戶端向gRPC服務傳送Protobuf訊息並從服務獲取Protobuf訊息。
我們僅需要一個元件就能達到這種比較好的效果。
Envoy 所扮演的角色
這裡必須承認,我之前講gRPC-Web直接呼叫gRPC服務的這種說法不是完全正確的。使用gRPC-Web的客戶端呼叫仍然需要轉換成對於gRPC友好的呼叫。Envoy填補了這個角色。同時Envoy也是gRPC-Web內建的預設服務閘道器。
下圖中展示了Envoy結合gRPC-Web使用。圖中Web App呼叫了一個gRPC服務,該服務又依賴另外兩個gRPC服務。Envoy 將 HTTP/1.1 請求轉換成 HTTP/2 請求。底層其實還是需要進行HTTP協議的轉換,但客戶端和服務端都不需要考慮HTTP層的問題。
gRPC-Web明顯優於REST,因為它只需開發者建立一個Envoy並做一些基礎配置,而不需要自己建立轉換層。
Envoy 示例配置
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 8080 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/”
route:
cluster: auth_service
cors:
allow_origin:
- "*"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web
max_age: "1728000"
expose_headers: grpc-status,grpc-message
enabled: true
http_filters:
- name: envoy.grpc_web
- name: envoy.cors
- name: envoy.router
clusters:
- name: auth_service
connect_timeout: 0.25s
type: logical_dns
http2_protocol_options: {}
lb_policy: round_robin
hosts:
socket_address:
address: auth-server
port_value: 9090複製程式碼
總的來講它就是Envoy最基本的HTTP配置,只是有一點點區別:
一點 gRPC-Web 必須的自定義頭部:
x-grpc-web
,grpc-status
和grpc-message
(JavaScript 會自動處理它們)內建的
envoy.grpc_web
HTTP過濾器用來完成繁雜的gRPC-Web代理工作在
auth_service
配置中指定http2_protocol_options: {}
來獲取HTTP/2的連結
你只需要寫一點YAML配置就可以從額外的HTTP適配工作中解脫出來。你不用關心HTTP與gRPC的方法對映問題,也不用去StackOverflow找HTTP的哪個狀態碼對應gRPC的哪個狀態碼,更不需要將Proto訊息包裝成JSON。
新方式
gRPC-Web + Envoy為web開發提供了一種全新的方式,它能保證Protocol Buffers和gRPC的型別安全還規避了HTTP+REST中的很多常見問題。我推薦大家在自己的下一個專案中試試它。