如何使用 grpc-web 进行流式传输并在同一端口上使用 REST API?
How do I stream with grpc-web and use a REST API on the same port?
我有一个用 Go 编写的服务器,它使用 cmux
允许多个协议在同一个端口上 运行,但是 README 中指定了一个限制,说
cmux
matches the connection when it's accepted. For example, one connection can be either gRPC or REST, but not both. That is, we assume that a client connection is either used for gRPC or REST.
我需要浏览器能够从 grpc-web 流式传输并在同一端口上调用 REST API,但浏览器重复使用相同的连接并导致 muxing 无法工作。
这是一个很难识别的问题。由于浏览器更喜欢使用现有的 TCP 连接进行流水线操作,多路复用器往往会将数据包发送到错误的协议处理程序,例如它可能会将 grpc-web
数据包发送到 REST,反之亦然。幸运的是,有一个非常简单的解决方案:
package main
listener := net.Listen("tcp", ":2289")
multiplexer := cmux.New(listener)
grpcListener := multiplexer.Match(cmux.HTTP2())
httpListener := multiplexer.Match(cmux.HTTP1Fast())
grpcServer := grpc.Server()
wrappedGrpc := grpcweb.WrapServer(grpcServer)
go func() {
httpServer := echo.New()
(&http.Server{
Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if strings.Contains(req.Header.Get("Access-Control-Request-Headers"), "x-grpc-web") || req.Header.Get("x-grpc-web") == "1" || req.Header.Get("Sec-Websocket-Protocol") == "grpc-websockets" {
inst.API.GrpcWebServer.ServeHTTP(resp, req)
} else {
httpServer.ServeHTTP(resp, req)
}
}),
}).Serve(httpListener)
}()
go func() {
grpcServer.Serve(grpcListener)
}()
go func() {
multiplexer.Serve()
}()
这是如何工作的?
本质上,我们没有使用 cmux
的默认多路复用(仅多路复用 per-connection),而是在所有传入的 HTTP 请求上注册了一个新的迷你 HTTP 服务器处理程序,然后让我们明确检查headers 并直接调用处理程序。
我有一个用 Go 编写的服务器,它使用 cmux
允许多个协议在同一个端口上 运行,但是 README 中指定了一个限制,说
cmux
matches the connection when it's accepted. For example, one connection can be either gRPC or REST, but not both. That is, we assume that a client connection is either used for gRPC or REST.
我需要浏览器能够从 grpc-web 流式传输并在同一端口上调用 REST API,但浏览器重复使用相同的连接并导致 muxing 无法工作。
这是一个很难识别的问题。由于浏览器更喜欢使用现有的 TCP 连接进行流水线操作,多路复用器往往会将数据包发送到错误的协议处理程序,例如它可能会将 grpc-web
数据包发送到 REST,反之亦然。幸运的是,有一个非常简单的解决方案:
package main
listener := net.Listen("tcp", ":2289")
multiplexer := cmux.New(listener)
grpcListener := multiplexer.Match(cmux.HTTP2())
httpListener := multiplexer.Match(cmux.HTTP1Fast())
grpcServer := grpc.Server()
wrappedGrpc := grpcweb.WrapServer(grpcServer)
go func() {
httpServer := echo.New()
(&http.Server{
Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if strings.Contains(req.Header.Get("Access-Control-Request-Headers"), "x-grpc-web") || req.Header.Get("x-grpc-web") == "1" || req.Header.Get("Sec-Websocket-Protocol") == "grpc-websockets" {
inst.API.GrpcWebServer.ServeHTTP(resp, req)
} else {
httpServer.ServeHTTP(resp, req)
}
}),
}).Serve(httpListener)
}()
go func() {
grpcServer.Serve(grpcListener)
}()
go func() {
multiplexer.Serve()
}()
这是如何工作的?
本质上,我们没有使用 cmux
的默认多路复用(仅多路复用 per-connection),而是在所有传入的 HTTP 请求上注册了一个新的迷你 HTTP 服务器处理程序,然后让我们明确检查headers 并直接调用处理程序。