Nginx+KV db进行AB灰度测试
周6参加华东运维大会,听了人家淘宝用nginx的一些场景,其中AB的灰度测试可能适用场景会比较普遍,当然大会上,并没有详细讨论实现。
大概需求是: 网站类业务在更新new feature时,并不想让全量用户看到,可以针对地区性用户开放此feature
大概构思了一个方式,使用 nginx+redis/memcache+IP库实现,简单的流程图如下:
当然其中的new feature server和normal server不必要一定得是物理上的服务器,可以是任意逻辑上分开的服务和http URI
所用的模块是 ngx-lua-module, 以及一个基于ngx-lua写的lib: lua-resty-memcached或lua-resty-redis, 这里假设使用memcached作为ip数据的存储,cache内保存以ip作为key,以true(1)或false(0)作为value的数据,nginx在请求到来时,从cache内以remote_addr(如果是用XFF头,则对XFF做一次处理后获取到real ip)作为key从cache内做一次get,判断此req应该的转发;
这里有一个问题是:cache内是保存具体的IP形式的方式,还是以CIDR的超网形势存储,若直接使用IP作为key,数据量不容小视,而且IP信息的准确度得有一定的保证才行;若使用CIDR的方式,则在nginx端又会增加一次IP转换CIDR以及对get到的CIDR做比较(具体实现方法还没想到), 复杂度会有所增加,个人偏向直接使用IP作为key,只要保证了IP的一定准确性,数据大小问题不大,现在遍地都是32G,64G内存的缓存。
若使用ip作为key,一个折中的办法是每次进行ABtest的时候,flush缓存,只保存指定地区的ip数据即可,ngx在做get的时候,如果没有返回,则认为此req是到normal server的.
管理平台方面,只需要做个简单的批量set缓存的功能就可以了,至于UI么,就看你给谁用了,自己用嘛,UI丑陋点就丑陋点了 :)
性能和可用性方面:
增加了一次缓存的连接和get操作,理论上此开销应该是很小的,ngx-lua实现的lua-resty-memcached有不少人做过测试,性能非常可观.
可用性方面会增加一个当缓存断线的风险点,通过settimeout,将缓存超时限制到一个较小的时间,影响较小,另外ABtest的方案也不应该常年累月的在线上,只有在有需求时,才需要这套系统吧,因此可用性方面对全局影响应该是较小的,相比新的feature上线时影响全部用户的风险,这个冒险还是值得的。
上述暂时只是个人的思路,而且也还没上线使用,实现方面只完成了nginx获取key来判断req转发的验证,针对此方式也未做过详细压力测试,抛砖引玉,有好的方式欢迎讨论。
本文转自: http://www.mysqlops.com/2012/06/29/nginxkv-db%E8%BF%9B%E8%A1%8Cab%E7%81%B0%E5%BA%A6%E6%B5%8B%E8%AF%95.html
Nginx过滤hash ddos攻击的方法,用的着的可以参考。
上段时间的各语言hash绝对印象深刻吧,做网站的几乎都在此类,不论你是用的是php,python还是ruby都不同程度受到影响, PHP尤其明显,因为PHP用的人也多嘛,攻击方式简直简单到不行,有兴趣的可以找我索取此测试脚本,一个终端随便搞挂一台有次漏洞的PHP站点.
我们http请求都是通过nginx反向代理,所以优势是可以在nginx这一层对请求做很多逻辑,此次防hash dos就是这个架构.
原理是过滤post请求中超过指定参数数量的请求, 我的是300,可自己调整,应该没有http开发者在使用post方法的时候有超过这个参数值的了,所以不会影响正常请求.
之前有哥们自己写ngx的C模块,使用的也是这个原理,不过NGX模块开发复杂度是有的,因为自己C也看不熟练,还是自己用lua写,方便快捷,简洁明了.
#配置依赖ngx-lua模块
好,废话不多说,贴上nginx的配置:
>>cat post-limit.lua
ngx.req.read_body()
local method = ngx.var.request_method
local max_count= 300 –post最大参数
if method == ‘POST’ then
local data = ngx.req.get_body_data()
if data then
local count = 0
local i = 0
while true do
if count > max_count then
–ngx.redirect(‘/post-error’)
ngx.exit(ngx.HTTP_BAD_REQUEST)
end
i = string.find(data, ‘&’, i+1)
if i == nil then break end
count = count + 1
end
end
end
>> cat nginx.conf
…..
server {
client_body_buffer_size 20m;
client_max_body_size 20m;
access_by_lua_file /opt/conf/nginx/lua/post_limit.lua;
}
…..
nginx其他配置已隐去.
nginx 1.0.XX结合后端PHP应用测试通过,配置也适用其他开发语言的http应用.
有时间可以慢慢升级你的后端应用PHP版本了,否则一系列的版本升级,也够呛.
代码段:
local method = ngx.var.request_method
local max_count= 300
if method == 'POST' then
local data = ngx.req.get_body_data()
if data then
local count = 0
local i = 0
while true do
if count > max_count then
--ngx.redirect('/post-error')
ngx.exit(ngx.HTTP_BAD_REQUEST)
end
i = string.find(data, '&', i+1)
if i == nil then break end
count = count + 1
end
--else
-- ngx.redirect('/post-error')
end
end
问题:过滤脚步好像有问题,对POST的数据体,如果说用户提交的是文件等,里面可能会包含大量的&字符,该过滤方法不完善
回复:这位朋友指出的是,针对这种上传文件,且文件内包含众多&符号的情况,此过滤会影响到文件上传,此种情况应该是少见的。
不过安全的限制程度总是会影响其他,若出现这种情况,可以将get_body()函数换成request_uri进行统计。
本文转自: http://www.mysqlops.com/2012/04/01/nginx%E8%BF%87%E6%BB%A4hash-dos.html
当测试环境需要一些数据时,除了仿造一些数据外,更方便和更好的做法是,将来自正式环境的用户请求copy一份到测试环境。
依赖模块:lua-nginx-module,ngx_devel_kit, headers-more-nginx-module
以上模块在github上均可以找到,作者是国内nginx的著名开发者agentzh。
使用模块需要重新编译nginx,加上编译选项:–add-module=/path/to/your/module 。
下面是nginx配置:
>>cat copy_req.lua
local res1, res2, action
action = ngx.var.request_method
if action == “POST” then
arry = {method = ngx.HTTP_POST, body = request_body}
else
arry = {method = ngx.HTTP_GET}
end
if ngx.var.svr == “on” then
res1, res2 = ngx.location.capture_multi {
{ “/s1″ .. ngx.var.request_uri , arry},
{ “/test” .. ngx.var.request_uri , arry},
}
else
res1, res2 = ngx.location.capture_multi {
{ “/s1″ .. ngx.var.request_uri , arry},
}
end
if res1.status == ngx.HTTP_OK then
local header_list = {“Content-Length”, “Content-Type”, “Content-Encoding”, “Accept-Ranges”}
for _, i in ipairs(header_list) do
if res1.header[i] then
ngx.header[i] = res1.header[i]
end
end
ngx.say(res1.body)
else
ngx.status = ngx.HTTP_NOT_FOUND
end
>>cat nginx.conf
……..
location ~* ^/s1 {
log_subrequest on;
rewrite ^/s1(.*)$ $1 break;
proxy_pass http://s1;
access_log /opt/logs/nginx/youni/upstream.log;
}
location ~* ^/test {
log_subrequest on;
rewrite ^/test(.*)$ $1 break;
proxy_pass http://test;
access_log /opt/logs/nginx/youni/upstream.log;
}
location ~* ^/(.*)$ {
client_body_buffer_size 2m;
set $svr “on”; #开启或关闭copy功能
content_by_lua_file req_fenliu.lua;
}
upstream s1 {
server x.x.x.x;
}
upstream test {
server xx.xx.xx.xx;
}
其中nginx.conf内的http,server域没有给出,大家依照自己的配置即可。