Spring Cloud Gateway 和 gRPC

工程 | 阿爾貝託·C·里奧斯 | 2021年12月8日 | ...

從3.1.0版本開始,作為 Spring Cloud 2021.0.0(又名 Jubilee)釋出列車的一部分,Spring Cloud Gateway 包含了對 gRPC 和 HTTP/2 的支援。

我們將介紹 gRPC 背後的基本概念以及如何透過兩個示例進行配置

  • 其中一個示例展示了 Spring Cloud Gateway 如何透明地重新路由 gRPC 流量,而無需瞭解 proto 定義,也無需修改我們現有的 gRPC 伺服器。

  • 另一個示例展示了我們如何在 Spring Cloud Gateway 中建立一個自定義過濾器,將 JSON 有效負載轉換為 gRPC 訊息。

gRPC 和 HTTP/2 簡介

HTTP/2 使我們的應用程式更快、更簡單、更健壯。它透過啟用請求和響應多路複用、新增高效的 HTTP 頭部欄位壓縮以及新增對請求優先順序和伺服器推送的支援來減少延遲。

連線數量的減少對於提高 HTTPS 效能尤為重要:這樣我們就可以減少昂貴的 TLS 握手,更有效地重用會話,從而減少客戶端和伺服器的資源。

HTTP/2 提供了兩種機制來協商應用層協議

  • H2C 支援明文 HTTP/2.0

  • H2 支援 TLS 的 HTTP/2.0

儘管 reactor-netty 支援 H2C 明文協議,但 Spring Cloud Gateway 要求使用帶 TLS 的 H2 以確保傳輸安全。

HTTP/2 添加了一個二進位制幀層,這是 HTTP 訊息在客戶端和伺服器之間封裝和傳輸的方式,從而實現了更有效的資料傳輸方式。

得益於 reactor-netty 及其 HTTP/2 支援,我們能夠擴充套件 Spring Cloud Gateway 以支援 gRPC。

gRPC 是一個高效能的遠端過程呼叫框架,可以在任何環境中執行。它提供雙向流式傳輸,並且基於 HTTP/2。

gRPC 服務可以使用 Protocol Buffers 定義,Protocol Buffers 是一個功能強大的二進位制序列化工具集和語言,並提供用於生成不同語言的客戶端和伺服器的工具。

開始

為了在 Spring Cloud Gateway 中啟用 gRPC,我們需要在專案中啟用 HTTP/2 和 SSL,透過新增金鑰庫,這可以透過新增以下配置來完成

server:
  http2:
    enabled: true
  ssl:
    key-store-type: PKCS12
    key-store: classpath:keystore.p12
    key-store-password: password
    key-password: password
    enabled: true

現在我們已經啟用了它,我們可以建立一個路由,將流量重定向到 gRPC 伺服器並利用現有的過濾器和謂詞,例如,此路由將把所有以 grpc 開頭的路徑的流量重定向到埠 6565 上的本地伺服器,並新增值為 header-valueX-Request-header

spring:
  cloud:
    gateway:
      routes:
        - id: grpc
          uri: https://:6565
          predicates:
            - Path=/grpc/**
          filters:
            - AddResponseHeader=X-Request-header, header-value

執行 gRPC 到 gRPC

此倉庫中包含一個端到端示例,包含以下部分:

grpc-simple-gateway

  • 一個 grpc-server,暴露一個 HelloService 和 gRPC 端點,用於接收 HelloRequest 並返回 HelloResponse

syntax = "proto3";

message HelloRequest { string firstName = 1; string lastName = 2; }

message HelloResponse { string greeting = 1; }

service HelloService { rpc hello(HelloRequest) returns (HelloResponse); }

伺服器將把稱呼與 firstNamelastName 連線起來,並以 greeting 作為響應。

例如,此輸入

firstName: Saul
lastName: Hudson

將輸出

greeting: Hello, Saul Hudson
  • 一個 grpc-client,負責向 HelloService 傳送 HelloRequest

  • grpc-simple-gateway 負責路由請求並新增上述配置的頭。請注意,此閘道器應用程式不依賴於 gRPC,也不依賴於客戶端和伺服器使用的 proto 定義。

目前只有一個路由會將所有內容轉發到 grpc-server

      routes:
        - id: grpc
          uri: https://:6565
          predicates:
            - Path=/**
          filters:
            - AddResponseHeader=X-Request-header, header-value

啟動將監聽請求的伺服器

 ./gradlew :grpc-server:bootRun

然後,我們啟動閘道器,它將重新路由 gRPC 請求

./gradlew :grpc-simple-gateway:bootRun

最後,我們可以使用指向閘道器應用程式的客戶端

./gradlew :grpc-client:bootRun

閘道器路由和過濾器可以在 grpc-simple-gateway/src/main/resources/application.yaml 中修改

使用自定義過濾器執行 JSON 到 gRPC

得益於 Spring Cloud Gateway 的靈活性,可以建立自定義過濾器來將 JSON 有效負載轉換為 gRPC 訊息。

儘管由於我們必須在閘道器中序列化和反序列化請求並從中建立通道,它會對效能產生影響,但如果您想公開 JSON API 同時保持內部相容性,這是一種常見的模式。

為此,我們可以擴充套件我們的 grpc-json-gateway 以包含帶有我們想要傳送的訊息的 proto 定義。

grpc-json-gateway

Spring Cloud Gateway 包含一種建立自定義過濾器的機制,允許我們攔截請求並向其新增自定義邏輯。

對於這種特殊情況,我們將反序列化 JSON 請求並建立一個 gRPC 通道,該通道將向 grpc-server 傳送訊息。

static class GRPCResponseDecorator extends ServerHttpResponseDecorator {

  @Override
  public Mono<Void> writeWith(Publisher<?extends DataBuffer> body) {
    exchange.getResponse().getHeaders().set("Content-Type", "application/json");

    URI requestURI = exchange.getRequest().getURI();
    ManagedChannel channel = createSecuredChannel(requestURI.getHost(), 6565);

    return getDelegate().writeWith(deserializeJSONRequest()
            .map(jsonRequest -> {
                String firstName = jsonRequest.getFirstName();
                String lastName = jsonRequest.getLastName();
                return HelloServiceGrpc.newBlockingStub(channel)
                        .hello(HelloRequest.newBuilder()
                                .setFirstName(firstName)
                                .setLastName(lastName)
                                .build());
            })
            .map(this::serialiseJSONResponse)
            .map(wrapGRPCResponse())
            .cast(DataBuffer.class)
            .last());
  }
}

完整實現可在以下位置找到:grpc-json-gateway/src/main/java/com/example/grpcserver/hello/JSONToGRPCFilterFactory.java

使用相同的 grpc-server,我們可以透過以下方式啟動帶自定義過濾器的閘道器

./gradlew :grpc-json-gateway:bootRun

並使用例如 curlgrpc-json-gateway 傳送 JSON 請求

curl -XPOST 'https://:8091/json/hello' -d '{"firstName":"Duff","lastName":"McKagan"}' -k -H"Content-Type: application/json" -v

我們看到閘道器應用程式如何轉發請求並返回帶有新 Content-Type 頭的 JSON 有效負載

< HTTP/2 200
< content-type: application/json
< content-length: 34
<
* Connection #0 to host localhost left intact
{"greeting":"Hello, Duff McKagan"}

下一步

在這篇文章中,我們探討了 gRPC 如何整合到 Spring Cloud Gateway 的幾個示例。我很想知道您在實踐中還發現了哪些其他有用的用法。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有