Nginx实现A/B测试(ngx http split clients module模块)

来自Linux78|wiki

ngx_http_split_clients_module模块创建适合于A/B测试(也叫做分离测试)的变量。也可当做负载均衡来使用。

nginx提供了“nginx_http_split_clients_module”、“nginx_stream_split_clients_module”,分别适用于http和tcp,可以帮助简单实现这些功能。

split模块在0.8版本就已经有了,使用起来也非常简单:

split_clients string $variable {....}  

此指令的上下文为http。

1、假如,web项目中提供了2个html页面,其中index_new.html为最新发布页面,index.html为旧页面,为了“避免新页面有BUG,而影响全站用户”、“测试新页面上体验改进是否符合预期”,我们将index页面的访问流量中的10%转发到新页面上,剩余90%继续使用旧页面。

split_clients "${remote_addr}_1qazxsw2" $variant {  
              10%               _new;  
              *                  "";  
}   
server {  
   ...  
   location / {  
       index index${variant}.html;  
}  

配置实例

http {
   split_clients "${remote_addr}AAA" $variant {
                  0.5%               .one;
                  2.0%               .two;
                  *                  "";
   }
   server {
       location / {
           index index${variant}.html;
    }
}

IP地址加上AAA字符串会使用MurmurHash2转换成数字。得出的数字在前0.5%,那么$variant值为.one,相应的在0.5-2.0%区间的值为.two,其他的为空字符。

split_clients指令

语法:split_clients string $variable { ... }
默认值:     —
配置段:     http

原始字符串的值经过MurmurHash2算法进行了哈希。 示例中,哈希值从0到21474835(0.5%)对应于变量$variant的".one"值, 哈希值从21474836到107374180(2%)对应于值".two", 哈希值从107374181到4294967295对应于值""(一个空字符串)。


split_clients "${remote_addr}_1qazxsw2" $group {  
              10%               gray_group;  
              *                  main_group;  
}  
upstream gray_group {  
   server 10.0.1.11;  
   ...  
}  
upstream main_group {  
   ....  
}  
server {  
   ...  
   location / {  
       proxy_pass http://${group};  
}  

上述配置文件中,可以看到split模块的使用方式,“split_clients”指令接受2个参数,nginx将会根据第一个参数的值使用MurmurHash2算法计算hashcode,hashcode值域为0~4294967295(即2<<31 - 1),通常第一个参数是一个动态值,比如用户的ip、session_id等。10%是表示一个区域,如果第一个参数的hashcode值,在0 ~ 4294967295 *10%之间,则将“_new”赋值给$variant;20%表示第一个参数的hashcode值,在4294967295 * (10% ~ 20%)区间时....依次轮推,"*"表示上述区间都没有覆盖的值域,将使用的值。

我们发现这种方式的灰度发布,其实流量导入的比例并非严格。如果你的服务多次灰度发布,可能会导致某些IP总是在灰度区域,有时候我们需要调整spit第一个参数的随机数因子,本例中"${remote_addr}_1qazxsw2",“_1qazxsw2”这个补充字符串就是随机因子,每次灰度发布,我们需要修改这个值以避免上一次灰度的IP再次进入灰度,当然这个操作也是没有太大必要。

通常还有一些更加苛刻的要求,比如强制要求某个IP、用户ID进入灰度区域。那么使用spit模块就显得有些不太适应,要么使用nginx的geo组件将相应的IP、uid添加到geo文件中(还需要后端程序在cookie输入相应的uid值,以便nginx获取和判断)。对于这种有很多“个性化”条件的,我们可以使用nginx + lua + redis (memcached)实现,其中lua作为脚本语言来做条件判断工具以及使用redis客户端操作数据,redis用于存储需要灰度的用户信息,此外可能还需要开发一个后端程序来运营redis中的数据,比如将某个uid加入redis,lua从cookie中读取uid并检查redis中是否存在,如果存在则进入灰度区域。