gRPC is a widely used communication protocol in the server environment. Thanks to Protobuf IDL, we can quickly generate Client and Server Stubs, which greatly improves development efficiency and avoids type errors during communication.
However, for web pages, although the browser supports HTTP/2, the web page code cannot force a request to go through HTTP/2 or HTTP/1.1, and the network layer is shielded by the browser. You can refer to the following article:
So if we want to move the gRPC mechanism to the browser, we need to add a protocol conversion layer in the middle to support both HTTP/1.x and HTTP/2.
There are currently two relatively mature solutions: gRPC-Gateway and gRPC-Web:
gRPC-Gateway¶
gRPC-Gateway is a community-driven project that generates an HTTP+JSON gateway server for gRPC by parsing the google.api.http declarations in the protobuf IDL. The user only needs to start this gateway server, which will accept requests and map them to the corresponding gRPC methods, and then call the corresponding service.
rpc GetUser(GetUserRequest) returns (GetUserResponse) {
(google.api.http) = {
get: "/v1/user/{id}"
}
};
gRPC-Web¶
The gRPC-Web project is an official gRPC solution for using gRPC in browsers. It is based on the following specifications, which transcode between gRPC and gRPC-Web:
Unlike gRPC-Gateway, gRPC-Web does not require additional IDL declarations. Its server still receives gRPC requests directly, but it makes some adjustments in data encoding and transmission to adapt to HTTP/1.x.
gRPC-Web payload can be binary or base64 encoded between client and server.
curl -v 'http://localhost:8080/api.v1.API/GetInfo' \
-H 'accept: application/grpc-web-text' \
-H 'x-grpc-web: 1' \
-H 'content-type: application/grpc-web-text' \
--data-raw 'AAAAAD4KPGh0dHBzOi8vdHdpdHRlci5jb20vY29vbFhpYW8vc3RhdHVzLzE0ODIyNTQ3MjU3ODk1NDQ0NDg/cz0yMA==' \
--compressed
* Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> POST /api.v1.API/GetInfo HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept-Encoding: deflate, gzip
> accept: application/grpc-web-text
> x-grpc-web: 1
> content-type: application/grpc-web-text
> Content-Length: 92
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Access-Control-Expose-Headers: Content-Type, Vary, Date, grpc-status, grpc-message
< Content-Type: application/grpc-web-text
< Vary: Origin
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
AAAAARSKAZACCgpoYXBweSB4aWFvEglAY29vbFhpYW8aRWh0dHBzOi8vcGJzLnR3aW1nLmNvbS9wcm9maWxlX2ltYWdlcy8xMzk4MTQ1NTE0NjY3ODQzNTg2Ly1rNHRheHJMLmpwZyKnAei/meS4que9keermeaPkOS+m+WFjei0ueeahCBtYWNPUyDlupTnlKjlm77moIcKCmh0dHBzOi8vdC5jby9Wd0p2RWVPQndnCgrmiJHmkJzkuobkuIDkuIsgT2JzaWRpYW4g5ZKMIE5vdGlvbgrmnInkupvnmoTnoa7mr5Tljp/phY3nmoTlpb3nnIvwn5mCIGh0dHBzOi8vdC5jby83a1lLSXozTzg3KgYI6PGJjwY=gAAAABBncnBjLXN0YXR1czogMA0K%
The reason for directly transmitting binary data is that it is difficult to debug during development. Only by capturing packets can we understand the content of the payload, which to some extent reduces development efficiency.
However, since the encoding and decoding of the payload heavily rely on the proto, it also largely prevents others from using the interface directly. Use the following tool to directly view the proto content, but you cannot know the meaning of each field, which is better than nothing.
Streaming in HTTP/1.x¶
Unlike HTTP/2, which is a full-duplex network protocol, in HTTP/1.x, the server cannot actively push data to the client. Therefore, there are two ways to achieve this:
- Websocket
The service negotiates with the client to upgrade the protocol to Websocket when receiving the request, and then data can be transmitted freely.
- PayloadChunk
Multiple records can be sent in a single response, separated by a boundary, which simulates server streaming.
As I introduced before, SSE also delivers data in this way, but through a unified standard, and the browser encapsulates another layer on the upper layer.
In both gRPC-Gateway and gRPC-Web, in order to implement ServerStreaming, they both use the TransferEncodingChunk scheme, they will add the header Transfer-Encoding:chunked to the response, so that the client will receive data from the server in chunks according to the boundary.
Using gRPC to interact with the front end¶
gRPC-Gateway | gRPC-Web | |
Unary | ✅ | ✅ |
ServerStreaming | ✅ | ⚠️(需要使用Base64编码) |
ClientStreaming | ⚠️(需要使用转发模式) | ❌ |
Bi-directionalStreaming | ❌ | ❌ |
Default encoding | JSON | ProtocolBuffers+Base64 |
Scenario | Suitable for systems that need to support both REST and gRPC interfaces, allowing for a smooth migration from REST to gRPC | Suitable for front-end and back-end applications that communicate directly using the gRPC protocol, providing support for HTTP/1.x |