背景
由于业务需求我们需要根据client_ip
对部分接口做一个简单的 ab test,将部分流量导到新的服务上。基于之前做 WAF 的经验,制定了大体方案:根据 client_ip
做 hash, 根据 hash 值来确定是请求新后端还是原来的后端服务。
在说明详细方案前,先说明一下现状。当前的配置如下:
location ~ ^/api/rest/vd /Login {
rewrite ^(.*)$ /loginServer$1 break;
proxy_http_version 1.1;
proxy_next_upstream error timeout invalid_header;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header Clientip $http_clientip;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
proxy_pass http://test.fanruo.net;
}
其中,/loginServer
是当前服务路径前缀,/loginServerNew
是新服务路径前缀。所以,具体需求可以描述为,10% 的用户使用/loginServerNew
服务,90% 的用户使用/loginServer
服务。
具体方案
- 根据字符串 client_ip 转成长整数 ip_num;
- 对 ip_num 按 100 取模,取最后两位 hash;
- 当 hash < 10 时走
/loginServerNew
服务, 设置tag="New"
, 否则tag=""
- 改造rewrite指令为rewrite ^(.*) /loginServertag
对应配置为
代码语言:javascript复制location ~ ^/api/rest/vd /Login {
set $tag "";
access_by_lua_file conf/lua/abtest.lua;
rewrite ^(.*)$ /loginServer$tag$1 break;
proxy_http_version 1.1;
proxy_next_upstream error timeout invalid_header;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header Clientip $http_clientip;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
proxy_pass http://test.fanruo.net;
}
对应 lua 脚本(conf/lua/btest.lua):
代码语言:javascript复制local client_ip = ngx.var.http_clientip or ngx.var.remote_addr
local o1,o2,o3,o4 = client_ip:match("(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)" )
local ip_num = 2^24*o1 2^16*o2 2^8*o3 o4
local hash = ip_num % 100
ngx.var.tag = ""
if hash > 10 then
ngx.var.tag = "New"
end
问题
按照上述实现,最终总是走原来的服务/loginServer
。通过分析 tag 的值一直是 “”,因此,需求不能满足。
原因分析
通过分析,由于之前 WAF 是通过直接修改 proxy_pass 参数来完成的,而 proxy_pass 对应 content phase,该阶段在 access phase 之后,因此上面的配置没有问题。而本需求是在 rewrite phase,因此需要在该阶段之前修改对应变量 tag 只能在 set phase 来完成 nginx 变量修改操作。
解决方案
跟进上述分析,修改 nginx.conf 配置和 lua 脚本如下:
lua 脚本修改为:
代码语言:javascript复制local client_ip = ngx.var.http_clientip or ngx.var.remote_addr
local o1,o2,o3,o4 = client_ip:match("(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)" )
local ip_num = 2^24*o1 2^16*o2 2^8*o3 o4
local hash = ip_num % 100
local tag = ""
if hash > 10 then
tag = "New"
end
return tag
nginx.conf 配置修改为:
代码语言:javascript复制location ~ ^/api/rest/vd /Login {
set_by_lua_file $tag conf/lua/abtest.lua;
rewrite ^(.*)$ /loginServer$tag$1 break;
proxy_http_version 1.1;
proxy_next_upstream error timeout invalid_header;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header Clientip $http_clientip;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
proxy_pass http://test.fanruo.net;
}