My current architecture is a mix of static files and REST endpoints and I want to migrate from REST to gRPC - which is working quite well in itself. To keep the number of processes low, I would like to serve the static files AND the gRPC-web from a single process, but behind different routes (e.g. /static
and /grpc
).
Can I somehow "serve" gRPC from a route in gin?
In the example below http.Server variant
works fine, but the GIN Server variant
does nothing except logging the request.
func (gs *GRPCWebServer) Init(servePort int, path string) {
gs.port = servePort
grpcServer := grpc.NewServer()
library.RegisterPermissionServiceServer(grpcServer, newPermissionService(path))
grpclog.SetLogger(log.New(os.Stdout, "grpc: ", log.LstdFlags))
wrappedServer := grpcweb.WrapServer(grpcServer)
// http.Server variant
//
// handler := func(resp http.ResponseWriter, req *http.Request) {
// wrappedServer.ServeHTTP(resp, req)
// }
// httpServer := http.Server{
// Addr: fmt.Sprintf(":%d", gs.port),
// Handler: http.HandlerFunc(handler),
// }
// grpclog.Printf("Starting gRPC-web server, port: %d", gs.port)
// if err := httpServer.ListenAndServe(); err != nil {
// grpclog.Fatalf("failed starting gRPC-web server: %v", err)
// }
// GIN Server variant
//
ginServer := gin.New()
ginServer.Use(gin.Logger())
ginServer.Use(gin.Recovery())
ginServer.Group("/grpc", func(c *gin.Context) {
wrappedServer.ServeHTTP(c.Writer, c.Request)
})
ginServer.Run(fmt.Sprintf(":%d", servePort+1))
}
Comment From: VoyTechnology
I recommend using https://github.com/soheilhy/cmux. I've used it on plain gRPC with a webserver running on the same port and it worked fine, although I am unsure how well it would work with grpc-web
Comment From: ykensuke
This is how I implemented in order to run gin and grpcweb servers on the same port. It seems be working fine so far.
func main() {
http.ListenAndServe(":8000", NewHandler())
}
type Handler struct {
ginHandler *gin.Engine
grpcwebHandler *grpcweb.WrappedGrpcServer
}
func NewHandler() *Handler {
router := gin.Default()
router.GET("/", handleFunc)
grpcServer := grpc.NewServer()
library.RegisterPermissionServiceServer(grpcServer, newPermissionService(path))
wrappedServer := grpcweb.WrapServer(grpcServer)
return &Handler{
ginHandler: router,
grpcwebHandler: wrappedServer,
}
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
contentType := req.Header.Get("Content-Type")
if contentType == "application/grpc-web+proto" {
h.grpcwebHandler.ServeHTTP(w, req)
return
}
h.ginHandler.ServeHTTP(w, req)
}
I've tried cmux but I finally gave up setting up it. I don't know how it should be set up and if it's possible. Both of gin and grpcweb are http2 based. cmux doesn't seem to be needed.
Comment From: a67793581
I recommend using https://github.com/soheilhy/cmux. I've used it on plain gRPC with a webserver running on the same port and it worked fine, although I am unsure how well it would work with grpc-web 还可以使用这个方法 router.Use(func(ctx *gin.Context) { if ctx.Request.ProtoMajor == 2 && strings.HasPrefix(ctx.GetHeader("Content-Type"), "application/grpc") { ctx.Status(http.StatusOK) grpcServer.ServeHTTP(ctx.Writer, ctx.Request) ctx.Abort() return } ctx.Next() }) router.UseH2C = true srv := &http.Server{ Addr: fmt.Sprintf(":%d", config.GetConfig().HttpPort), Handler: router.Handler(), }