go语言gRPC系列(三) - 使用grpc-gateway同时提供HTTP和gRPC服务

1. gRPC提供HTTP服务#

1.1 存在的意义#

在某些场景下单纯的RPC服务不能满足提供的服务需求的话,还是需要提供HTTP服务作为补充,gRPC一样可以提供HTTP服务。

  • 注意:gRPC提供的HTTP接口是基于HTTP 2.0

1.2 代码示例#

Copy
package main import ( "fmt" "gomicro-quickstart/grpc_server/service" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "log" "net/http" ) func main() { // 1. 引用证书 tls, err := credentials.NewServerTLSFromFile("grpc_server/keys/server.crt", "grpc_server/keys/server_no_password.key") if err != nil { log.Fatal("服务端获取证书失败: ", err) } // 2. new一个grpc的server,并且加入证书 rpcServer := grpc.NewServer(grpc.Creds(tls)) // 3. 将刚刚我们新建的ProdService注册进去 service.RegisterProdServiceServer(rpcServer, new(service.ProdService)) // 4. 新建一个路由,并传入rpcServer mux := http.NewServeMux() mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Println(request) rpcServer.ServeHTTP(writer, request) }) // 5. 定义httpServer,监听8082 httpServer := http.Server{ Addr: ":8082", Handler: mux, } // 6. 以https形式监听httpServer httpServer.ListenAndServeTLS("grpc_server/keys/server.crt", "grpc_server/keys/server_no_password.key") }

1.3 使用postman尝试调用#

运行上述的代码,然后postman访问8082端口,提示访问这个接口需要http/2协议

1.4 gRPC客户端代码调用#

针对上一节的客户端调用的代码,我们不需要修改即可以直接访问

即直接调用protoc产生的go文件中的方法

我们服务端代码因为打印出了,http request的内容

所以我们查看一下通过客户端调用,会打印出什么,可以看到

  • 请求的路径是/service.ProdService/GetProductStock,是{服务名}/{方法名}的格式
  • 协议是:http/2

2. 使用grpc-gateway同时提供HTTP和gRPC服务#

2.1 前言#

某些场景下需要同时要提供REST API服务gRPC服务,维护两个版本的服务显然不太合理,所以grpc-gateway诞生了。

原理:通过protobuf的自定义option实现了一个网关,服务端同时开启gRPCHTTP 1.1服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。

按照官方的结构说明如图

2.2 安装#

执行安装以下三个

Copy
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger go get -u github.com/golang/protobuf/protoc-gen-go

2.3 目录结构#

这里用到了google官方Api中的两个proto描述文件,直接拷贝不要做修改,里面定义了protocol buffer扩展的HTTP option,为grpc的http转换提供支持。

Copy
|—— hello_http/ |—— client/ |—— main.go // 客户端 |—— server/ |—— main.go // GRPC服务端 |—— server_http/ |—— main.go // HTTP服务端 |—— proto/ |—— google // googleApi http-proto定义 |—— api |—— annotations.proto |—— annotations.pb.go |—— http.proto |—— http.pb.go |—— hello_http/ |—— hello_http.proto // proto描述文件 |—— hello_http.pb.go // proto编译后文件 |—— hello_http_pb.gw.go // gateway编译后文件

2.4 示例代码#

2.4.1 编写proto描述文件:proto/hello_http.proto#

SayHello方法定义中增加了http option, POST方式,路由为/example/echo

Copy
syntax = "proto3"; package hello_http; option go_package = "hello_http"; import "google/api/annotations.proto"; // 定义Hello服务 service HelloHTTP { // 定义SayHello方法 rpc SayHello(HelloHTTPRequest) returns (HelloHTTPResponse) { // http option option (google.api.http) = { post: "/example/echo" body: "*" }; } } // HelloRequest 请求结构 message HelloHTTPRequest { string name = 1; } // HelloResponse 响应结构 message HelloHTTPResponse { string message = 1; }

2.4.2 编译proto#

Copy
$ cd proto # 编译google.api $ protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto # 编译hello_http.proto $ protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=github.com/jergoo/go-grpc-example/proto/google/api:. hello_http/*.proto # 编译hello_http.proto gateway $ protoc --grpc-gateway_out=logtostderr=true:. hello_http/hello_http.proto

2.4.3 实现HTTP服务端#

Copy
package main import ( "net/http" "github.com/grpc-ecosystem/grpc-gateway/runtime" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" gw "github.com/jergoo/go-grpc-example/proto/hello_http" ) func main() { // 1. 定义一个context ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() // grpc服务地址 endpoint := "127.0.0.1:50052" mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} // HTTP转grpc err := gw.RegisterHelloHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts) if err != nil { grpclog.Fatalf("Register handler err:%v\n", err) } grpclog.Println("HTTP Listen on 8080") http.ListenAndServe(":8080", mux) }

2.4.4 实现gRPC服务端#

Copy
package main import ( "fmt" "net" "net/http" pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包 "golang.org/x/net/context" "golang.org/x/net/trace" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" ) const ( // Address gRPC服务地址 Address = "127.0.0.1:50052" ) // 定义helloService并实现约定的接口 type helloService struct{} // HelloService Hello服务 var HelloService = helloService{} // SayHello 实现Hello服务接口 func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) { resp := new(pb.HelloResponse) resp.Message = fmt.Sprintf("Hello %s.", in.Name) return resp, nil } func main() { listen, err := net.Listen("tcp", Address) if err != nil { grpclog.Fatalf("failed to listen: %v", err) } // 实例化grpc Server s := grpc.NewServer() // 注册HelloService pb.RegisterHelloServer(s, HelloService) grpclog.Println("Listen on " + Address) s.Serve(listen) }

2.4.5 实现客户端#

Copy
package main import ( pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包 "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" ) const ( // Address gRPC服务地址 Address = "127.0.0.1:50052" ) func main() { // 连接 conn, err := grpc.Dial(Address, grpc.WithInsecure()) if err != nil { grpclog.Fatalln(err) } defer conn.Close() // 初始化客户端 c := pb.NewHelloClient(conn) // 调用方法 req := &pb.HelloRequest{Name: "gRPC"} res, err := c.SayHello(context.Background(), req) if err != nil { grpclog.Fatalln(err) } grpclog.Println(res.Message) }

2.5 运行并调用#

依次开启gRPC服务端和HTTP服务端

Copy
$ cd hello_http/server && go run main.go Listen on 127.0.0.1:50052 $ cd hello_http/server_http && go run main.go HTTP Listen on 8080

然后调用gRPC的客户端

Copy
$ cd hello_http/client && go run main.go Hello gRPC. # HTTP 请求 $ curl -X POST -k http://localhost:8080/example/echo -d '{"name": "gRPC-HTTP is working!"}' {"message":"Hello gRPC-HTTP is working!."}
posted @   宝树呐  阅读(7489)  评论(0编辑  收藏  举报
编辑推荐:
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示
CONTENTS