在编译了lua-nginx-module的nginx上,可以方便地使用shared dict特性,在不reload配置文件的情况下实现配置同步。

由于shared dict使用一块共享内存,因此所有worker均可读写,也就不存在一致性的问题。

使用shared dict

ngx.shared.DICT的作用域是”init_by_lua, init_worker_by_lua, set_by_lua, rewrite_by_lua, access_by_lua, content_by_lua, header_filter_by_lua, body_filter_by_lua, log_by_lua, ngx.timer.**”,加上最近的balancer_by_lua赌五毛也是可以使用的。

目前可用的API有

貌似春哥还准备给shared dict加上更多redis like的API

同步配置

作为例子使用一个的location来更新shared dict中的值,假设配置如下:

可以使用curl http://yay.lol/update_cfg?loc=http%3A%2F%2Fyooooo.us来更新配置,更新后的配置将体现在/test的返回中,即重定向到http://yooooo.us

由于Lua简单的数据结构(ngx.shared本身就是一个table),还可以跳过shared dict的API:

优点是API更简洁,不用预先定义键和大小(lua_shared_dict项),还可以省去shared dict的API call(其实就是绑定到C的function)的开销;缺点是功能只有存取,没有ttl之类的功能了,而且reload之后会丢失

现实场景

在现实场景中,我们肯定不能向每台服务器都发一个http请求来更新配合,不仅看起来非常地low,而且最重要的是相当于给自己的服务器留下了一个后门。当然你可以说做一些http验证啊、随机的server_name啊、allow/deny啊等等,但这其实增加了管理的开销。

mysql和redis等已经提供了replication的功能,并且有完善的认证机制,可以直接拿来用: )

这里以mysql举例。首先需要一台主服务器,配置的修改直接在这台机器上进行,对这台机器,可以使用上面提到的安全措施进行保护,甚至直接ssh进去也不是很费事。将这台主服务器设置为mysql的master,建立同步用的库和表(假设为config.main_tbl),然后将需要同步配置的机器设置为它的slave并同步该表。

在主服务器上,简单起见这里省略了所有错误处理:

在从服务器上:

然后,在crontab中增加定时curl localhost/update_cfg_slave的项

 

下一篇博客我们将对使用shared dict与使用静态配置或者nginx set变量相比产生的额外开销做一个benchmark 😉