gRPC 学习之六:gRPC-Gateway 集成 swagger
string message = 1;
}
文件 swaggerdemo.proto 和 [《gRPC-Gateway 实战》](()一文中的 proto 文件大部分是一致的,不同之处在于增加了 swagger 的配置,这个配置的作用是让 swagger 把远程调用配置成 http,如果没有这些配置,swagger 默认的远程调用就是 https 的,本文的 gRPC-Gateway 提供的是 http 服务,所以要加上这些配置,在上述 swaggerdemo.proto 的内容中,具体的配置有以下两处:
用 import 关键词导入 protoc-gen-swagger/options/annotations.proto
下面这段就是 swagger 的配置了,重点是 schemes,里面只有 HTTP:
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
info: {
title: "grpc gateway helloworld sample";
version: "1.0";
};
schemes: HTTP;
};
还要把 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 目录下新增以下三个文件:
swaggerdemo.pb.go:gRPC 所需的 go 文件
swaggerdemo.pb.gw.go:gRPC-Gateway 所需的 go 文件
swaggerdemo.swagger.json:swagger-ui 要用的 json 文件,依据此文件,swagger 展现的页面中会有 gRPC-Gateway 暴露的服务和参数定义,可以在页面上发起请求
[](()生成 swagger-ui 的 go 文件
要想在服务中提供 swagger 的 web 页面,需要将 swagger-ui 的源码转为 go 文件,步骤如下:
接下来的命令会从 Github 下载 swagger-ui 的源码,这个文件本该从 swagger 官方下载,但是我这 Java 开源项目【ali1024.coding.net/public/P7/Java/git】 里尝试多次后发现,下载得到的 zip 包很容器出现文 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 件损坏而无法解压缩的情况,于是我将此文件放在了自己的 Github 上,下面的操作也是从我自己的 Github 下载的,但实际上此文件和 swagger 官方的并无区别;
进入目录 $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/swagger-ui \
&& rm -f ./swagger-ui.zip \
&& rm -rf ./swagger-ui-3.38.0
执行以下命令新建文件夹,该文件夹用来存放稍后生成的 swagger-ui 的 go 源码:
mkdir -p $GOPATH/src/swaggerdemo/pkg/ui/data/swagger
执行以下命令,将 swagger-ui 源码转为 datafile.go 文件:
cd $GOPATH/src/swaggerdemo/
go-bindata --nocompress -pkg swagger -o pkg/ui/data/swagger/datafile.go third_party/swagger-ui/...
这时候在 $GOPATH/src/swaggerdemo/pkg/ui/data/swagger 目录下生成了文件 datafile.go
所有文件和材料已经准备完成,开始编码;
[](()编写 gRPC 的服务端代码
按照 swaggerdemo.proto 的配置新建一个 gRPC 服务,步骤如下:
新建文件夹 $GOPATH/src/swaggerdemo/server;
在新建的 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)
知其然不知其所以然,大厂常问面试技术如何复习?
1、热门面试题及答案大全
面试前做足功夫,让你面试成功率提升一截,这里一份热门 350 道一线互联网常问面试题及答案助你拿 offer
2、多线程、高并发、缓存入门到实战项目 pdf 书籍
3、文中提到面试题答案整理
4、Java 核心知识面试宝典
覆盖了 JVM 、JAVA 集合、JAVA 多线程并发、JAVA 基础、Spring 原理、微服务、Netty 与 RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA 算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm 的大量技术点且讲解的非常深入
评论