开源星球:设计 Utopia 框架,彻底解决 nginx reload 问题

本文介绍了Utopia框架,一个基于Lua的Nginx扩展,无需修改源代码,通过Lua控制Nginx功能,如路由、响应头设置和IP黑名单,通过HTTP接口实时更新配置,提供高性能和易用性解决方案,以替代传统的nginx-reload。
摘要由CSDN通过智能技术生成

在之前的文章中,我们深入讨论了 Nginx Reload 这一难题,并探讨了可能的解决方案。我为此设计了一个通用的解决方案,名为 Utopia 框架,让开发者能够基于此框架,通过编写 Lua 脚本来满足其业务需求。

Utopia框架设计图

先说核心思路:

无需修改 Nginx 源代码。
无需更改 Nginx 任何配置指令。
使用 Lua 实现常见 Nginx 功能。
更新的是 Lua 对应的配置。

简而言之,通过 Lua 脚本重新掌控 Nginx 常见功能,如路由、响应头设置、上游服务器管理等等。甚至可以添加 Nginx 本身不具备>的功能,如 JWT 鉴权等。

实现方式

首先,我们需要使 Nginx 支持 Lua 开发,这就需要开发一个 Nginx Lua C 模块。通过这个模块,可以让 Lua 脚本在 Nginx 进程内>运行,开发者可以编写 Lua 脚本来控制 HTTP 请求。这正是 nginx-http-lua-module 所实现的功能。它用于与 Nginx 进行交互,提供诸如 r.uri、r.body、r.set_header(name, value) 等操作请求的方法,还内置了 ngx.json_encode()、ngx.json_decode() 等常用接口。

接下来是 Lua 部分,我们需要进行配置管理和配置使用。

配置管理的目的是将 Lua 配置更新到 Nginx 进程内,使运行在该进程中的 Lua 模块能够直接使用配置。由于我们目前尚未实现实时>刷新,因此通过定时器的机制来定期同步更新配置。

配置使用则是通过 Lua 脚本控制 HTTP 请求,例如 IP 黑名单、路由处理、响应头设置等。

为什么这样设计

现代软件通常通过 HTTP 接口来更新配置,这些配置可以存储在文件、共享内存或第三方服务中。但不管使用何种存储方式,只需要提供一种对外统一的 HTTP 接口。用户可以通过 curl 命令行工具或仪表板界面来更新配置。这即是配置管理的一部分。

一旦配置更新,由于 NGINX 采用多进程架构,为了性能考虑,应当确保每个进程都能快速访问这些配置数据。因此,配置应定期更新>到每个进程内。最理想的情况是能够实现秒级刷新,这完全是可能的。换句话说,配置应当在每个进程内生效。当 HTTP 请求到达各个 NGINX 进程时,Nginx 将执行 Lua 代码,Lua 代码将访问配置数据并进行相应的处理。这即是配置使用的部分。这一设计架构简洁清晰,与NGINX社区的风格相符。

如何设计配置

首先,配置采用 JSON 格式,与 RESTful 风格保持一致,同时也借鉴了我在 Nginx Unit 开发中的经验。

其次,对于 HTTP 服务器而言,最核心的部分就是路由。一切都始于路由,一切操作都基于路由发生。因此,我们需要设计一个易于使用、简洁高效的路由系统。

示例配置如下:

{    "routes": [        {            "match": {                "uri": "/baz",                "host": "example.com"            },            "action": {                "backend": "b1",                "set_headers": {                    "X-Foo": "foo"                },                "ip_blacklist": [                    "192.168.1.1",                    "10.0.0.2",                    "172.16.0.0/24"                ]            }        }    ],    "backends": {        "b1": {            "policy": "round_robin",            "servers": [                "127.0.0.1:8090",                "127.0.0.1:8082"            ]        }    }}

routes是个数组,包含多条route。每个route由match和action组成。
match定义了匹配规则,这跟nginx的location很像,但更灵活。
action定义了执行动作,可能是ip黑名单,头部响应设置,或者后端服务。
backends类似nginx的upstreams。

配置管理

我们会将所有lua代码放进utopia这样的目录下。

首先在nginx里提供更新配置的http接口:

server {    listen  8000;    location /config {        lua_script  "require('utopia').config(r)";    }    location /timer {        lua_timer  "require('utopia').timer()";    }}

/config用于更新配置,使用如下:

curl -X POST -d@/path/conf.json http://127.0.0.1:8000/config

/timer用于定时同步配置,这个在将来可以优化掉,做到实时更新。

所有的Lua代码都运行在NGINX进程里,配置更新进进程里的Lua模块后,Lua代码就能正常使用配置,而不用做类似nginx reload这样的操作,以达到热加载的效果。
比如头部响应设置的配置如何生效的:

function _M.set_header_init(action, headers)    action.set_headers = headers;end


配置使用

utopia框架会实现通用的功能,比如路由处理,这也是最核心的部分。任何请求都会先进入路由部分,根据配置里的match规则,找到对应的action。然后在处理部分就可以直接用action对应的数据,比如头部响应设置的代码如下:

local function set_header_handler(r, action)    for name, value in pairs(action.set_headers) do        r.set_header(name, value);    endend


完整示例

我们以一个例子演示用这个框架怎么实现IP黑名单拦截的。

1. 制定配置语法,我们用"ip_blacklist"作为IP黑名单的配置,比如:
"ip_blacklist": ["ip", "ip", ...]

{    "routes": [        {            "match": {                "uri": "/baz"            },            "action": {                "ip_blacklist": [                    "192.168.1.1",                    "10.0.0.2",                    "172.16.0.0/24"                ]            }        }    ]}

2.配置验证

local function conf_vldt_ip_blacklist(value)    for _, ip in ipairs(value) do        if (type(ip) ~= "string") then            return "ip blacklist value must be a string"        end    end    return false;endlocal route_action_members = {    ip_blacklist = {        type = CONF.ARRAY,        validator = conf_vldt_ip_blacklist,    },    ...};

3. 配置生效

if (action['ip_blacklist']) then    route.action.ip_blacklist = action['ip_blacklist'];end

4. 配置使用

local function ip_blacklist_handler(r, action)    for _, ip in ipairs(action.ip_blacklist) do        if (r.client_ip == ip) then            r.exit(405);        end    endend

如果没完全理解,等这个开源后体验下就很容易理解了。

关于性能:

我们的设计原则是用Lua只写业务逻辑,算法和数据结构等跟性能有关的,用C去实现,这些隐藏在nginx lua c模块里。举个例子,有些开发者用Lua实现json的解析,我们会将这种常见的功能放在 C模块里,然后提供接口来使用,比如ngx.json_encode,ngx.json_decode。这样性能很高了。

关于易用性:

nginx-http-lua-module是整个的基础,它的易用性决定了整个utopia框架的使用门槛。还是以json为例,我将引入ngx.json_encode, ngx.json_decode这样的接口,这样开发者不用编译安装和维护cjson这样的第三方库。

因此我们的目标是提供高性能和易用的框架,以另一种方式解决nginx reload问题。

未来展望

我们要实现nginx-http-lua-module必要的功能:
请求对象:r.urir.methodr.bodyr.echo()r.exit().
nginx对象:ngx.json_encodengx.json_decode().
https://github.com/hongzhidao/nginx-http-lua-module

接着我们要实现lua utopia框架:
配置验证、路由功能、并且以ip黑名单作为示例

最后实现常用功能支持进来,头部响应设置,upstreams管理等。
这样开发者可以不用重造常用轮子。

预告后面文章:开发内置JSON功能,分享有关原创能力的东西。
关注公众号,不错过这个框架的创作过程。(公众号:程序员洪志道)​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值