使用 nginx 进行 ab 站点的过程简单分析

本贴最后更新于 2989 天前,其中的信息可能已经沧海桑田

由于业务需要,在官网上部署两套前端页面,通过特定的字段(例如手机号码)进行分流,来达到 a/b 站的要求,后续对 a/b 站最终数据进行分析,选出哪部分页面对用户体验来说会更优秀。

nginx 请求分流

考虑利用 nginx 的分流功能:http://neoremind.com/2012/03/nginx%E6%A0%B9%E6%8D%AEcookie%E5%88%86%E6%B5%81/

在 mac 下使用 brew install nginx,安装完成后的目录为:/usr/local/Cellar/nginx/1.10.3(根据不同的版本会有所不同)/,nginx 配置文件所在目录:/usr/local/etc/nginx。

在 Postman 中需要安装下载 Postman Interceptor 扩展程序,此时就可以通过发送 Headers 中的内容,来达到发送 Cookie 的目的:

[9D71E1F0-1777-4E15-876F-E3C74DD6F6C9.png](http://pic.findyou.xin/e84e776bb97f44d992454a680cb6daf7.png)

在 nginx 中,可以根据该 cookie 进行匹配判断,决定要发送的服务器 upstream:

match cookie set $stream stream0; if ($http_cookie ~* "phone=([^;]+)(1$)"){ set $stream stream1; } if ($http_cookie ~* "phone=([^;]+)(2$)"){ set $stream stream2; }

在上面的示例中,仅能匹配单个 http_cookie 的最后一行,如果我们想要根据手机尾号进行用户划分的话,必须要匹配多个属性:

match cookie set $stream stream0; if ($http_cookie ~* "phone=([^;]+)([5-9]$)"){ set $stream stream1; } if ($http_cookie ~* "phone=([^;]+)([0-4]$)"){ set $stream stream2; }

进行范围查找,如果在 5-9 之间,对应 stream1,否则对应 stream2,如果没有该 cookie,需要给定一个默认值 stream0。

上述情况出现在用户已经登录的情况下,如果请求是处于注册/登录的过程中,此时并没有 cookie 数据,但这两种操作都是通过 POST 请求,在 form 表单中存在对应的字段手机号(phone),考虑是否可以根据 request body 中的字段进行填充。

nginx 中的变量介绍主要如链接中:https://moonbingbing.gitbooks.io/openresty-best-practices/content/openresty/inline_var.html

可以在日志中将 request_body 打印出来,只要加上 request_body 属性即可,如果我们加上的数据为“phone=111”

------WebKitFormBoundaryq2rbBAdTrAuTi6IG\x0D\x0AContent-Disposition: form-data; name=\x22phone\x22\x0D\x0A\x0D\x0A111\x0D\x0A------WebKitFormBoundaryq2rbBAdTrAuTi6IG--\x0D\x0A

可见这些字段是已经经过了额外的转义处理,如果想要分析 request body 中的字段比较麻烦,nginx 只有在修改插件运行的情况下(对 nginx 本身进行编程),才能访问到 request body 中的字段。

因此我们的方案调整为,注册/登录完成后写 Cookie,但不能马上刷新缓存,但可以通过页面上的 ajax 请求 success 回调,去强制重刷整个页面来获取 a/b 站点对应 js/css 资源,但可能造成额外的流量损耗。

内部域名解析/转换

但我们部署的服务理论上是在两台 docker 容器上,并无固定 ip,是通过不同的内部域名进行处理的,因此在 upstream 出现域名时,就会发生无法转发的问题,即定义的 http://${url}并不进行替换。

upstream main { server web1.local:80; server web2.local:80; server web3.local:80; }

通过问题查找,参考下面的一篇文章:

曾经尝试了第一种方式,设置 proxy_set_header,并没有起作用:

proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr;

第二种方式理论上应该可行,是通过开放多个端口的方式,建立几个 virtual server,但由于我们将系统部署在 lain(docker 的一种实践)上,限制条件比较多,只能开放一个 web 端口,因此该方式在 lain 环境上不可行。

server { listen 8001 default_server; server_name web1.example.com; location / { proxy_pass http://web1.local:80; proxy_set_header Host web1.local:80; } } server { listen 8002 default_server; server_name web2.example.com; location / { proxy_pass http://web2.local:80; proxy_set_header Host web2.local:80; } } server { listen 8003 default_server; server_name web3.example.com; location / { proxy_pass http://web3.local:80; proxy_set_header Host web3.local:80; } } upstream main { server 127.0.0.1:8001; server 127.0.0.1:8002; server 127.0.0.1:8003; } server { listen 80; server_name example.com; location / { proxy_pass http://main; } }

Tengine 提供此支持,http://tengine.taobao.org/document_cn/http_upstream_dynamic_cn.html,但通过测试发现 tengine 支持的这种方式可能只能利用外网可解析的域名来处理,如果是内网域名仍然是与没有配置该模块的结果相同。

upstream stream80 { dynamic_resolve fallback=next fail_timeout=30s; #server www.xxx.cn; server xxx.xxapp.xyz; }

转移到 xxx.xxapp.xyz,此为内部解析的域名:

[Snip20170302_2.png](http://pic.findyou.xin/38257ca397a84fc896e0d9f617f99cc0.png)

我们将转移到 www.xxx.cn,会发现已经进行了转换(错误是由于 servername 名称不匹配)

[Snip20170302_2.png](http://pic.findyou.xin/ba9885a13dcb4797ab9f50e473deae58.png)

基本判断 tengine 的这个模块应该是可用的,但域名解析可能用到了一些特殊的条件或算法,导致无法解析我们内网的域名,所以在只能部署单个对外端口的 docker 容器下,暂时不能解决内网 upstream 带 server_name 的问题(最终考虑将其部署在虚拟机上,开启多个端口来解决该问题,也就是参考链接中的第二条)。

  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖
  • ab
    1 引用 • 1 回帖
  • cookie分流
    1 引用 • 1 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • alanfans

    我就喜欢这样的干货

推荐标签 标签

  • 笔记

    好记性不如烂笔头。

    310 引用 • 794 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    949 引用 • 1460 回帖
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖 • 2 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 58 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 676 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    693 引用 • 537 回帖 • 1 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    345 引用 • 747 回帖 • 1 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 181 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 7 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 448 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 2 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    83 引用 • 37 回帖
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 555 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1454 回帖 • 1 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    133 引用 • 796 回帖 • 1 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 113 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    22 引用 • 148 回帖 • 16 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 2 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    58 引用 • 25 回帖 • 4 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 667 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖
  • 自由行
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 544 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    170 引用 • 1529 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    246 引用 • 1338 回帖