我记得在毕业以前,就大致明白这样一件事情,系统之间、模块之间的交互,要确定协议,要定义接口,兜兜转转这些年过去了,我觉得对这件事情认识当然越来越深刻,也说不清其中的程度。最近做的项目中,开始大量地和 OpenAPI 打交道,一方面要最先使用 OpenAPI 来定义接口,让多个其他交互的模块都遵循它来开发,就是 “OpenAPI Driven Development” 的意思,这没啥特别的;但另一方面,系统中还需要把 Protobuf 接口定义转换成 HTTP 接口定义,并实施地使用 swagger-core 来动态创建 OpenAPI Spec,这就比较好玩了。
gRPC 到 HTTP 的协议转换
先来说说这第二件,动态创建 OpenAPI Spec。我们的网关系统需要大量地涉足两种接口,一种是对内调用 gRPC 接口,需要使用 Protobuf 来描述它们;另一种则是对外开放 HTTP 接口,需要使用 OpenAPI 来描述它们。这就牵涉到了两个事情:
- 一个是需要设计一种机制,允许用户只用某一种描述语言(可以是 Protobuf,也可以是 OpenAPI)做出接口定义,然后这个定义会被自动转述为另一种接口定义语言,从而实现通过最少的配置,将两种接口都定义清楚了。技术实现上,我们使用了 Wire。
- 第二个是对于任何一个从外部到来的 HTTP 请求,这个系统需要根据定义自动把他转成 gRPC 的请求,并调用相应的内部系统,等到得到 gRPC 响应之后,也相应地转成 HTTP 响应并返回给外部。技术实现上,我们使用了 Vert.x。
在这个过程中,我也学到了很多有意思的内容。一个是关于协议转换的,必须要完整地了解 Protobuf 是怎样描述一个接口的,而 OpenAPI 又是怎样描述一个接口的,然后才能谈转换,二者在定义上是有一些无法共同覆盖的部分的,这就需要使用某些替代或者扩展机制;再一个是基于 coroutine 或者 reactive 的异步编程(有的子系统基于前者,有的则是后者),以往写的 service 多数都是 blocking 的,适应 non-blocking 的 service 整个思维模式需要做一个转变。这部分体会还是比较深的,后续再写一点理解和总结。
OpenAPI Spec 驱动开发
再来说说这第一件,接口定义来驱动开发,这本来是一个平平无奇的事情,我记得 OpenAPI 的名字还是 Swagger 的时候就在项目中开始大范围地使用了,可是这一次,我才慢慢体会到它的威力远不止此。
使用 OpenAPI spec 来定义接口,不只是确定了所谓的系统和模块之间的合约(其实合约这一点其实使用任何方式来表述接口都可以做到);它还做到了一点,那就是 “标准化”。或者说,写这个合约的语言,叫做 OpenAPI,它是世界通用的语言,用它写出来的合约叫做 OpenAPI spec,大家都能看得懂。
Control Plane 中多模块对 OpenAPI Spec 的依赖
整个系统可以大致分为 Data Plane 和 Control Plane,前者可以说覆盖了从请求抵达、分解、协议转换,到内部接口调用,并将结果再次转换后返回的过程;而后者则是提供一系列机制和工具,去完成定义和控制这个过程所需的接口定义、序列化、持久化、请求校验、接口版本管理等一系列操作。
OpenAPI spec 是 Control Plane 整个系统中最重要的一个依赖项,有了它以后,很多模块都可以完成它相应的任务,无论是开发上,还是这些模块工作上,它们都可以并行。比如说,校验模块可以根据 OpenAPI spec 来校验外来的 HTTP request 和内部转换 gRPC 响应得到的 HTTP response 是否严格符合 spec 的格式;外部的客户端团队可以获取 OpenAPI spec 来自动生成客户端 SDK;接口定义人员可以在完成 protobuf 的定义后立即查看自动生成的 OpenAPI spec 是否符合他的预期等等。
OpenAPI 工具
有了 OpenAPI spec,或者说围绕它,就可以创造一系列的工具,并且这其中的许许多多都可以自动完成。OpenAPI.Tools 就是这样一个汇聚一系列 OpenAPI 工具的网站,而且基本上都是开源的,许多项目里面都可以比较自由地使用。我自己尝试了其中的一些,也实际用到了一些,我把其中比较有用的,记录在这里:
Avantation
可以根据 HAR 文件来生成 OpenAPI spec. HAR 就是 HTTP Archive format,是一种记录浏览器交互数据的 JSON 文件。上面有全部的访问某个网址的记录,包括 http 头、请求、响应和时间等等信息。
比如我们访问 https://reqres.in/api/users?page=2,然后就可以使用 Chrome 的开发者工具来导出 HAR 文件:
安装:
npm install -g avantation
运行:
avantation get_user.har
✔ GET /api/users/2
✔ all taskes completed
接着就可以查看生成的 openapi.yaml 这个 OpenAPI spec 了.
Swagger CLI
这个功能就可多了,比如可以校验 OpenAPI spec 的格式,合并 spec 等等。
npm install -g @apidevtools/swagger-cli
安装好后,跑一下格式校验:
swagger-cli -d validate ./spec.yaml
{
"command": "validate",
"file": "./spec.yaml",
"options": {
"schema": true,
"spec": true,
"format": 2,
"type": "json",
"wrap": null,
"debug": true
}
}
./spec.yaml is valid
Vacuum
这个就是 OpenAPI 的 linter:
brew install daveshanley/vacuum/vacuum
可以生成各种格式的结果页面,比如:
vacuum html-report ./spec.yaml
OpenAPI-diff
这个东西用来做 backward compatibility 的检查是个不错的选择。
安装就是一个 docker 镜像:
docker pull openapitools/openapi-diff
对比两个 OpenAPI spec 的版本:
docker run --rm -t \
-v ~/Downloads/open_api:/open_api \
openapitools/openapi-diff \
--fail-on-incompatible \
/open_api/spec.yaml \
/open_api/spec-updated.yaml
...
- GET /endpoints
Parameter:
- Delete input in query
API changes broke backward compatibility
Redoc
可以生成精美的接口文档。
安装:
npm i -g redoc-cli
运行:
redoc-cli build ./spec.yaml
OpenAPI Generator
可以根据 OpenAPI spec 生成客户端、服务端 stub、文档等等,非常好用。
安装:
brew install openapi-generator
运行:
openapi-generator generate -i spec.yaml -g kotlin -o output-client
openapi-generator generate -i spec.yaml -g kotlin-spring -o output-server
上面的例子中,第一个生成客户端,第二个生成服务端。其中参数-g 是可以用来指定某一种生成器。
Swagger UI
Swagger UI 大概是这些工具里面我用的最早的,可以比较方便地查看 spec 并个根据它构造请求。
安装:
docker pull swaggerapi/swagger-ui
运行:
docker run -p 80:8080 -e
SWAGGER_JSON=/open_api/spec.yaml -v
~/Downloads/open_api:/open_api swaggerapi/swagger-ui
运行起来后就可以通过 http://localhost/访问了。
Restish
Restish 非常好用,我们部署的 service 需要使用命令行(CLI)来访问,以作为 portal 访问的一个功能上更强大的备选方案,可是去写 commandline 和维护挺费劲的,Restish 可以自动生成这样的命令行。
安装:
brew tap danielgtaylor/restish && brew install restish
运行:
restish api configure rest-example
restish rest-example get-endpoint --service abc --name def
上例中,第一行用来打开交互界面做初始化配置,以从远程 URL 读取指定 OpenAPI spec,第二行则是根据它来进行真正的 API call。
TCases
它可以用来自动生成远程 API 的测试代码,也是比较实用的。
./tcases-4.0.1/bin/tcases-api-test -o
./tcases-output/src/test/com/xyz/openapi -u 10000 -m
2 -l stdout -B uri=https://www.xyz.com/v1/abc ./spec.yaml
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》