有一应用需动态更新目标的ip和端口,使用nginx/openresty tcp/udp端口转发配合lua扩展很方便实现。
stream {
lua_shared_dict ups_dict 1m;
lua_shared_dict ups_list 1m;
lua_add_variable $ups_name;
#当nginx启动或reload时读取配置文件内upstream列表到lua_shared_dict,更新后端的时候判断是否有效
init_by_lua_block {
ngx.shared.ups_list:flush_all()
local file = "/etc/nginx/stream.conf"
local f = io.open(file, "r")
local content = f:read("*a")
f:close()
for substr in string.gmatch(content, "upstream%s+([%w-_]+)%s+%{") do
ngx.log(ngx.WARN, substr)
ngx.shared.ups_list:set(substr, 1)
end
}
log_format main '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
access_log /run/log/nginx/dns_access.log main;
error_log /run/log/nginx/dns_error.log error;
#log level: debug, info, notice, warn, error (default), crit, alert, and emerg.
upstream default_tcp {
server 127.0.0.1:53;
server 127.0.0.1:55;
}
upstream default_udp {
server 10.0.1.1:53;
server 10.0.2.1:53;
}
upstream default_udp2 {
server 10.0.3.1:53;
server 10.0.3.2:53;
}
upstream test1_tcp {
server 127.0.0.1:53;
}
upstream test1_udp {
server 10.0.1.1:53;
}
upstream test1_udp2 {
server 10.0.3.2:53;
}
upstream test2_tcp {
server 127.0.0.1:55;
}
upstream test2_udp {
server 10.0.2.1:53;
}
upstream test2_udp2 {
server 10.0.3.1:53;
}
server {
listen 53000 udp;
preread_by_lua_block {
assert(not ngx.var.ups_name)
local ups = ngx.shared.ups_dict:get("ups_name")
if ups ~= nil then
ngx.log(ngx.WARN, "udp 53000 use " .. ups .. " upstream")
ngx.var.ups_name = ups .. "_udp"
else
ngx.log(ngx.WARN, "udp 53000 use default upstream")
ngx.var.ups_name = "default_udp"
end
}
proxy_pass $ups_name;
proxy_responses 1; #当返回一个udp数据包时就关闭连接,或proxy_timeout时间内无数据关闭连接
proxy_connect_timeout 1s;
proxy_timeout 5s;
}
server {
listen 53000;
preread_by_lua_block {
assert(not ngx.var.ups_name)
local ups = ngx.shared.ups_dict:get("ups_name")
if ups ~= nil then
ngx.log(ngx.WARN, "tcp 53000 use " .. ups .. " upstream")
ngx.var.ups_name = ups .. "_tcp"
else
ngx.log(ngx.WARN, "tcp 53000 use default upstream")
ngx.var.ups_name = "default_tcp"
end
}
proxy_pass $ups_name;
#proxy_responses 1; #当返回一个udp数据包时就关闭连接,或proxy_timeout时间内无数据关闭连接
proxy_connect_timeout 1s;
proxy_timeout 5s;
}
server {
listen 53001 udp;
preread_by_lua_block {
assert(not ngx.var.ups_name)
local ups = ngx.shared.ups_dict:get("ups_name")
if ups ~= nil then
ngx.log(ngx.WARN, "udp 53001 use " .. ups .. " upstream")
ngx.var.ups_name = ups .. "_udp2"
else
ngx.log(ngx.WARN, "udp 53001 use default upstream")
ngx.var.ups_name = "default_udp2"
end
}
proxy_pass $ups_name;
proxy_responses 1; #当返回一个udp数据包时就关闭连接,或proxy_timeout时间内无数据关闭连接
proxy_connect_timeout 1s;
proxy_timeout 5s;
}
server {
listen 53001;
content_by_lua_block {
local sock = assert(ngx.req.socket(true))
local data = sock:receive()
local args, err = ngx.decode_args(data, 0)
if err == nil then
for k,v in pairs(args) do
--ngx.say(v)
ip = v:match("(%d+%.%d+%.%d+%.%d+%:%d+)")
if ip ~= nil then
local sus, err = ngx.shared.ups_dict:set(k, ip)
if sus then
ngx.say("set upstream sus: " .. k .. "=" .. ip)
else
ngx.say(err)
end
end
end
end
}
access_log off;
}
server {
listen 53002;
content_by_lua_block {
local sock = assert(ngx.req.socket(true))
local data = sock:receive()
if data then
local args, err = ngx.decode_args(data, 0)
if err == nil then
for k,v in pairs(args) do
--ngx.say(v)
--if k == "name" and type(v) == "string" and v:find("dns") then
if k == "name" and type(v) == "string" and ngx.shared.ups_list:get(v .. "_udp") then
local sus, err = ngx.shared.ups_dict:set("ups_name", v)
if sus then
ngx.say("set upstream sus: " .. k .. "=" .. v)
ngx.log(ngx.WARN, "set upstream sus: " .. k .. "=" .. v)
else
ngx.say(err)
end
end
end
end
end
}
access_log off;
}
}
使用nc测试设置nginx转发使用的后端upstream名称:
nc 127.0.0.1 53002
name=test1
set upstream sus: name=test1
也可通过53001端口设置目标ip:port,然后proxy_pass即可。
参考:
https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md
https://xiangxianzui.github.io/2017/11/nginx-lua%E5%8A%A8%E6%80%81%E6%94%B9%E5%8F%98upstream/