在編譯了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的項