• 还要把swaggerdemo.proto中提到的protoc-gen-swagger/options/annotations.proto文件放在合适的地方,以便使用swaggerdemo.proto的时候能找到此annotations.proto文件,执行以下命令:

cd $GOPATH/src

cp -r ./github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger ./

  • 上述命令中的protoc-gen-swagger文件夹,是在前文的操作中下载好的;

生成gRPC、gRPC-Gateway所需的go源码

  • 生成gRPC、gRPC-Gateway所需的go源码,这样的操作在前面已经做过,这里用swaggerdemo.proto再做一次,先进入目录$GOPATH/src/swaggerdemo

  • 执行以下命令,生成gRPC所需源码:

protoc -I. \

-I$GOPATH/src \

-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \

–go_out=plugins=grpc:. \

swaggerdemo.proto

  • 执行以下命令,生成gRPC-Gateway所需源码:

protoc -I. \

-I$GOPATH/src \

-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \

–grpc-gateway_out=logtostderr=true:. \

swaggerdemo.proto

生成swagger所需的json文件

  • 还是在目录$GOPATH/src/swaggerdemo,执行以下命令,生成swagger所需json:

protoc -I. \

-I$GOPATH/src \

-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \

–swagger_out=logtostderr=true:. \

swaggerdemo.proto

  • 此时的$GOPATH/src/swaggerdemo目录下新增以下三个文件:
  1. swaggerdemo.pb.go:gRPC所需的go文件

  2. swaggerdemo.pb.gw.go:gRPC-Gateway所需的go文件

  3. swaggerdemo.swagger.json:swagger-ui要用的json文件,依据此文件,swagger展现的页面中会有gRPC-Gateway暴露的服务和参数定义,可以在页面上发起请求

生成swagger-ui的go文件

  • 要想在服务中提供swagger的web页面,需要将swagger-ui的源码转为go文件,步骤如下:
  1. 接下来的命令会从Github下载swagger-ui的源码,这个文件本该从swagger官方下载,但是我这里尝试多次后发现,下载得到的zip包很容器出现文件损坏而无法解压缩的情况,于是我将此文件放在了自己的Github上,下面的操作也是从我自己的Github下载的,但实际上此文件和swagger官方的并无区别;

  2. 进入目录$GOPATH/src/swaggerdemo,执行以下命令下载swagger-ui源码,并放入指定位置:

wget https://raw.githubusercontent.com/zq2599/blog_download_files/master/files/swagger-ui.zip -O swagger-ui.zip \

&& unzip swagger-ui.zip \

&& mkdir -p $GOPATH/src/swaggerdemo/third_party/ \

&& mv ./swagger-ui-3.38.0/dist $GOPATH/src/swaggerdemo/third_party/ \

&& mv $GOPATH/src/swaggerdemo/third_party/dist $GOPATH/src/swaggerdemo/third_party/swagger-ui \

&& rm -f ./swagger-ui.zip \

&& rm -rf ./swagger-ui-3.38.0

  1. 执行以下命令新建文件夹,该文件夹用来存放稍后生成的swagger-ui的go源码:

mkdir -p $GOPATH/src/swaggerdemo/pkg/ui/data/swagger

  1. 执行以下命令,将swagger-ui源码转为datafile.go文件:

cd $GOPATH/src/swaggerdemo/

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

  1. 这时候在$GOPATH/src/swaggerdemo/pkg/ui/data/swagger目录下生成了文件datafile.go
  • 所有文件和材料已经准备完成,开始编码;

编写gRPC的服务端代码

  • 按照swaggerdemo.proto的配置新建一个gRPC服务,步骤如下:
  1. 新建文件夹$GOPATH/src/swaggerdemo/server;

  2. 在新建的server文件夹下新增文件server.go,内容如下,只是个普通的gRPC服务而已:

package main

import (

“context”

“log”

“net”

“google.golang.org/grpc”

pb “swaggerdemo”

)

const (

port = “:50051”

)

// 定义结构体,在调用注册api的时候作为入参,

// 该结构体会带上SayHello方法,里面是业务代码

// 这样远程调用时就执行了业务代码了

type server struct {

// pb.go中自动生成的,是个空结构体

pb.UnimplementedGreeterServer

}

// 业务代码在此写,客户端远程调用SayHello时,

// 会执行这里的代码

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {

// 打印请求参数

log.Printf(“Received: %v”, in.GetName())

// 实例化结构体HelloReply,作为返回值

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)

}

// 实例化gRPC server结构体

s := grpc.NewServer()

// 服务注册

pb.RegisterGreeterServer(s, &server{})

log.Println(“开始监听,等待远程调用…”)

if err := s.Serve(lis); err != nil {

log.Fatalf(“failed to serve: %v”, err)

}

}

  • 以上就是gRPC服务的代码,与前几篇文章中的差不多,就不赘述了;

编写gRPC-Gateway服务端的代码

  • 开始编写gRPC-Gateway服务端代码,这是本文的重点所在,除了提供与前文一样的gRPC-Gateway服务,还提供了swagger的json文件服务,以及swagger的ui服务;

  • 新建文件夹$GOPATH/src/swaggerdemo/gateway;

  • 在新建的gateway文件夹下新增文件gateway.go,内容如下,有几处要注意的地方稍后会说明:

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 “swaggerdemo/pkg/ui/data/swagger”

gw “swaggerdemo”

)

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:9090”)

return http.ListenAndServe(“:9090”, mux)

}

func newGateway(ctx context.Context) (http.Handler, error) {

opts := []grpc.DialOption{grpc.WithInsecure()}

gwmux := runtime.NewServeMux()

if err := gw.RegisterGreeterHandlerFromEndpoint(ctx, gwmux, “:50051”, opts); err != nil {

return nil, err

}

return gwmux, nil

}

func serveSwaggerFile(w http.ResponseWriter, r *http.Request) {

log.Println(“start serveSwaggerFile”)

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(“…/”, 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)

}

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐