配置中心
在之前 tcp的yaml配置 介紹瞭如何監聽yaml檔案變化然後更新配置。
當然假如我們有很多例項,那麼yaml改動將是非常痛苦的事情,那麼如何做到配置檔案統一管理,實時更新呢?
我們可以引入配置中心,從而達到這樣的效果。
業界已經有非常多配置中心了,這裡為了簡化內容,將選用etcd作為配置中心來介紹實現。
etcd
etcd 是一個分散式鍵值對儲存系統。
設計用於可靠儲存不頻繁更新的資料,並提供可靠的觀察查詢。
etcd 暴露鍵值對的先前版本來支援不昂貴的快速和觀察歷史事件(“time travel queries”)。
對於這些使用場景,持久化,多版本,併發控制的資料模型是非常適合的。
ectd 使用多版本持久化鍵值儲存來儲存資料。
當鍵值對的值被新的資料替代時,持久化鍵值儲存儲存先前版本的鍵值對。
鍵值儲存事實上是不可變的;它的操作不會就地更新結構,替代的是總是生成一個新的更新後的結構。
在修改之後,key的所有先前版本還是可以訪問和觀察的。為了防止隨著時間的過去為了維護老版本導致資料儲存無限增長,儲存應該壓縮來脫離被替代的資料的最舊的版本。
所以其非常適合作為配置中心,每一個配置變動都是有序的。
使用 etcd
大家測試可以使用docker 實驗
docker run -p 2479:2479 -p 2480:2480 --mount type=bind,source=$(shell pwd)/tmp/etcd-data.tmp,destination=/etcd-data --name etcd \
gcr.io/etcd-development/etcd:v3.5.0 \
/usr/local/bin/etcd \
--name s1 \
--data-dir /etcd-data \
--listen-client-urls http://0.0.0.0:2479 \
--advertise-client-urls http://0.0.0.0:2479 \
--listen-peer-urls http://0.0.0.0:2480 \
--initial-advertise-peer-urls http://0.0.0.0:2480 \
--initial-cluster s1=http://0.0.0.0:2480 \
--initial-cluster-token tkn \
--initial-cluster-state new \
--log-level info \
--logger zap \
--log-outputs stderr
cli
可以使用 cli 工具: https://github.com/etcd-io/etcd/tree/main/etcdctl
./etcdctl put foo bar --lease=1234abcd
# OK
./etcdctl get foo
# foo
# bar
./etcdctl put foo --ignore-value # to detache lease
# OK
ui
或者使用ui工具: https://github.com/evildecay/etcdkeeper
實踐
以下內容更新到 openresty-dev-1.rockspec
-- 依賴包
dependencies = {
"lua-resty-etcd >= 1.9.0",
}
然後執行
luarocks install openresty-dev-1.rockspec --tree=deps --only-deps --local
程式碼內容:
worker_processes 1; #nginx worker 數量
error_log logs/error.log; #指定錯誤日誌檔案路徑
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr [$time_local] $status $request_time $upstream_status $upstream_addr $upstream_response_time';
access_log logs/access.log main buffer=16384 flush=3; #access_log 檔案配置
lua_package_path "${prefix}deps/share/lua/5.1/?.lua;${prefix}deps/share/lua/5.1/?/init.lua;${prefix}?.lua;${prefix}?/init.lua;;./?.lua;/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/openresty/luajit/share/lua/5.1/?.lua;/usr/local/openresty/luajit/share/lua/5.1/?/init.lua;";
lua_package_cpath "${prefix}deps/lib64/lua/5.1/?.so;${prefix}deps/lib/lua/5.1/?.so;;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/openresty/luajit/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so;";
# 開啟 lua code 快取
lua_code_cache on;
upstream nature_upstream {
server 127.0.0.1:6699; #upstream 配置為 hello world 服務
# 一樣的balancer
balancer_by_lua_block {
local balancer = require "ngx.balancer"
local upstream = ngx.ctx.api_ctx.upstream
local ok, err = balancer.set_current_peer(upstream.host, upstream.port)
if not ok then
ngx.log(ngx.ERR, "failed to set the current peer: ", err)
return ngx.exit(ngx.ERROR)
end
}
}
# 換為init_worker 是因為 init 不允許請求etcd
init_worker_by_lua_block {
node = nil
-- 匹配路由, 為了演示,這裡簡化為單個節點,並且路由處理也去掉了
router_match = function()
return node
end
-- 從etcd 載入配置,同理為了演示簡單,這裡只做單個節點
local etcdlib = require("resty.etcd").new({
protocol = "v3",
api_prefix = "/v3",
http_host = 'http://127.0.0.1:2479',
key_prefix = '/test/'
})
-- 這裡為了簡單,展示輪詢方式, watch 的方式可以參考 https://github.com/fs7744/nature/blob/main/nature/config/etcd.lua
ngx.timer.every(1, function()
local res, err = etcdlib:get('node')
local json = require('cjson.safe')
if res ~= nil and res.body ~= nil and res.body.kvs ~= nil and res.body.kvs[1] ~= nil then
node = res.body.kvs[1].value
ngx.log(ngx.ERR, json.encode(node))
end
end)
}
server {
#監聽埠,若你的8699埠已經被佔用,則需要修改
listen 8699 reuseport;
location / {
# 在access階段匹配路由
access_by_lua_block {
local upstream = router_match()
if upstream then
ngx.ctx.api_ctx = { upstream = upstream }
else
ngx.exit(404)
end
}
proxy_http_version 1.1;
proxy_pass http://nature_upstream; #轉發到 upstream
}
}
#為了大家方便理解和測試,我們引入一個hello world 服務
server {
#監聽埠,若你的6699埠已經被佔用,則需要修改
listen 6699;
location / {
default_type text/html;
content_by_lua_block {
ngx.say("HelloWorld")
}
}
}
}
啟動服務並測試
$ openresty -p ~/openresty-test -c openresty.conf #啟動
$ curl --request GET 'http://127.0.0.1:8699/aa/d' #第一次測試
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
</body>
</html>
$ ./etcdctl put /test/node {"host":"127.0.0.1","port":6699} # 寫入測試節點資料
$ curl --request GET 'http://127.0.0.1:8699/aa/d' #第二次測試
HelloWorld
可以看到獲取到了etcd的配置變化