gRPC学习之六:gRPC-Gateway集成swagger
yuyutoo 2024-10-12 01:50 9 浏览 0 评论
欢迎访问我的GitHub
https://github.com/zq2599/blog_demos
内容:所有原创文章分类和汇总,及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
本篇概览
- 本文《gRPC学习》系列的第六篇,前文咱们实战了gRPC-Gateway,将gRPC服务以RESTful形式对外暴露,当时由于篇幅所限没有完成swagger集成,本篇来完成这个工作:开发gRPC服务,为其提供gRPC-Gateway,并提供在线swagger服务;
- 本文由以下章节构成,这也是gRPC-Gateway集成swagger的常规流程:
- 提前预览关键知识点;
- 新建工程文件夹;
- 安装必要的go包;
- 编写proto文件,使swagger支持http(默认是https);
- 生成gRPC、gRPC-Gateway所需的go源码;
- 生成swagger所需的json文件;
- 下载swagger-ui的源码,以此生成go源码;
- 编写gRPC的服务端代码;
- 编写gRPC-Gateway服务端的代码;
- 验证;
- 注意,本文的所有操作都没有用到root账号,而是前文创建的golang账号;
源码下载
- 如果您不想编码,可以在GitHub下载所有源码,地址和链接信息如下表所示:
- 这个git项目中有多个文件夹,本章的应用在go-source文件夹下,如下图红框所示:
- go-source里面有多个子文件夹,本篇的源码在swaggerdemo中,如下图红框:
提前预览关键知识点
在gRPC-Gateway集成swagger服务的过程挺繁琐,咱们将其中的重点提前看看,做到心里有数:
- 为了简化实战过程,gRPC-Gateway暴露的服务并未使用https,而是http,但是swagger-ui提供的调用服务却是https的,因此要在proto文件中指定swagger以http调用服务,指定的时候会用到文件protoc-gen-swagger/options/annotations.proto,因此需要找到这个文件对应的包,放在合适的位置;
- swaggerdemo.swagger.json:这是swagger-ui要用的json文件,依据此文件,swagger才能正确的展现出gRPC-Gateway暴露的服务和参数定义,可以在页面上发起请求,此文件由插件protoc-gen-swagger生成,该插件是上一篇《gRPC学习之五:gRPC-Gateway实战 》中安装好的;
- 在gRPC-Gateway的代码中集成swagger-ui的代码:swagger-ui的代码由多个png、html、js文件组成,需要用工具go-bindata转换成go源码并放入合适的位置,流程如下图:
前提条件
- 本文是《gRPC学习之五:gRPC-Gateway实战 》 的续篇,请您参考前文将gRPC-Gateway环境准备好;
提前展示文件结构
- 本次实战涉及到多个文件,在此先将最终的文件内容全部展示出来,以便您在开发过程中作为参考,所有内容都在$GOPATH/src/swaggerdemo目录下:
[golang@centos7 src]$ tree swaggerdemo/
swaggerdemo/
├── gateway
│ └── gateway.go
├── pkg
│ └── ui
│ └── data
│ └── swagger
│ └── datafile.go
├── server
│ └── server.go
├── swaggerdemo.pb.go
├── swaggerdemo.pb.gw.go
├── swaggerdemo.proto
├── swaggerdemo.swagger.json
└── third_party
└── swagger-ui
├── favicon-16x16.png
├── favicon-32x32.png
├── index.html
├── oauth2-redirect.html
├── swagger-ui-bundle.js
├── swagger-ui-bundle.js.map
├── swagger-ui.css
├── swagger-ui.css.map
├── swagger-ui-es-bundle-core.js
├── swagger-ui-es-bundle-core.js.map
├── swagger-ui-es-bundle.js
├── swagger-ui-es-bundle.js.map
├── swagger-ui.js
├── swagger-ui.js.map
├── swagger-ui-standalone-preset.js
└── swagger-ui-standalone-preset.js.map
8 directories, 23 files
新建工程文件夹
- 本次实战与前面几篇文章的代码没有关系,而是一个全新的工程,请在$GOPATH/src下面新建名为swaggerdemo的文件夹;
安装必要的go包
- 安装git,执行命令sudo yum install -y git unzip
- 工程中会用到几个包,接下来逐个安装;
- go-bindata用来将swagger-ui的源码转为GO代码:
go get -u github.com/jteeuwen/go-bindata/...
- go-bindata-assetfs在应用启动后,对外提供文件服务,这样可以通过web访问swagger的json文件:
go get -u github.com/elazarl/go-bindata-assetfs/...
- glog是常用的日志工具:
go get -u github.com/golang/glog
编写proto文件
- 进入目录$GOPATH/src/swaggerdemo,新建swaggerdemo.proto,内容如下,有几处要注意的地方稍后会说明:
// 协议类型
syntax = "proto3";
// 包名
package swaggerdemo;
import "google/api/annotations.proto";
import "protoc-gen-swagger/options/annotations.proto";
// 定义swagger内容
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
info: {
title: "grpc gateway helloworld sample";
version: "1.0";
};
schemes: HTTP;
};
// 定义的服务名
service Greeter {
// 具体的远程服务方法
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/helloworld"
body: "*"
};
}
}
// SayHello方法的入参,只有一个字符串字段
message HelloRequest {
string name = 1;
}
// SayHello方法的返回值,只有一个字符串字段
message HelloReply {
string message = 1;
}
- 文件swaggerdemo.proto和《gRPC学习之五: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 ./
- 上述命令中的github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger文件夹,是在《gRPC学习之五:gRPC-Gateway实战 》的操作中下载好的;
生成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官方下载,但是我这里尝试多次后发现,下载得到的zip包很容器出现文件损坏而无法解压缩的情况,于是我将此文件放在了自己的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/dist $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)
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)
}
}
- 对于这个gateway.go文件,有以下几处需要重点注意:
- 外部的RESTful请求转发到server.go的功能,被封装到newGateway方法中;
- 请求URL中如果含有/swagger,就交给serveSwaggerFile方法处理,这里面的逻辑是将文件$GOPATH/src/swaggerdemo/swaggerdemo.swagger.json返回给请求方;
- 重点关注serveSwaggerUI方法,经过该方法的处理后,如果请求URL中含有/swagger-ui,就会交给前面生成的datafile.go处理,也就是打开了swagger-ui的页面;
- 至此,开发工作已经完成,可以开始验证了;
验证
- 进入目录$GOPATH/src/swaggerdemo/server,执行go run server.go启动gRPC服务;
- 进入目录$GOPATH/src/swaggerdemo/gateway,执行go run gateway.go启动gRPC-Gateway服务;
- 确保服务所在机器的防火墙已经关闭;
- 我这边服务器IP地址是192.168.133.204,因此浏览器访问:http://192.168.133.204:9090/swagger/swaggerdemo.swagger.json ,即可看到swagger.json的内容,如下图:
- 访问swagger-ui页面,地址是:http://192.168.133.204:9090/swagger-ui/ ,如下图,可见swagger-ui功能正常:
- 此时的swagger-ui页面并未展示gRPC-Gateway的接口内容,需要将http://192.168.133.204:9090/swagger/swaggerdemo.swagger.json填入下图红框1中,再点击红框2的按钮,即可正常展示,红框3就是gRPC-Gateway对外暴露的服务:
- 点击下图红框中的Try it out按钮,即可在页面上向后台发起请求:
- 如下图,修改红框1中的请求参数,再点击红框2中的按钮,即可发起请求:
- 如下图,红框1中是请求地址,可见是http请求,证明咱们之前在proto文件中的设置已经生效,红框2中是收到的返回内容,很明显这个内容来自server.go:
- 至此,gRPC-Gateway集成swagger的操作就完成了,可见这是一系列繁琐的操作,希望本文能给您提供一些参考,助您顺利集成swagger;
欢迎关注我的公众号:程序员欣宸
相关推荐
- 墨尔本一华裔男子与亚裔男子分别失踪数日 警方寻人
-
中新网5月15日电据澳洲新快网报道,据澳大利亚维州警察局网站消息,22岁的华裔男子邓跃(Yue‘Peter’Deng,音译)失踪已6天,维州警方于当地时间13日发布寻人通告,寻求公众协助寻找邓跃。华...
- 网络交友须谨慎!美国犹他州一男子因涉嫌杀害女网友被捕
-
伊森·洪克斯克(图源网络,侵删)据美国广播公司(ABC)25日报道,美国犹他州一名男子于24日因涉嫌谋杀被捕。警方表示,这名男子主动告知警局,称其杀害了一名在网络交友软件上认识的25岁女子。雷顿警...
- 一课译词:来龙去脉(来龙去脉 的意思解释)
-
Mountainranges[Photo/SIPA]“来龙去脉”,汉语成语,本指山脉的走势和去向,现比喻一件事的前因后果(causeandeffectofanevent),可以翻译为“i...
- 高考重要考点:range(range高考用法)
-
range可以用作动词,也可以用作名词,含义特别多,在阅读理解中出现的频率很高,还经常作为完形填空的选项,而且在作文中使用是非常好的高级词汇。...
- C++20 Ranges:现代范围操作(现代c++白皮书)
-
1.引言:C++20Ranges库简介C++20引入的Ranges库是C++标准库的重要更新,旨在提供更现代化、表达力更强的方式来处理数据序列(范围,range)。Ranges库基于...
- 学习VBA,报表做到飞 第二章 数组 2.4 Filter函数
-
第二章数组2.4Filter函数Filter函数功能与autofilter函数类似,它对一个一维数组进行筛选,返回一个从0开始的数组。...
- VBA学习笔记:数组:数组相关函数—Split,Join
-
Split拆分字符串函数,语法Split(expression,字符,Limit,compare),第1参数为必写,后面3个参数都是可选项。Expression为需要拆分的数据,“字符”就是以哪个字...
- VBA如何自定义序列,学会这些方法,让你工作更轻松
-
No.1在Excel中,自定义序列是一种快速填表机制,如何有效地利用这个方法,可以大大增加工作效率。通常在操作工作表的时候,可能会输入一些很有序的序列,如果一一录入就显得十分笨拙。Excel给出了一种...
- Excel VBA入门教程1.3 数组基础(vba数组详解)
-
1.3数组使用数组和对象时,也要声明,这里说下数组的声明:'确定范围的数组,可以存储b-a+1个数,a、b为整数Dim数组名称(aTob)As数据类型Dimarr...
- 远程网络调试工具百宝箱-MobaXterm
-
MobaXterm是一个功能强大的远程网络工具百宝箱,它将所有重要的远程网络工具(SSH、Telnet、X11、RDP、VNC、FTP、MOSH、Serial等)和Unix命令(bash、ls、cat...
- AREX:携程新一代自动化回归测试工具的设计与实现
-
一、背景随着携程机票BU业务规模的不断提高,业务系统日趋复杂,各种问题和挑战也随之而来。对于研发测试团队,面临着各种效能困境,包括业务复杂度高、数据构造工作量大、回归测试全量回归、沟通成本高、测试用例...
- Windows、Android、IOS、Web自动化工具选择策略
-
Windows平台中应用UI自动化测试解决方案AutoIT是开源工具,该工具识别windows的标准控件效果不错,但是当它遇到应用中非标准控件定义的UI元素时往往就无能为力了,这个时候选择silkte...
- python自动化工具:pywinauto(python快速上手 自动化)
-
简介Pywinauto是完全由Python构建的一个模块,可以用于自动化Windows上的GUI应用程序。同时,它支持鼠标、键盘操作,在元素控件树较复杂的界面,可以辅助我们完成自动化操作。我在...
- 时下最火的 Airtest 如何测试手机 APP?
-
引言Airtest是网易出品的一款基于图像识别的自动化测试工具,主要应用在手机APP和游戏的测试。一旦使用了这个工具进行APP的自动化,你就会发现自动化测试原来是如此简单!!连接手机要进行...
- 【推荐】7个最强Appium替代工具,移动App自动化测试必备!
-
在移动应用开发日益火爆的今天,自动化测试成为了确保应用质量和用户体验的关键环节。Appium作为一款广泛应用的移动应用自动化测试工具,为测试人员所熟知。然而,在不同的测试场景和需求下,还有许多其他优...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)