REST 的替代者:Envoy + gRPC-Web

ServiceMesher發表於2019-01-28
原文: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 的替代者:Envoy + 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層的問題。

REST 的替代者:Envoy + gRPC-Web

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-webgrpc-statusgrpc-message (JavaScript 會自動處理它們)

  • 內建的envoy.grpc_webHTTP過濾器用來完成繁雜的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中的很多常見問題。我推薦大家在自己的下一個專案中試試它。

REST 的替代者:Envoy + gRPC-Web


相關文章