下面给你一个:
Gin/Fiber REST API + gRPC + h2c + 同一个端口
的完整 Go 示例。
这是现代 Go 微服务非常推荐的结构。
同一个端口:
localhost:8080
同时支持:
GET /ping
/hello.v1.HelloService/SayHello
project/ ├── cmd/ │ └── server/ │ └── main.go │ ├── api/ │ └── proto/ │ └── hello/v1/ │ └── hello.proto │ ├── gen/ │ └── go/ │ └── hello/v1/ │ ├── hello.pb.go │ └── hello_grpc.pb.go │ ├── internal/ │ └── service/ │ └── hello_service.go │ ├── go.mod └── Makefile
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go get google.golang.org/grpc go get google.golang.org/protobuf go get golang.org/x/net/http2 go get golang.org/x/net/http2/h2c go get github.com/gin-gonic/gin
syntax = "proto3"; package hello.v1; option go_package = "project/gen/go/hello/v1;hellov1"; service HelloService { rpc SayHello(HelloRequest) returns (HelloResponse); } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; }
protoc \ --go_out=. \ --go-grpc_out=. \ --go_opt=paths=source_relative \ --go-grpc_opt=paths=source_relative \ api/proto/hello/v1/hello.proto
package service import ( "context" hellov1 "project/gen/go/hello/v1" ) type HelloService struct { hellov1.UnimplementedHelloServiceServer } func (s *HelloService) SayHello( ctx context.Context, req *hellov1.HelloRequest, ) (*hellov1.HelloResponse, error) { return &hellov1.HelloResponse{ Message: "Hello " + req.Name, }, nil }
package main import ( "log" "net/http" "strings" "github.com/gin-gonic/gin" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" "google.golang.org/grpc" hellov1 "project/gen/go/hello/v1" "project/internal/service" ) func main() { // ========================= // gRPC Server // ========================= grpcServer := grpc.NewServer() helloService := &service.HelloService{} hellov1.RegisterHelloServiceServer( grpcServer, helloService, ) // ========================= // REST API // ========================= router := gin.Default() router.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) // ========================= // Mixed Handler // ========================= mixedHandler := h2c.NewHandler( http.HandlerFunc(func( w http.ResponseWriter, r *http.Request, ) { // grpc request if r.ProtoMajor == 2 && strings.Contains( r.Header.Get("Content-Type"), "application/grpc", ) { grpcServer.ServeHTTP(w, r) return } // normal REST request router.ServeHTTP(w, r) }), &http2.Server{}, ) // ========================= // HTTP Server // ========================= server := &http.Server{ Addr: ":8080", Handler: mixedHandler, } log.Println("server started at :8080") if err := server.ListenAndServe(); err != nil { panic(err) } }
go run ./cmd/server
curl http://localhost:8080/ping
结果:
{ "message": "pong" }
推荐:
安装:
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
grpcurl \ -plaintext \ -d '{"name":"Edward"}' \ localhost:8080 \ hello.v1.HelloService/SayHello
{ "message": "Hello Edward" }
因为:
普通:
http.Server
默认:
不支持 h2c
h2c.NewHandler:
负责:
HTTP/2 cleartext upgrade
gRPC 必须:
HTTP/2
避免:
普通 HTTP/2 请求
误进入 grpc server。
生产环境一般:
Nginx/Traefik 终止 TLS
Go 内部:
h2c 明文
非常常见。
React Frontend | HTTPS | +----------------+ | Nginx/Traefik | +----------------+ | h2c | +----------------+ | Go Server | | REST + gRPC | +----------------+
你后面还可以:
例如:
rpc StreamLogs(LogRequest) returns (stream LogMessage);
特别适合你:
h2c mixed handler 的核心: 同一个 http.Server ↓ 判断: 是不是 gRPC 请求 ↓ gRPC -> grpcServer REST -> Gin/Fiber
Github开源生信云平台 DEMO