gRPC+gRPC Gateway+swagger小记

前言

本文记录了grpc-gateway的简单使用。

定义proto

先来看看最常规的

syntax = "proto3";

package protos;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
 
message HelloRequest {
  string name = 1;
}
 
message HelloReply {
  string message = 1;
}

然后加入gateway等相关的内容

syntax = "proto3";

package protos;


// 1 导入 gateway 相关的proto 以及 swagger 相关的 proto
import "google/api/annotations.proto";
import "protoc-gen-swagger/options/annotations.proto";

// 2 定义 swagger 相关的内容
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
  info: {
		title: "grpc gateway sample";
		version: "1.0";	
		license: {
			name: "MIT";			
		};
  };
  schemes: HTTP;
};

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
      // 3 标识接口路由
      option (google.api.http) = {
                post: "/hello_world"
                body: "*"
            };
  }
}
 
message HelloRequest {
  string name = 1;
}
 
message HelloReply {
  string message = 1;
}

从proto生成文件

执行下面的三个命令。

protoc -I . --go_out=plugins=grpc:. hello.proto
protoc -I . --grpc-gateway_out=logtostderr=true:. hello.proto  
protoc -I . --swagger_out=logtostderr=true:. hello.proto  

实现service和启动service

实现

package services

import (
	"context"
	pb "grpc-sample/protos"
	"log"
)

type server struct{}

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

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Println("request: ", in.Name)
	return &pb.HelloReply{Message: "hello, " + in.Name}, nil
}

启动

package main

import (
	"google.golang.org/grpc"
	pb "grpc-sample/protos"
	"grpc-sample/services"
	"log"
	"net"
)

const (
	PORT = ":9192"
)

func main() {
	lis, err := net.Listen("tcp", PORT)

	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, services.NewServer())
	log.Println("rpc services started, listen on localhost:9192")
	s.Serve(lis)
}

整合 swagger

先下载swagger ui的静态文件。

把这些文件打包成go文件。

go-bindata --nocompress -pkg swagger -o gateway/swagger/datafile.go third_party/swagger-ui/...

9M。。。

写 gateway

package main

import (
	"github.com/elazarl/go-bindata-assetfs"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	swagger "grpc-sample/gateway/swagger"
	gw "grpc-sample/protos"
)

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

	gwmux, err := newGateway(ctx)
	if err != nil {
		panic(err)
	}

	mux := http.NewServeMux()
	mux.Handle("/", gwmux)
	mux.HandleFunc("/swagger/", serveSwaggerFile)
	serveSwaggerUI(mux)

	log.Println("grpc-gateway listen on localhost:8080")
	return http.ListenAndServe(":8080", mux)
}

func newGateway(ctx context.Context) (http.Handler, error) {
	opts := []grpc.DialOption{grpc.WithInsecure()}

	gwmux := runtime.NewServeMux()
	if err := gw.RegisterGreeterHandlerFromEndpoint(ctx, gwmux, ":9192", opts); err != nil {
		return nil, err
	}

	return gwmux, nil
}

func serveSwaggerFile(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.URL.Path, "swagger.json") {
		log.Printf("Not Found: %s", r.URL.Path)
		http.NotFound(w, r)
		return
	}

	p := strings.TrimPrefix(r.URL.Path, "/swagger/")
	p = path.Join("../protos", p)

	log.Printf("Serving swagger-file: %s", p)

	http.ServeFile(w, r, p)
}

func serveSwaggerUI(mux *http.ServeMux) {
	fileServer := http.FileServer(&assetfs.AssetFS{
		Asset:    swagger.Asset,
		AssetDir: swagger.AssetDir,
		Prefix:   "third_party/swagger-ui",
	})
	prefix := "/swagger-ui/"
	mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}

func main() {
	defer glog.Flush()

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

结果如下:

总结

通过grpc-gateway的方式来访问grpc服务,还是挺方便的,不过性能会有所折损。

参考

https://segmentfault.com/a/1190000013513469

posted @ 2019-11-15 21:01  Catcher8  阅读(3218)  评论(0编辑  收藏  举报