領先一步
VMware 提供培訓和認證,助你快速前進。
瞭解更多從 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 訊息。
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
上的伺服器,並新增頭部 X-Request-header
,其值為 header-value
spring:
cloud:
gateway:
routes:
- id: grpc
uri: https://:6565
predicates:
- Path=/grpc/**
filters:
- AddResponseHeader=X-Request-header, header-value
端到端示例可以在這個倉庫中找到,包含以下部分
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); }
伺服器會將稱謂與 firstName
和 lastName
拼接起來,並以 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 中修改
得益於 Spring Cloud Gateway 的靈活性,可以建立一個自定義過濾器,將 JSON 負載轉換為 gRPC 訊息。
儘管這會帶來效能影響,因為我們必須在閘道器中序列化和反序列化請求並從中建立一個通道,但如果想要暴露 JSON API 同時保持內部相容性,這是一種常見的模式。
為此,我們可以擴充套件我們的 grpc-json-gateway
以包含帶有我們想要傳送的訊息的 proto
定義。
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
然後使用例如 curl
向 grpc-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 整合。我希望瞭解你在實際經驗中發現的其他有用的用法。