事先声明,这里说的A/B测试跟工具ApacheBench没有半毛钱关系。这里说的是关于web页面转化率统计方面的测试,点击这里看其解释。A/B测试是目前很多大公司采用的一种科学的统计方法。使用了这种方法后,就再也不需要争吵到底是A图片好还是B图片好了。一切看统计数据。
发布新版本web网站前,先做下A/B测试是最好的做法。那么,在服务器、代码结构方面应该如何实现呢?这个就是本文打算探讨的问题。
前提说明:本文只考虑静态页面的A/B测试。动态请求的A/B测试很容易实现,因此不作考虑。
最初的考虑:利用页面跳转
假设目标页面的URL是http://www.example.com/index.html,可以在index.html加一段随机跳转的代码,例如:
<script>
if(Math.random()>0.5){
location.href='a.html';
}else{
location.href='b.html';
}
</script>
这种实现的最大有点是实现简单,并且可以做很多自定义的判断。
但最大的缺点就是页面的URL会改变,而我们的目标是不让用户觉察出他正在被A/B测试,因此这种方式不列入考虑。
一:使用random_index命令
蔽司的服务器使用的是nginx服务器,所以我们先来看下nginx是否有自带的实现。
从google搜索了一下,找到了一个命令random_index。文章链接。具体使用方法如下:
server {
listen 80;
server_name www.example1.com;
location / {
root /var/www/www.example1.com/test_index;
random_index on;
}
}
这个命令的作用是在当前目录随机挑选一个文件进行输出。不过它有一个缺点,就是同一个用户每次刷出来的页面可能是不一样的。
二:使用nginx user模块
介绍的文章请看这里,作者是大虾。由于我没实验过,因此我将文章内容直接抄过来给大家看下:
userid on;
userid_name uid;
userid_domain milanoo.com;
userid_path /;
userid_expires 365d;
if ( $uid_set ~ “^uid=(.{9})(.)(.+)$” ) {
set $serp $2;
set $uid $1$2$3;
}
if ( $uid_got ~ “^uid=(.{9})(.)(.+)$” ) {
set $serp $2;
set $uid $1$2$3;
}
set $fa A;
if ( $serp ~ “(A|B|C|D)” ) {
set $fa B;
}
##这个也可以出C方案D方案,就和 $serp分吧,但是必须是1/16的。。
log_format main ‘$uid_got – $serp – $uid_set’; //debug Log
access_log logs/php.log main; //debug//
#####要想做a/b test对$serp进行正则即可。。###
fastcgi_pass 127.0.0.1:9000;
fastcgi_param FA $fa #将方案号传递给php $_SERVER['FA']
fastcgi_param UID $uid; #传递给php $_SERVER['UID']
感觉有点复杂了,因此我不打算使用这种方法。
三:使用ip_hash
ip_hash也是nginx自带的命令,通常用于负载均衡。配置方法:
upstream backend {
ip_hash;
server 211.100.26.100:80;
server 211.100.26.101:80;
}
这种方法实现简单,但是不能通过weight来分配权重。同一个IP访问,每次得到的结果是一致的。
这种实现方式有缺点。假如你的server上面有数千个静态页面,而你需要做A/B测试的或许只有一个页面,你仍然得将所有代码都COPY到每个机器上面去。
四:第二种ip匹配方法
这种方法是使用nginx的正则匹配上打主意,请看代码:
location / {
if ($remote_addr ~ "[02468]$") {
rewrite ^(.+)$ /experiment$1 last;
}
rewrite ^(.+)$ /main$1 last;
}
location /main {
internal;
proxy_pass http://www.reddit.com/r/lisp;
}
location /experiment {
internal;
proxy_pass http://www.reddit.com/r/haskell;
}
简单解释一下代码的意思,$remote_addr表示用户的IP。第一个判断语句的意思是,如果IP第四段最后一个数字,如果是0、2、4、6、8,就转向到test中去。这种方式也可以调整概率。假如我希望experiment页面显示的概率是20%,那么我可以这样写: $remote_addr ~ "[0]$" 这样就实现了概率的控制。同一个IP访问服务器时,每次得到的结果都是一样的。
这种方式也是我目前最主要使用的一种方法。
五:自己写一个fastcgi程序
这种实现方式是我自己想的。我编写了一个abtest.cgi的fastcgi程序。
访问方式:/abtest.cgi?templist=a.html;b.html;c.html;d.html。参数templist是需要显示的不同页面。
我只需要以SSI的方式将这段代码嵌入到页面中,就神不知鬼不觉了。
下面贡献出我写的代码:
#include "fcgi_server.h"
#include "fcgi_processor.h"
#include "fcgi_stdio.h"
#include <string>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <vector>
#include <map>
using namespace std;
/*
[A/B测试通用类]说明:
本CGI的访问形式为:
/abtest?ip=$REMOTE_ADDR&templist=page_a.html;page_b.html;page_c.html
ip参数表示用户的IP地址,templist表示模板列表。
思路:
将用户的IP地址切成4个整数相加,然后%模板数量,得到模板下标。
然后根据得到的模板下标,读取相应的模板,输出。
*/
#define BLOCK_SIZE 40960
//切割字符串的函数,用于后面处理IP hash
vector<string> explode(const char *sep , const char* str){
int i,last_i;
string ss(str),block;
vector<string> ips;
size_t len = strlen(str);
for(i=0,last_i=0;i<len;i++){
if(str[i] == sep[0]){
block = ss.substr(last_i , i - last_i).c_str();
ips.push_back(block);
//cout<<block<<endl;
last_i = i + 1;
}
}
block = ss.substr(last_i , len - last_i).c_str();
ips.push_back(block);
//cout<<block<<endl;
return ips;
}
//IP转化为INT以便hash
int ip2int(const char* str){
const char *sep = ".";
vector<string> ips;
ips = explode(sep , str);
return atoi(ips[0].c_str())+atoi(ips[1].c_str())+atoi(ips[2].c_str())+atoi(ips[3].c_str());
}
string file_get_contents(string filename){//读取静态文件内容以输出
ifstream input;
char c;
int n=0;
char html[BLOCK_SIZE] = "";
filename = "/path/to/your/document/abtest/" + filename;
input.open(filename.c_str());
if(input.fail())
{
cerr<<" File not found"<<endl;
return "404 File not found";
}
while(!input.eof())
{
input.get(c);
html[n++] = c;
}
input.close();
return html;
}
string ip2html(std::string ip_addr , std::string templist){
int value = ip2int(ip_addr.c_str());
map<string,string> mytemplates;
string mytemp;
const char *sep = ";";
vector<string> temps;
temps = explode(sep , templist.c_str());
mytemp = temps[value % temps.size()];
return file_get_contents(mytemp);
}
class MyProcessor : public FcgiProcessor
{
public:
void HandleRequest(FcgiRequest* pRequest, FcgiResponse* pResponse)
{
pResponse->SetContentType("text/html");
pResponse->PrintHeader();
std::string myip = pRequest->GetClientIp();
std::string templist = pRequest->GetParam("templist");
printf(ip2html(myip , templist).c_str());
printf("<div style='display:none;'>");
printf("IP:[%s]\n" , myip.c_str());
printf("</div>");
}
};
int main()
{
MyProcessor processor;
FcgiServer svr(&processor);
svr.Run();
return 0;
}
(这个代码写得烂别见怪。我只是个PHPer,C++代码只在大学时学过,丢光了。)
使用这种方式实现有一个好处,就是可以实现很多个性化的功能,而你只需要稍微修改一下上面的代码。当然,前提是你必须懂C++和fastcgi。
看到我这里使用C++写了一个fastcgi接口,可能你会说既然这样何不使用PHP?主要的原因是PHP太笨重了,对于每秒钟千次的请求,PHP必死无疑。
六:使用SSI判断
既然可以在nginx.conf中使用条件判断,那么是不是也可以使用SSI中的条件判断来实现呢?这样的话,就更具有通用性了。
笔者我昨天花了一个下午做实验,在apache下实验成功了,可是在nginx中却不行。在谷歌中搜索了一下,说是nginx对SSI的支持还不够好。
希望往后新版本的nginx能将这一块继续优化一下啦!
分享到:
相关推荐
本源码是基于C开发的Nginx Virtual Host A/B Testing设计,包含91个文件,其中包括29个.json文件,19个.h文件,以及19个.c文件。此外,还包括4个.md文件,3个.yml文件,...该项目是一个Nginx虚拟主机A/B测试的设计。
在实际工作中,有时我们需要在同一台Linux服务器上安装并运行多个Nginx实例,以满足不同项目的需求或进行A/B测试等操作。本文将详细介绍如何在Linux系统下安装两个Nginx,并确保它们能够正常运行而不互相干扰的方法...
Nginx 提供 A/B 测试模块,允许在不同用户间测试多种版本的页面或应用。 通过以上特性,Nginx 1.10 成为了构建高效、稳定、安全的 Web 系统的理想选择。其简洁的设计和强大的功能使其在 IT 领域中广受欢迎。对于...
b) 安装 nginx-1.3.13.tar.gz 1. cd /home/rick -- 进入软件目录 2. tar -zxvf nginx-1.3.13.tar.gz -- 解压 3. cd nginx-1.3.13 -- 进入目录 4. ./configure --prefix=/usr/local/nginx -- 设置安装路径 5. make 6...
- **目标**:配置Nginx正向代理服务,使得服务器B可以通过服务器A访问http和https资源。 #### 2. 安装依赖环境 在服务器A上(IP地址为192.168.252.247)安装必要的依赖库: ```shell yum install gcc gcc-c++ ...
- **测试 DNS 解析**:可以使用 `nslookup` 或 `dig` 命令来测试域名是否成功解析到对应的 IP 地址。例如: ```bash nslookup mgcrazy.com nslookup www.mgcrazy.com ``` ##### 2. Nginx 配置 - **编辑 Nginx ...
在Nginx服务器的配置中,超时时间的设置至关重要,因为它关系到服务器对客户端请求的响应速度和系统的稳定性。本文将深入讲解如何在Nginx中配置超时时间,并介绍相关的参数设置。 首先,我们需要了解何时需要设置...
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -c:a aac -ar 44100 -ac 2 -b:v 500k -maxrate 500k -bufsize 1000k -vf "scale=w=1280:h=720,format=yuv420p" -b:a 128k -f flv rtmp://your-server-...
**15.5 Nginx的内存池管理分析(b)** 继续深入探讨Nginx内存池管理的相关技术细节。 **15.6 Nginx数据结构数组,列表** 探讨Nginx内部使用的数据结构,如数组和链表等。 **15.7 Nginx源代码分析** 对Nginx的源代码...
Nginx是一款高性能的Web服务器和反向代理服务器,以其轻量级、高效能和高并发处理能力在互联网行业中广泛应用。本文件主要介绍了Nginx的配置与部署,特别是Rewrite规则的设置以及Nginx的基本操作命令。 1. **Nginx ...
a)对c:\nginx\conf\nginx.conf文件进行配置: - 1 - b)常用的 Nginx 参数 - 3 - c)静态文件处理 - 4 - d)动态页面请求处理 - 4 - e)下面为nginx.conf配置实例: - 5 - f)Nginx 启动,停止等命令 - 8 - (2) Resin安装...
配置完成后,需要执行sudonginx -t来测试nginx配置文件是否有误,如果没有问题,再执行sudonginx -s reload来重载配置文件,使修改生效。 3. 处理可能出现的错误。如果在重启nginx时遇到错误nginx:[error] invalid ...
4. **A/B测试**:通过Lua脚本来实现不同版本的服务或界面之间的切换,以进行A/B测试。 5. **流量复制**:通过Lua脚本复制一部分请求到另一个目的地,用于日志记录或数据分析。 6. **协程管理**:利用Lua的协程特性来...
开发测试过程中,因为某些原因,想要让手头的A、B域名同时指向云服务器的443端口,支持HTTPS。 Nginx支持TLS协议的SNI扩展(同一个IP上可以支持多个不同证书的域名),只需要重新安装Nginx,使其支持TLS即可。 安装...
Nginx配置文件测试** 在修改Nginx配置文件后,可以通过命令`nginx -t`来检查配置是否正确无误。如果配置有误,Nginx会显示错误信息,帮助开发者快速定位问题所在。 **4. Nginx启动** 启动Nginx服务通常使用命令`...