Skip to content

Commit 5bf80a2

Browse files
committed
Initial commit
0 parents  commit 5bf80a2

File tree

62 files changed

+2785
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2785
-0
lines changed

Diff for: .gitignore

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
HELP.md
2+
.gradle
3+
build/
4+
!gradle/wrapper/gradle-wrapper.jar
5+
!**/src/main/**/build/
6+
!**/src/test/**/build/
7+
8+
### STS ###
9+
.apt_generated
10+
.classpath
11+
.factorypath
12+
.project
13+
.settings
14+
.springBeans
15+
.sts4-cache
16+
bin/
17+
!**/src/main/**/bin/
18+
!**/src/test/**/bin/
19+
20+
### IntelliJ IDEA ###
21+
.idea
22+
*.iws
23+
*.iml
24+
*.ipr
25+
out/
26+
!**/src/main/**/out/
27+
!**/src/test/**/out/
28+
29+
### NetBeans ###
30+
/nbproject/private/
31+
/nbbuild/
32+
/dist/
33+
/nbdist/
34+
/.nb-gradle/
35+
36+
### VS Code ###
37+
.vscode/

Diff for: README.adoc

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
= Spring Cloud Gateway and gRPC
2+
3+
Starting from `3.1.0-RC1`, Spring Cloud Gateway included support for gRPC and HTTP/2.
4+
5+
We will introduce the basic concepts behind gRPC and how to configure it with two examples:
6+
7+
* One that showcases how Spring Cloud Gateway can transparently re-route gRPC traffic without needing to know the proto definition and without having to modify our existing gRPC servers.
8+
9+
* Another that showcases how we can create a custom filter in Spring Cloud Gateway to transform a JSON payload to a gRPC message.
10+
11+
12+
== Introduction to gRPC and HTTP/2
13+
14+
HTTP/2 makes our applications faster, simpler, and more robust. Reducing latency by enabling request and response
15+
multiplexing, adding efficient compression of HTTP header fields, and adding support for request prioritization and
16+
server push.
17+
18+
The reduction in the number of connections is particularly important when improving the performance of HTTPS: that way
19+
we have less expensive TLS handshakes, more efficient session reuse, reducing client and server resources.
20+
21+
HTTP/2 provides two mechanisms for negotiating the application level protocol:
22+
23+
* `H2C` HTTP/2.0 support with clear-text
24+
* `H2` HTTP/2.0 support with TLS
25+
26+
Even though `reactor-netty` has support for `H2C` clear-text protocol, Spring Cloud Gateway requires `H2` with TLS to
27+
assure transport security.
28+
29+
HTTP/2 adds a binary framing layer, which is how the HTTP messages are encapsulated and transferred between the client
30+
and server, enabling more efficient ways to transfer data.
31+
32+
Thanks to https://github.com/reactor/reactor-netty[reactor-netty] and its HTTP/2 support, we were able to extend
33+
Spring Cloud Gateway to support gRPC.
34+
35+
https://grpc.io/[gRPC] is a high-performance Remote Procedure Call framework that can run in any environment. It
36+
provides bi-directional streaming, and it's based on HTTP/2.
37+
38+
gRPC services can be defined using Protocol Buffers, a powerful binary serialization toolset and language, and
39+
provides tools for generating clients and servers across different languages.
40+
41+
== Getting started
42+
43+
In order to enable gRPC in Spring Cloud Gateway, we need to enable HTTP/2 and SSL in our project https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html[by adding a keystore], this can be done
44+
through configuration by adding the following:
45+
46+
[source,yaml]
47+
----
48+
server:
49+
http2:
50+
enabled: true
51+
ssl:
52+
key-store-type: PKCS12
53+
key-store: classpath:keystore.p12
54+
key-store-password: password
55+
key-password: password
56+
enabled: true
57+
58+
----
59+
60+
Now that we have it enabled, we can create a route that redirects traffic to a gRPC server and take advantage of the
61+
existing filters and predicates, for example, this route will redirect traffic that comes from any path starting
62+
with `grpc` to a local server in the port `6565` and add header `X-Request-header` with the value `header-value`:
63+
64+
[source,yaml]
65+
----
66+
spring:
67+
cloud:
68+
gateway:
69+
routes:
70+
- id: grpc
71+
uri: https://localhost:6565
72+
predicates:
73+
- Path=/grpc/**
74+
filters:
75+
- AddResponseHeader=X-Request-header, header-value
76+
----
77+
78+
== Running gRPC to gRPC
79+
80+
An end to end example can be found in this repository with the following parts:
81+
82+
image::grpc-simple-gateway.png[grpc-simple-gateway]
83+
84+
85+
* A `grpc-server` that exposes a `HelloService`, and gRPC endpoint to receive a `HelloRequest` and return
86+
a `HelloResponse`:
87+
[source,protobuf]
88+
----
89+
syntax = "proto3";
90+
91+
message HelloRequest {
92+
string firstName = 1;
93+
string lastName = 2;
94+
}
95+
96+
message HelloResponse {
97+
string greeting = 1;
98+
}
99+
100+
service HelloService {
101+
rpc hello(HelloRequest) returns (HelloResponse);
102+
}
103+
----
104+
105+
The server will concatenate a salutation with `firstName` and a `lastName` and respond with a `greeting`.
106+
107+
For example, this input:
108+
109+
[source,text]
110+
----
111+
firstName: Saul
112+
lastName: Hudson
113+
----
114+
115+
Will output:
116+
117+
[source,text]
118+
----
119+
greeting: Hello, Saul Hudson
120+
----
121+
122+
* A `grpc-client`, in charge of sending the `HelloRequest` to the `HelloService`.
123+
124+
* `grpc-simple-gateway` that routes the requests and adds a header with the configuration mentioned above. Note that this gateway application does not have any dependency to gRPC nor to the proto definition used by client and server.
125+
126+
At the moment there is just one route that will forward everything to the `grpc-server`:
127+
128+
[source,yaml]
129+
----
130+
routes:
131+
- id: grpc
132+
uri: https://localhost:6565
133+
predicates:
134+
- Path=/**
135+
filters:
136+
- AddResponseHeader=X-Request-header, header-value
137+
----
138+
139+
To start the server that is going to be listening to requests:
140+
141+
[source,shell]
142+
----
143+
./gradlew :grpc-server:bootRun
144+
----
145+
146+
Then, we start the gateway that is going to re-route the gRPC requests:
147+
148+
[source,shell]
149+
----
150+
./gradlew :grpc-simple-gateway:bootRun
151+
----
152+
153+
Finally, we can use the client that points to the gateway application:
154+
155+
[source,shell]
156+
----
157+
./gradlew :grpc-client:bootRun
158+
----
159+
160+
The gateway routes and filters can be modified in `grpc-simple-gateway/src/main/resources/application.yaml`
161+
162+
== Running JSON to gRPC with a custom filter
163+
164+
Thanks to Spring Cloud Gateway flexibility, it is possible to create a custom filter to transform from a JSON payload to
165+
a gRPC message.
166+
167+
Even though it will have a performance impact since we have to serialize and deserialize the requests in the gateway and creating a channel from it,
168+
it is a common pattern if you want to expose a JSON API while maintaining internal compatibility.
169+
170+
For that, we can extend our `grpc-json-gateway` to include the `proto` definition with the message we want to send.
171+
172+
image::grpc-json-gateway.png[grpc-json-gateway]
173+
174+
175+
Spring Cloud Gateway contains a mechanism to create custom filters allowing us to intercept requests and add custom logic to them.
176+
177+
For this particular scenario, we are going to deserialize the JSON request and create a gRPC channel that will send a message to the `grpc-server`.
178+
179+
[source,java]
180+
----
181+
static class GRPCResponseDecorator extends ServerHttpResponseDecorator {
182+
183+
@Override
184+
public Mono<Void> writeWith(Publisher<?extends DataBuffer> body) {
185+
exchange.getResponse().getHeaders().set("Content-Type", "application/json");
186+
187+
URI requestURI = exchange.getRequest().getURI();
188+
ManagedChannel channel = createSecuredChannel(requestURI.getHost(), 6565);
189+
190+
return getDelegate().writeWith(deserializeJSONRequest()
191+
.map(jsonRequest -> {
192+
String firstName = jsonRequest.getFirstName();
193+
String lastName = jsonRequest.getLastName();
194+
return HelloServiceGrpc.newBlockingStub(channel)
195+
.hello(HelloRequest.newBuilder()
196+
.setFirstName(firstName)
197+
.setLastName(lastName)
198+
.build());
199+
})
200+
.map(this::serialiseJSONResponse)
201+
.map(wrapGRPCResponse())
202+
.cast(DataBuffer.class)
203+
.last());
204+
}
205+
}
206+
----
207+
208+
The full implementation can be found
209+
in: `grpc-json-gateway/src/main/java/com/example/grpcserver/hello/JSONToGRPCFilterFactory.java`
210+
211+
Using the same `grpc-server`, we can start the gateway with the custom filter with:
212+
213+
[source,shell]
214+
----
215+
./gradlew :grpc-json-gateway:bootRun
216+
----
217+
218+
And send JSON requests to the `grpc-json-gateway` using, for example, `curl`:
219+
220+
[source,bash]
221+
----
222+
curl -XPOST 'https://localhost:8091/json/hello' -d '{"firstName":"Duff","lastName":"McKagan"}' -k -H"Content-Type: application/json" -v
223+
----
224+
225+
We see how the gateway application forwards the requests and returns the JSON payload with the new `Content-Type` header:
226+
227+
[source,bash]
228+
----
229+
< HTTP/2 200
230+
< content-type: application/json
231+
< content-length: 34
232+
<
233+
* Connection #0 to host localhost left intact
234+
{"greeting":"Hello, Duff McKagan"}
235+
----
236+
237+
== Next Steps
238+
239+
In this post, we've looked at a few examples of how gRPC can be integrated within Spring Cloud Gateway. I’d love to know
240+
what other usages you've found to be helpful in your experiences.

Diff for: gradle/wrapper/gradle-wrapper.jar

58.1 KB
Binary file not shown.

Diff for: gradle/wrapper/gradle-wrapper.properties

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
4+
zipStoreBase=GRADLE_USER_HOME
5+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)