local _G = {}
_G.refresh = function()
-- 读取X-Proxy-OSS-Filename头
local target = ngx.var.http_x_proxy_oss_filename
-- 在共享dict中写入一个key为$target值为当前时间戳的数据作为版本号
ngx.shared.osstarget_version:set(target, ngx.now())
end
_G.getOssTarget = function()
local target = ngx.var.http_x_proxy_oss_filename
-- 从共享dict读取$target
local v, _ = ngx.shared.osstarget_version:get(target)
if not v then
v = ngx.now()
ngx.shared.osstarget_version:set(v, ngx.now())
end
-- 把版本号作为参数v添加到URL中
return target ... "?v=" ... v
end
return _G
Ok 了吗? 上面nginx-proxy的服务有replicas: 4字段说明这个服务是有 4 个实例, 刷新请求只会刷新其中一个实例, 我们需要让每个实例刷新的时候都顺便刷新其它实例, 这很简单只需要逐个请求并带上refresh=1 参数即可, 需要解决的问题是, 怎么让一个实例知道其它实例的访问地址呢? k8s 为我们提供了一种headless的 Service 服务, 正常情况下我们通过 DNS 查询一个域名 DNS 会返回一个 A 记录指向一个 IP 地址
-- headless Service地址
local headlessSvc = "nginx-proxy-headless.default.svc.cluster.local"
-- 实例自己的IP, 避免循环调用
local selfIp = "1.1.1.1"
-- 集群DNS服务的IP
local clusterDns = "2.2.2.2"
function dispatchRefresh(target)
-- 如果是被动刷新则不派发刷新请求
if ngx.var.arg_no_dispatch then
return
end
-- 通过集群DNS查询headless Service
local r, err = resolver:new({
nameservers = { _clusterDns },
retrans = 5,
timeout = 2000
})
if err then
ngx.log(ngx.STDERR, err)
return
end
local answers, err, _ = r:query(headlessSvc)
if err then
ngx.log(ngx.ERR, "dispatchWebProxyRefresh: query error: ", err)
return
end
if answers.errcode then
ngx.log(ngx.ERR, "dispatchWebProxyRefresh: query error: ", answers.errstr)
return
end
-- 遍历answers
for _, ans in ipairs(answers) do
local addr = ans.address
-- 判断是否是自己的IP
if addr ~= selfIP then
-- 调用/refresh这个location并传入参数
local res = ngx.local.capture("/refresh", {
args = fmt("host=%s&target=%s", addr, target)
})
ngx.log(ngx.INFO, fmt("dispatchWebProxyRefresh: request %s %s", addr, res.body))
end
end
end
这里着重分析 40~42 行的内容, 可以看到在遍历得到其它实例的 IP 后, 调用了/refresh这个 location 并传入了实例 IP 和 target 参数, 这里是利用 Nginx 的反向代理功能间接实现请求其它实例的功能, 下面来看看/refresh
总结一下, dispatchRefresh函数通过集群 DNS 查询得到所有实例的 IP 地址, 然后通过/refresh块逐个请求, /refresh块从capture方法传递过来的args中分别获取targetOSS 文件地址和host实例 IP 地址, 然后通过proxy_pass进行反向代理间接发起请求