h2c 实现grpc与http共用一个端口

最后发布时间:2026-05-21 16:07:35 浏览量:

下面给你一个:

Gin/Fiber REST API
+
gRPC
+
h2c
+
同一个端口

的完整 Go 示例。

这是现代 Go 微服务非常推荐的结构。


一、最终效果

同一个端口:

localhost:8080

同时支持:


REST

GET /ping

gRPC

/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

三、安装依赖


protoc 插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Go 依赖

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

四、proto 文件

api/proto/hello/v1/hello.proto

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;
}

五、生成 gRPC 代码

protoc \
  --go_out=. \
  --go-grpc_out=. \
  --go_opt=paths=source_relative \
  --go-grpc_opt=paths=source_relative \
  api/proto/hello/v1/hello.proto

六、gRPC 服务实现

internal/service/hello_service.go

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
}

七、核心:h2c mixed handler

cmd/server/main.go

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

九、测试 REST

curl http://localhost:8080/ping

结果:

{
  "message": "pong"
}

十、测试 gRPC

推荐:

grpcurl

安装:

go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

调用

grpcurl \
  -plaintext \
  -d '{"name":"Edward"}' \
  localhost:8080 \
  hello.v1.HelloService/SayHello

结果:

{
  "message": "Hello Edward"
}

十一、为什么必须 h2c.NewHandler

因为:

普通:

http.Server

默认:

不支持 h2c

h2c.NewHandler:

负责:

HTTP/2 cleartext upgrade

十二、为什么判断 ProtoMajor == 2

因为:

gRPC 必须:

HTTP/2

十三、为什么判断 Content-Type

避免:

普通 HTTP/2 请求

误进入 grpc server。


十四、TLS 场景

生产环境一般:

Nginx/Traefik
终止 TLS

Go 内部:

h2c 明文

非常常见。


十五、现代推荐架构(你很适合)

React Frontend
        |
     HTTPS
        |
+----------------+
| Nginx/Traefik  |
+----------------+
        |
      h2c
        |
+----------------+
| Go Server      |
| REST + gRPC    |
+----------------+

十六、进一步升级

你后面还可以:


1. JWT Interceptor


2. grpc-gateway


3. OpenTelemetry

OpenTelemetry


4. Prometheus metrics

Prometheus


5. Streaming

例如:

rpc StreamLogs(LogRequest)
    returns (stream LogMessage);

特别适合你:

  • workflow
  • AI token stream
  • 实时日志

十七、一句话总结

h2c mixed handler 的核心:

同一个 http.Server
↓
判断:
是不是 gRPC 请求
↓
gRPC -> grpcServer
REST -> Gin/Fiber