最近在主导部门统一接入服务项目,其中涉及 7 层网关组件的选型。在过去一年多时间内,我们部门的业务主要使用的 Kong 网关,也打算作为一个长期方案,结果杀出了 APISIX 这个黑马,经过分析讨论,最终敲定基于 APISIX 来开发统一接入服务。所以,最近有一些零散的折腾存货,会陆续整理到博客,方便有相同诉求的朋友。
APISIX 的高级路由非常厉害,可以基于任意变量来做转发路由,比如可以基于 Header、cookie、querystring 参数等。而我们这里历史上有个非常变态的用法:基于 Body 里面某个参数的值来路由,即不同的值要转发到不同的后端 IP:PORT(这个问题主要是因为服务拆分时偷懒,没有推动客户端修改请求留下的尾巴)。
在 APISIX 前面的版本中,发现并不支持解析 Body 然后通过 Body 里面的指定参数来做路由,最近更新到 2.10.0 LTS 版本后,发现这个特性赫然支持了:
如下表所示,目前 APISIX 的 route 里面已经支持植入一个 lua 函数,那么就变得非常灵活了!因此,上面这个变态需求也成为了可能。
根据需求,写了如下 lua 函数,意思是当 body 里面有个 foo 参数且值为 bar 的时候命中此路由:
function(vars)
local core = require ('apisix.core')
local body, err = core.request.get_body()
if not body then
return false
end
local data, err = core.json.decode(body)
if not data then
return false
end
-- 当匹配 body 里面 foo 字段等于 bar 的时候,路由生效
if data['foo'] == 'bar' then
return true
end
return false
end
加到 APISIX 的路由当中,示例代码如下(这里关键是需要加\n 来换行):
{
"uri": "/hello",
"filter_func": "function(vars)\n local core = require ('apisix.core')\n local body, err = core.request.get_body()\n if not body then\n return false\n end\n\n local data, err = core.json.decode(body)\n if not data then\n return false\n end\n\n if data['foo'] == 'bar' then\n return true\n end\n\n return false\nend",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1": 9080
}
}
"status": 1
}
需要注意的是,加了这条路由之后,对 /hello 的所有请求都会命中到这个路由,并不会因为 Body 里面没有 foo 或者 foo 的值不为 bar 而出现 404 报错。这里也不太理解官方的设计逻辑,可能是因为此时只有一条路由,已经没有其他选择了。
因此,还需要加一条不含过滤的路由,转发到另一个后端,协议如下:
{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1": 9081
}
}
"status": 1
}
此时,请求才能正常被区分开来,即请求 Body 里面带了 foo 且值为 bar 的请求,会转发到 9080 端口,否则转发到 9081 端口。
网站每日ip 1千,交换友链,https://money1.us/521
老张干啥去了,这么久不更新博客了,今天终于更新了!
哇柳暗花明啊,这就是我想要的功能,我这两天还一直在研究自己写个插件来干这个活,能直接 filter_func 就方便多了,超级感谢!
我这边也是业务侧有些奇葩需求要用 post 的 body 参数来进行路由,而且这个参数还 base64 + json 套娃,
用这个方案需要加两条路由,生产环境实测发现当body超过一定体积的时候可能会404报错,解决办法:将apisix配置里面的router_http值设置为radixtree_host_uri
哈哈补充下相关信息,我们最终还是在后端服务上实现了这个需求,在网关上搞风险太大了,而且后端实际仍然需要解码,这个方案数据要在网关跟后端被解码两次,成本上也不划算。
之前业务侧这需求是先 base64 解码,解出来的二进制数据再 gzip 解压,解压完是个 json,再从 json 里提取某个 value,根据其前缀决定路由,其实当时我就觉得这需求有点离谱...后面沟通也还是决定在他们后端做了。
看到你的文章,感觉很不错,想与你友情链接
网站名:电脑教程网
网站域名:https://dnjcw.com.cn/
同意的话给我发邮件internetyewu@163.com