简介
突然心血来潮想写一下基于lua实现灰度转发的文章。
根据前文内容的openresty处理阶段这一环节,假如要实现灰度流量的转发,需要在balancer这个阶段进行处理。这个阶段类似于nginx的upstream作用域
upstream backend { server test.com test; }
原生的upstream其实是可以实现灰度流量转发的,但是主要策略是基于权重比例来流量的转发,无法实现颗粒度细的流量转发,为此,我使用lua代码实现了四种灰度流量转发策略:
基于权重比例转发;
基于IP地址转发;
基于地区位置转发;
基于HTTP字段转发(通用);
首先需要了解在这个阶段可以支持的操作有什么内容:
#告诉openresty流量要转到什么后端服务器 syntax:ok, err = balancer.set_current_peer(host, port) #设置尝试错误次数 syntax:ok, err = balancer.set_more_tries(count) #获取上次失败的原因,我用来剔除失效的后端服务器 syntax:state_name, status_code = balancer.get_last_failure() #设置超时时间 syntax:ok, err = balancer.set_timeouts(connect_timeout, send_timeout, read_timeout
接着根据上面的内容编写一个转发流量到后端的函数
--ip_lists是一个后端服务器列表,比如(192.168.1.2,192.168.1.3,192.168.1.4),port是固定的,
local function forward_server(ip_lists, port)
--设置错误尝试失败次数 if not ngx.ctx.tries then ngx.ctx.tries = 0 end #判断后端服务器列表有多少个,确定重试次数 if ngx.ctx.tries < #ip_lists then local set_more_tries_ok, set_more_tries_err = balancer.set_more_tries(1) if not set_more_tries_ok then ngx.log(ngx.ERR, "failed to set the current peer: ", set_more_tries_err) elseif set_more_tries_err then ngx.log(ngx.ALERT, "set more tries: ", set_more_tries_err) end end ngx.ctx.tries = ngx.ctx.tries + 1
#确定有效的后端服务器列表 if not ngx.ctx.ip_lists then ngx.ctx.ip_lists = ip_lists end #确定客户端IP指向一个后端服务器,用于保持会话 local first_count = {} table.insert(first_count, string.sub(ngx.var.remote_addr, 1, 1)) table.insert(first_count, string.sub(ngx.var.remote_addr, -1)) local ip_count = (tonumber(table.concat(first_count)) % #ngx.ctx.ip_lists) + 1 local _host = ngx.ctx.ip_lists[ip_count] local state_name, state_code = balancer.get_last_failure() #剔除失效后端服务器 if state_name == "failed" then for k, v in ipairs(ngx.ctx.ip_lists) do if v == _host then if not (#ngx.ctx.ip_lists == 1) then table.remove(ngx.ctx.ip_lists, k) ip_count = (string.sub(ngx.var.remote_addr, -1) % #ngx.ctx.ip_lists) + 1 _host = ngx.ctx.ip_lists[ip_count] end end end end
#一切没有问题之后,直接流量转发 local ok, err = balancer.set_current_peer(_host, port) if not ok then ngx.log(ngx.ERR, "failed to set the current peer: ", err) end end
最后聊聊四个灰度转发策略的编写
基于权重比例转发策略
基于权重比例比较简单,就是使用随机数落到那一个后端服务器里面。
比如{“192.168.1.20”:“20”,“192.168.1.30”:30}
local weight_list = {} local iplist = {} iplist['192.168.1.20']=20 iplist['192.168.1.30']=30 for key,value in pairs(iplist) do for i=1, value do table.insert(weight_list,key) end end local random_value = math.random(1, #weight_list) print(random_value) print(weight_list[random_value])
基于IP比例转发策略
local iputils = require "resty.waf.iputils"
基于iputils 库,用于处理客户端IP的地址转发(支持IP地址和网段)
local ip_forward_list={"192.168.1.2","192.168.20.0/24"} local ip_list = iputils.parse_cidrs(ip_forward_list) if iputils.ip_in_cidrs(remote_ip, ip_list) then --如果IP段灰度策略符合,转发到灰度服务器 return forward_server(gray_server, gray_port) end
基于地区灰度转发策略
local geo = require 'resty.waf.maxminddb' if not geo.initted() then geo.init("/opt/GeoLite2-City.mmdb") --需要在该目录设置geo库 end local res, err = geo.lookup(remote_ip) if res then if res['city'] then local city_name = res['city']['names']['zh-CN'] --查看当前IP所属的城市 --ngx.log(ngx.ERR,city_name) for _, _value in pairs(region_forward_data) do local gray_server = _value['gray_server'] local gray_port = _value['gray_port'] local region = _value['content'][1]['content'] if region == city_name then return forward_server(gray_server, gray_port) --如果地区灰度策略符合,转发到灰度服务器 end end end end
基于通用配置转发策略
我是用jxwaf里面的处理HTTP字段代码,就不造轮子,具体开发思路可以参考jxwaf的自定义规则功能
local waf = require "resty.waf.waf" local request = require "resty.waf.request" local operator = require "resty.waf.operator" local transform = require "resty.waf.transform"
PS:有人对CC防御的模块开发有兴趣吗?
来源:freebuf.com 2021-06-24 22:41:46 by: 陌度
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
喜欢就支持一下吧
请登录后发表评论
注册