gRPC-Gateway 简介

gRPCHTTP/JSON APIgRPCHTTP/JSON API
gRPCHTTP/JSONAPI
Yes
gRPC-GatewayGoogle protocol buffers compiler protocprotobuf servicereverse-proxy serverRESTful HTTP APIgRPCgoogle.api.httpannotations
gRPCHTTP/JSONAPI

开始之前

在开始编码之前,我们必须安装一些工具。

Go gRPC Serverhttps://golang.org/dl/Go
Gogo get
$ go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
$ go get google.golang.org/protobuf/cmd/protoc-gen-go
$ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc

这将安装我们生成存根所需的协议生成器插件。确保将 GOPATH/bin 添加到 PATH 中,以便通过 go get 安装的可执行文件在

我们将在本教程的新模块中进行工作,因此,请立即在您选择的文件夹中创建该模块:

创建 go.mod 文件

go mod initmodulego.mod
go mod initmodulegithub.com/myuser/myrepomodulemoduleURL
$ go mod init github.com/myuser/myrepo
go: creating new go.mod: module github.com/myuser/myrepo
go mod initgo.modmoduleGogo.modmodulemodule

用 gRPC 创建一个简单的 hello world

gRPC-Gatewayhello world gRPC

使用 protocol buffers 定义 gRPC service

gRPCprotoproto/helloworld/hello_world.proto
gRPC service
syntax = "proto3";

package helloworld;

// The greeting service definition
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

使用 buf 生成 stubs

Bufprotobuflintingbreaking change detectiongenerationhttps://docs.buf.build/installation/
buf.yamlBuf--config.json.yamlJSONYAML
.protoBufBuf.protoprotoc.protobuf.proto
.protoproto
version: v1beta1
name: buf.build/myuser/myrepo
build:
  roots:
    - proto
GotypegRPC stubsbuf.gen.yaml
version: v1beta1
plugins:
  - name: go
    out: proto
    opt: paths=source_relative
  - name: go-grpc
    out: proto
    opt: paths=source_relative
gogo-grpcGo typesgRPC serviceprotopath=source_relative.proto

然后运行:

$ buf generate
protoprotobuf*.pb.go*_grpc.pb.go

使用 protoc 生成 stubs

protocGo stubsprotoproto
$ protoc -I ./proto \
   --go_out ./proto --go_opt paths=source_relative \
   --go-grpc_out ./proto --go-grpc_opt paths=source_relative \
   ./proto/helloworld/hello_world.proto
gogo-grpcGo typesgRPC serviceprotopath=source_relative.proto
proto/helloworld/hello_world.proto*.pb.go*_grpc.pb.go

创建 main.go

main.gogithub.com/myuser/myrepogo.modimportproto/helloworld
package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"

	helloworldpb "github.com/myuser/myrepo/proto/helloworld"
)

type server struct{}

func NewServer() *server {
	return &server{}
}

func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
	return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil
}

func main() {
	// Create a listener on TCP port
	lis, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalln("Failed to listen:", err)
	}

	// Create a gRPC server object
	s := grpc.NewServer()
	// Attach the Greeter service to the server
	helloworldpb.RegisterGreeterServer(s, &server{})
	// Serve gRPC Server
	log.Println("Serving gRPC on 0.0.0.0:8080")
	log.Fatal(s.Serve(lis))
}

将 gRPC-Gateway 批注添加到现有的 proto 文件中

Go gRPCgRPC-Gateway
gRPCJSONprotocol buffersRPCgoogle.api.httpHTTP
google/api/http.protoprotoHTTP->gRPCPOST /v1/example/echoSayHello RPC
syntax = "proto3";

package helloworld;

import "google/api/annotations.proto";

// Here is the overall greeting service definition where we define all our endpoints
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/example/echo"
      body: "*"
    };
  }
}

// The request message containing the user's name
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

生成 gRPC-Gateway stubs

gRPC-GatewayprotogRPC-Gatewaystubs

使用 buf

gRPC-Gateway
version: v1beta1
plugins:
  - name: go
    out: proto
    opt: paths=source_relative
  - name: go-grpc
    out: proto
    opt: paths=source_relative,require_unimplemented_servers=false
  - name: grpc-gateway
    out: proto
    opt: paths=source_relative
googleapisbuf.yaml
version: v1beta1
name: buf.build/myuser/myrepo
deps:
  - buf.build/beta/googleapis
build:
  roots:
    - proto
buf beta mod update

就是这样!现在,如果您运行:

$ buf generate
*.gw.pb.go

使用 protoc

protocstubsprotogoogleapis
proto
├── google
│   └── api
│       ├── annotations.proto
│       └── http.proto
└── helloworld
    └── hello_world.proto
gRPC-Gatewayprotoc
$ protoc -I ./proto \
  --go_out ./proto --go_opt paths=source_relative \
  --go-grpc_out ./proto --go-grpc_opt paths=source_relative \
  --grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
  ./proto/helloworld/hello_world.proto
*.gw.pb.go
main.gogRPC-Gatewaymux
package main

import (
	"context"
	"log"
	"net"
	"net/http"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"

	helloworldpb "github.com/myuser/myrepo/proto/helloworld"
)

type server struct{
	helloworldpb.UnimplementedGreeterServer
}

func NewServer() *server {
	return &server{}
}

func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
	return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil
}

func main() {
	// Create a listener on TCP port
	lis, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalln("Failed to listen:", err)
	}

	// Create a gRPC server object
	s := grpc.NewServer()
	// Attach the Greeter service to the server
	helloworldpb.RegisterGreeterServer(s, &server{})
	// Serve gRPC server
	log.Println("Serving gRPC on 0.0.0.0:8080")
	go func() {
		log.Fatalln(s.Serve(lis))
	}()

	// Create a client connection to the gRPC server we just started
	// This is where the gRPC-Gateway proxies the requests
	conn, err := grpc.DialContext(
		context.Background(),
		"0.0.0.0:8080",
		grpc.WithBlock(),
		grpc.WithInsecure(),
	)
	if err != nil {
		log.Fatalln("Failed to dial server:", err)
	}

	gwmux := runtime.NewServeMux()
	// Register Greeter
	err = helloworldpb.RegisterGreeterHandler(context.Background(), gwmux, conn)
	if err != nil {
		log.Fatalln("Failed to register gateway:", err)
	}

	gwServer := &http.Server{
		Addr:    ":8090",
		Handler: gwmux,
	}

	log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090")
	log.Fatalln(gwServer.ListenAndServe())
}

测试 gRPC-Gateway

现在我们可以启动服务器了:

$ go run main.go
cURLHTTP
$ curl -X POST -k http://localhost:8090/v1/example/echo -d '{"name": " hello"}'
{"message":"hello world"}

Refs

  • https://github.com/iamrajiv/helloworld-grpc-gateway
  • https://grpc-ecosystem.github.io/grpc-gateway/docs/tutorials/introduction/