apisix生产应用

基础使用

apisix路由规则灰度实现

2条规则

规则1 优先级10

规则2 优先级11 配置客户端ip 为公司

效果 公司ip访问规则2 其他ip 访问规则1

注意事项

apisix: 任何匹配多于 uri 的路由都被认为是相等的,并应按优先级加以区分。

注意:当 Route 和 Service 都开启同一个插件时,Route 参数的优先级是高于 Service 的。

插件优先级

Consumer > Route > Service

如果再Consumer 配置了认证,而route没有开启认证,则无法访问

插件:

限流

1
2
3
4
5
6
7
8
"plugins": {
"limit-count": { #限流
"count": 2, #次数
"time_window": 3, # 限流后恢复时间
"rejected_code": 404, #限流返回code
"key": "remote_addr"
}
}

ip拦截

1
2
3
4
5
6
7
"ip-restriction": {
"blacklist": [
"xx.xx.xxx.xx"
],
"disable": false,
"message": "想搞事?"
}

response-rewrite

添加返回头

可自定义返回跨域头

1
2
3
4
5
"response-rewrite": {
"headers": {
"X-Server-AAAAA": 666
}
}

proxy-rewrite

上游代理信息重写,支持请求头重写

1
2
3
4
5
6
"proxy-rewrite": {
"regex_uri": [
"^/micro/app/xxx(.*)$",
"/"
]
}

跳转

1
2
3
4
5
6
"redirect": {
"append_query_string": false,
"encode_uri": false,
"ret_code": 307,
"uri": "https://xxxx/micro/app/xxx$is_args$args"
}

一些网关配置错误信息,可据此告警

1
2
3
4
5
6
7
2021/12/28 17:15:08 [error] 47#47: *4057755 [lua] config_etcd.lua:569: failed to fetch data from etcd: failed to check item data of [/apisix/routes] err:failed to check the configuration of plugin response-rewrite err: invalid base64 content,  etcd key: /apisix/routes, context: ngx.timer
2021/12/28 17:14:58 [error] 46#46: *4065775 [lua] init.lua:520: http_access_phase(): failed to set upstream: no valid upstream node, client: xxxxxxxx, server: _, request: "GET /favicon.png HTTP/1.1", host: "xxxxxxm", referrer: "http://xxxxxxx"
2021/12/28 17:15:08 [error] 46#46: *4057695 [lua] config_etcd.lua:569: failed to fetch data from etcd: failed to check item data of [/apisix/routes] err:failed to check the configuration of plugin response-rewrite err: invalid base64 content, etcd key: /apisix/routes, context: ngx.timer


failed to fetch data from etcd
failed to set upstream

基于apisix自定义插件实现无上游服务

需求: 前端阿里云的nginx需要迁移至腾讯云

技术选型:apisix充当网关

原因:在原有nginx中维护了大量灰度判断配置,大多是由uri判断+2个参数判断,所以非常繁琐,也不好观测,因为apisix便捷的路由配置管理,实现现有灰度配置非常轻松,且在实测中NGINX网关在高并发下更新配置会出现502的错误

在迁移时发现因为apisix-dashboard本身存在一些缓存的情况,且考虑需要备份和回滚路由,所以单独起了django项目充当apisix管理后台,实现路由的管理

前端情况:大部分应用使用dom https://www.w3cschool.cn/javascript/js-htmldom.html ,每个dom只有10几k大小,在初始选型中,将pod作为单个应用的最小单位,方便单个应用管理,作用是只返回一个dom文件

1
2
FROM registry.cn-hangzhou.aliyuncs.com/xxxxx/nginx:stable-alpine-v1
COPY index.html /usr/share/nginx/html/index.html

导致最后维护了500+ pod,后又因腾讯云托管集群是按照资源管理数量收费的,比较傻逼image-20220707112900500

插件开发

https://apisix.apache.org/zh/docs/apisix/plugin-develop

https://apisix.apache.org/zh/docs/apisix/architecture-design/apisix/

https://github.com/apache/apisix/blob/35269581e21473e1a27b11cceca6f773cad0192a/apisix/plugins/limit-count.lua#L177

https://apisix.apache.org/zh/blog/2022/02/16/file-logger-api-gateway/

可参考proxy-rewrite插件实现

最初在实践中发现apisix可以实现使用go-runner来实现直接返回前端页面,所以第一版是go-runner做的,上到开发环境压测后,发现性能不佳,达不到生产需求

再到后面,发现可以使用原生lua来实现插件,且apisix提供了请求在各阶段的方法实现

插件内部结构

下么是参考官方文档和一些插件写一个直接返回dom的自定义插件,非常简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
local ngx = ngx
local core = require("apisix.core")
local plugin = require("apisix.plugin")

local schema = {
type = "object",
properties = {
body = {
description = "body for response must base64",
type = "string",
}
},
required = {"body"},
}

local plugin_name = "return-dom"

local _M = {
version = 0.1,
priority = -6000,
name = plugin_name,
schema = schema,
}


function _M.check_schema(conf, schema_type)
return core.schema.check(schema, conf)
end


function _M.rewrite(conf, ctx)
core.response.set_header("Content-Type", "text/html")
return 200, ngx.decode_base64(conf.body) -- 直接使用decode_base64返回dom在压测中性能最优
end

return _M

对应service

1
2
3
4
5
6
7
8
{
"name": "xxx",
"plugins": {
"return-dom": {
"body": "BASE_64 STR"
}
}
}

对应路由

1
2
3
4
5
6
7
8
9
10
{
"uris": [
"/*"
],
"name": "xxx",
"priority": 30,
"host": "dev.xxx.com",
"service_id": "4",
"status": 1
}

apisix-dashboard临时修改插件内容:

默认dashboard只支持内置的插件,如果要修改,就要改dashboard配置文件

在pod /usr/local/apisix-dashboard/conf/schema.json 文件plugins中添加return-dom的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

"plugins": {
"return-dom": {
"schema": {
"properties": {
"body": {
"type": "string"
}
},
"required": [
"body"
],
"type": "object"
}
}
}

实现后丢失的功能

  • 上游负载均衡所有算法支持(轮询,加权轮询,最小链接等)
  • 一些依赖上游的插件可能没法使用了(暂时没有用到这些插件)

权重实现

根据权重返回dom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
local function randomByWeight(t)
``-- 总权重.
``local totalWeight = ``0
``for` `i, item in ipairs(t) ``do
``local p = item.weight
``totalWeight = totalWeight + p
``end
``-- 权重随机值.
``local r = math.random() * totalWeight
``totalWeight = ``0
``for` `i, item in ipairs(t) ``do
``local p = item.weight
``totalWeight = totalWeight + p
``if` `totalWeight >= r then
``return` `item[``1``]
``end
``end
end
local testtable = { { weight = ``50``, { dom = ``"A"` `} },
``{ weight = ``50``, { dom = ``"B"` `} } }
print(randomByWeight(testtable).dom)