上一篇博客 未来实现 API 管理系统的几个关键词 https://www.cnblogs.com/dc20181010/p/11676034.html 发布后, 有不少读者私信我, 让我写一篇实际运用的文章, 我周末趁着有空写了这篇有关 "服务编排" 的文章. 用的是 Goku API Gateway 进行演示, 希望本文对读者有所启发.
什么是服务编排 / 数据聚合?
服务编排 / 数据聚合指的是可以通过一个请求来依次调用多个微服务, 并对每个服务的返回结果做数据处理, 最终整合成一个大的结果返回给前端.
例如一个服务是 "查询用户预定的酒店", 前端仅需要传一个订单 ID, 后端会返回整个订单的信息, 包括用户信息, 酒店信息和房间信息等.
这个服务背后可能对应着以下几个操作:
请求订单详情, 返回订单对应的用户 ID, 酒店 ID, 房间 ID;
根据各类 ID 查询对应的信息;
将数据做过滤, 移动等操作, 最后整合起来;
将整合好的数据返回给前端.
下面的图可以帮你更好理解:
编排的优势
微服务架构上对功能做了解耦, 使用服务编排可以快速从各类服务上获取需要的数据, 对业务实现快速响应. 总的来说, 编排有以下几点优势:
功能解耦, 服务能够被复用;
对前端友好, 无需多次请求;
业务响应速度快, 服务能够被快速生成;
返回数据有改动的话, 请求接口无影响;
老系统改动的情况下, 不需要改动前端, 可以通过网关对数据做兼容.
使用编排工具 Goku API Gateway:
先简单介绍一下, Goku API Gateway (中文名: 悟空 API 网关)是一个基于 Golang 开发的微服务网关, 能够实现高性能 HTTP API 转发, 服务编排, 多租户管理, API 访问权限控制等目的, 拥有强大的自定义插件系统可以自行扩展, 并且提供友好的图形化配置界面, 能够快速帮助企业进行 API 服务治理, 提高 API 服务的稳定性和安全性.
本次之所以采用 Goku API Gateway 来进行演示, 主要看中的是它因为支持一个编排 API 对应多个后端服务, 每个后端服务的请求参数可以使用前端传入的参数, 也可以在编排里自定义(写静态参数或从返回数据里获得). 每个后端服务的返回数据支持过滤, 删除, 移动, 重命名, 拆包和封包等操作, 而且编排 API 能够设定编排失败时的异常返回. 这对本次服务编排很有作用.
这次用的是 Goku API Gateway 的社区版本(CE), 也就是开源版本, 我对内置的插件系统进行部分改动也算是定制开发了.
项目地址: https://github.com/eolinker/goku-api-gateway
官网地址: https://www.eolinker.com
下面进入实际操作环节.
如何在 Goku 上做服务编排?
我们将编排的整个操作放到网关进行, 由网关对数据做处理与转换, 这样无需对后端服务做改动. 一个请求到达网关, 网关调用多个后端服务, 并且在网关上对各个服务的返回数据做处理(操作有过滤, 移动, 重命名, 封包, 拆包, 后面会对各操作做详细解释), 最后由网关将数据整合好返回给前端. 通过下图查看会更加明白:
操作步骤
1. 在网关上, 新建 API 的类型可以选择服务编排:
2. 配置 查询预定酒店 API 的请求信息:
3. 添加并配置 Step:
网关将编排过程中对 API 的转发处理过程 (转发 ->获取返回数据 ->数据处理)称为一个 Step .
添加一个转发服务, 该服务为 查询订单详情 API, 配置相应的转发地址, 传入的参数, 对返回数据做何种处理等.
由于篇幅原因, 后续的 Step(查询用户详情, 查询酒店详情, 查询房间详情)就不一一展示了.
编排的传递参数方式和数据处理
为了让读者更好的理解编排的实现方式, 简单说一下两种传递参数方式:
(一)编排的两种传递参数方式
网关将编排过程中对 API 的转发处理过程 (转发 ->获取返回数据 ->数据处理)称为一个 Step.
我们将处理查询订单详情 API 称为 Step1 , 其中 Step1 的返回数据有: 用户 ID, 酒店 ID, 房间 ID. 同理, 将查询用户信息这步称为 Step2 , 将查询酒店信息称为 Step3 , 将查询房间信息称为 Step4 .
传参规则:
使用前端传入的参数可以写成: body. 参数名, header. 参数名
使用 Step1 里的返回数据作为参数可以写成: body1. 参数名 , header1. 参数名
以此类推
1. 在转发路径传参
以下为转发路径的传参写法:
例如 Step1 要接收前端传入的 orderID 参数, 还有 Authorization 参数.
Step1 的转发路径可以写成:/getOrderInfo/{{body.orderID}}/{{header.Authorization}}
例如 Step2 接收 Step1 里的返回用户 ID 参数(userID), 同时接收前端传入的 Authorization 参数.
Step2 的转发路径可以写成:/getUserInfo/{{body1.userID}}/{{header.Authorization}}
2. 在 Step 里配置请求参数
Step2 中需要接收 Step1 里返回的 userID 作为参数, 同时需要接收前端传入的 Authorization 参数
在网关里 Step2 的请求参数配置如下所示, 请求参数存在多个的话用换行表示:
(二)返回数据处理
1. 查询订单详情的 API, 返回数据称为 json1, 内容如下:
- {
- "status":"000000",
- "data":{
- "id":"201910180009x"
- ,"user_account":"zzz@163.com"
- ,"hotal":"0001"
- ,"room":"biger1"
- ,"time_start":"20191019"
- ,"time_end":"20191020"
- }
- }
2. 查询用户详情的 API, 返回数据称为 json2, 内容如下:
- {
- "status":"000000",
- "data":{
- "account":"goku@eolinker.com",
- "full_name":"",
- "phone":""
- }
- }
3. 查询酒店详情的返回数据, 称为 json3, 内容如下:
- {
- "status":"000000",
- "data":{
- "id":"001",
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
- }
4. 查询房间详情的返回数据, 称为 json4, 内容如下:
- {
- "status":"000000",
- "data":{
- "name":"豪华大床房"
- ,"window":1
- ,"floor":"10-12"
- ,"nosmoke":1
- }
- }
5. 可以在每一个 Step 里对返回 JSON 做处理, 网关会将处理过的数据最后整合起来, 再返回前端, 例如这是通过网关返回的最终数据:
- {
- "id":"201910180009x"
- ,"userInfo":{
- "account":"goku@eolinker.com",
- "full_name":"",
- "phone":""
- }
- ,"hotelinfo":{
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
- , "roominfo":{
- "name":"豪华大床房"
- ,"window":1
- ,"floor":"10-12"
- ,"nosmoke":1
- }
- }
这里以查询酒店详情 API 的返回数据 json3 为例, 讲解网关如何在编排过程中对返回数据做处理.
查询酒店详情 API 返回的原始数据如下:
- {
- "status":"000000",
- "data":{
- "id":"001",
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
- }
从网关返回给前端的数据中截取酒店信息的数据如下:
- "hotelinfo":{
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
那么从原始数据到处理后的数据需要经过以下操作:
字段黑名单
字段黑名单的作用是排除某些字段, 支持数组形式.
在网关的 Step3 里配置如下:
经过网关处理后, 实际的返回数据如下, 可以看到 data 对象里的 id 字段已经被过滤掉:
- {
- "status":"000000",
- "data":{
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
- }
字段拆包
拆包是指将指定对象的内容提取出来作为该步骤 (step) 的返回结果. 其中匹配目标只能为 object, 匹配目标为空时, 结果为 {}, 可用于清除数据.
在网关的 Step 里配置如下:
经过网关处理后, 实际的返回数据如下, 可以看到 data 对象被拆开, 最终数据仅保留了 data 对象里面的字段:
- {
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
封包
字段 封包 会 将当前的数据整体打包为最终返回数据中的 一个 对象 , 不支持 *, 不支持数组.
在网关的 Step 里配置如下:
经过网关处理后, 实际的返回数据如下, 数据被整体打包为 hotelinfo 对象:
- {
- "hotelinfo":{
- "type":"星级酒店",
- "name":"",
- "address":"",
- "location":{},
- }
- }
经过三个步骤, 就可以将原始数据变成最终的数据.
本文仅列举了编排过程中部分数据处理的操作, 如需了解更多编排细则, 可以在评论区留言. 欢迎提出任何建议和想法. 最后给出相关的链接, 有条件实践的可以去 GitHub 下载试试. 项目地址: https://github.com/eolinker/goku-api-gateway
来源: http://www.tuicool.com/articles/nEbQvqM