使用Google的protobuf,将REST端点映射到gRPC服务器

介绍一下,您可以使用Google提供的googleapis1源代码来支持REST和gRPC两种协议。

在需要使用HTTP/1.1进行通信的情况下,了解这一点会很方便。就我个人而言,在将请求转发到gRPC服务器时,我将其实现为ALB的目标组的健康检查端点。

我当时使用了 envoy 来搭建代理,但这一次我决定使用 grpc-gateway。

首先准备 gRPC 服务器。

由于我需要一个能快速运行的实例,所以借用了 grpc-go 的 helloworld。

git clone https://github.com/grpc/grpc-go

# 以降も基本的にこのディレクトリで作業する
cd grpc-go/examples/helloworld/

# gRPCサーバー起動
go run greeter_server/main.go

# クライアントコード実行で動作確認
go run greeter_client/main.go
2020/09/14 23:02:48 Greeting: Hello world

.proto 文件的内容如下所示。

syntax = "proto3";

option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

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

REST API 映射

你好世界.proto已修正

导入 annotations.proto,在 rpc 的定义中添加 option (google.api.http) {…}。

syntax = "proto3";

option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

// 追記
import "google/api/annotations.proto";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
      // 追記
      option (google.api.http) = {
        get: "/say_hello"
      };
  }
}

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

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

获取 googleapis 的源代码

go get -u github.com/googleapis/googleapis

只需要在编译时进行引用,所以甚至可以使用 git clone 之类的方式,都可以。

根据修改后的 protobuf 进行自动生成。

安装编译器插件

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

gRPC Server专用

这时候,在 protoc 命令中添加 -I 选项,以便能够引用到 google/api/annotations.proto。另外,由于现有的 helloworld_grpc.pb.go 和生成的客户端代码重复了,所以很抱歉,这次需要删除它们。

protoc \
  -I. -I$GOPATH/src/github.com/googleapis/googleapis \
  --go_out=plugins=grpc:. --go_opt=paths=source_relative \
  helloworld/helloworld.proto

// 申し訳ないが消す
rm helloworld/helloworld_grpc.pb.go

适用于代理服务器的

使用grpc-gateway的编译器插件进行类似的自动生成。

protoc -I. -I$GOPATH/src/github.com/googleapis/googleapis \
  --grpc-gateway_out=logtostderr=true,paths=source_relative:. \
  helloworld/helloworld.proto

修改 gRPC 服务器的实现。

根据生成的 helloworld.pb.go 文件,对服务器的实现进行修改。

package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

const (
    port = ":50051"
)

// 追記
type server struct {}

// 修正: ポインタレシーバに修正
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.GetName())
    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()

    // 修正: server を代入
    pb.RegisterGreeterServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

代理服务器的实施

package main

import (
    "flag"
    "net/http"

    "github.com/golang/glog"
    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    gw "google.golang.org/grpc/examples/helloworld/helloworld"
)

var (
    tasklistEndpoint = flag.String("tasklist_endpoint", "localhost:50051", "endpoint of YourService")
)

func run() error {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    mux := runtime.NewServeMux()
    opts := []grpc.DialOption{grpc.WithInsecure()}
    err := gw.RegisterGreeterHandlerFromEndpoint(ctx, mux, *tasklistEndpoint, opts)
    if err != nil {
        return err
    }

    return http.ListenAndServe(":8080", mux)
}

func main() {
    flag.Parse()
    defer glog.Flush()

    if err := run(); err != nil {
        glog.Fatal(err)
    }
}

验证动作

スクリーンショット 2020-09-15 15.00.18.png

如果你对设置POST或请求头也感兴趣,可以点击以下链接了解更多信息:https://grpc-ecosystem.github.io/grpc-gateway/

仅提供一种中文选择:
参考一下。

    • https://github.com/grpc-ecosystem/grpc-gateway

 

    • https://grpc.io/docs/languages/go/quickstart

 

    • https://github.com/grpc/grpc-go

 

    • https://budougumi0617.github.io/2018/02/03/grpc-gateway-for-rest-api/

 

    • https://qiita.com/ryu3/items/b2882d4f45c7f8485030

 

    https://grpc-ecosystem.github.io/grpc-gateway/
或者,https://github.com/grpc-ecosystem/grpc-gateway/tree/master/third_party/googleapis ↩
广告
将在 10 秒后关闭
bannerAds