以OpenResty搭建RTB竞价引擎接入层
概念解析
OpenResty: OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关,以下或也称之为Nginx。
RTB竞价引擎: 互联网广告实时竞价系统,以下的tomcat为其单实例。 当我们的应用单实例不能支撑用户请求的时候,此时就需要扩容,一般称之“横向扩容”,即:从一台服务器扩容到两台、几十台不等。如下是我们的架构方式:
其中红框中的nginx群,我们称之为“接入层”。在广告引擎系统中,常常需要做一些A-B测试用于对一些投放策略的比较、新增或者更改的功能同样需要拿到线上做bug风险测试,而所有这些都需要以重启服务器来实现。投放系统流量大且实时性要求高,重启系统导致系统的波动不说,还会影响整个广告系统的收入,这就成了我们的痛点。
发现痛点,就解决它!如此,我们便有了以上的架构。对于开发同学来说,只需要关心到接入层就够了,Haproxy由运维同学维护。在接入层,我们搭建流量分发功能,以下称之为“灰度发布”。灰度发布,通过lua脚本实现了特定的流量发送到特定的Tomcat服务。这还不够,服务的启停,需要实时动态的流量切换,这就需要一个服务发现系统。而我们选择在接入层搭建这些功能,解决这个问题,无非是考虑到与业务逻辑解耦,同时看中了OpenResty的高并发、高吞吐以及丰富的模块设计。
如下,分两个部分介绍一下我们的实现方式:
服务发现
我们的服务发现功能是基于OpenResty+upsync+consul功能搭建,辅以nginx_upstream_check_module做健康检查。 upsync是由微博开源的一款nginx插件,可以从consul或其他服务发现框架同步上游服务器,动态修改后端服务器属性(weight,max_fails等),而无需重新加载nginx。对于大流量、高负载的RTB引擎来说,nginx reload会进一步增加系统负载并暂时降低性能。而且增加upsync模块,将我们的服务于调用者来说完全解耦,后端系统的升级重启,乃至限流扩容都透明化了。
upstream online {
server 0.0.0.1:1234;
upsync 127.0.0.1:80/v1/kv/upstreams/springboottest upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
upsync_dump_path /usr/local/nginx/nginx/conf/servers/spring-boot-consul.conf;
upsync_lb hash_ketama;
hash $arg_uid consistent;
check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;
check_http_send "HEAD / HTTP/1.0rnrn";
check_http_expect_alive http_2xx http_3xx;
}
其中代码行:
第2:为假数据,预防启动时nginx报错;
第3:为拉取consul数据地址,此处使用kv形式;
upsync_timeout :拉取超时时间,不可小于5m;
upsync_interval :拉取时间间隔;
upsync_type :拉取形式,此处使用consul;
strong_dependency:打开时,每次nginx启动或重新加载时,nginx都会从consul中提取服务器,此处关闭;
第4: 为服务器列表落地本地地址;
第5:结合第6行,设置一致性hash;
第7:设置healthy check的检测方式:
`syntax: *check interval=milliseconds [fall=count] [rise=count][timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp|fastcgi]*`
interval :检测间隔,单位milliseconds;
rise :检查成功rise次后,服务器被标记为启动;
fall :检查失败fall次后,服务器被标记为关闭;
timeout :健康检测超时时间
type :健康检测类型
default_down:设置后端服务器的初始状态,默认为关闭。
如配置所示,nginx会定时从consul拉取上游服务器列表,同时落地在本地(防止consul集群不可用)。由于相对传统的取模操作一致性hash对后端服务的缓存友好且拥有更好的容错性与可扩展性,所以我们这里使用了一致性hash。upsync对每个上游服务器增加了160*weight个虚拟节点,使其负载均衡。
对于consul而言,我们只选用了它的kv功能,更看重的是,consul提供了time_wait和修改版本号概念,如果consul发现该kv没有变化就会hang住这个请求5分钟,在这5分钟内如果有任何变化都会及时返回结果。通过比较版本号我们就知道是超时了还是kv的确被修改了。由此OpenResty可以及时地获取consul的kv store变化。而consul不支持多路注册,使用它的service功能的话,很可能导致注册结果不可控,这一直以来是consul为开发者所诟病的大坑。而且注册对于后端服务来说,它和其所注册的server是一一对应的,一旦consul某个server挂掉,后端服务也被认为不存在了,这是我说不能容忍的。所以取用其长,而避用其短,搭配nginx的nginx_upstream_check_module所提供的丰富的api和健康检查方式实现服务发现功能。
灰度发布
百度词条对灰度发布的定义是:又名金丝雀发布,是指在黑与白之间,能够平滑过渡的一种发布方式。我们的灰度发布功能则是在服务发现的基础之上,增加流量分发层,使进入接入层的流量可以根据我们的业务需求,如userId、广告位、Ip、Ip段、操作系统等制定相应的规则,指定到相应的上游服务器中,便于我们测试一些新进功能或者策略的优劣。流程图如下。
我们的规则是通过json的方式存储于redis中,但是json的方式是非常浪费性能的,所以我们均采用unix domain socket的方式连接本地redis从库,OpenResty的lua-resty-redis对此支持非常好;其次在其上层使用lua_shared_dict搭建缓存,把相关的分发加入缓存中,从而降低redis的压力。另外,采用加权平均的方式,可以将指定流量的一部分分发到指定的一台或者多台机器上去。
这是我前两年发布在知乎上的文章,拿过来一起整理下。请多多批评
- 使用Python绘制点击图、热图
- 使用shell生成状态报表(r2笔记61天)
- 利用d3.js对QQ群资料进行大数据可视化分析
- 海量数据迁移之分区并行切分(r2笔记60天)
- 数据结构和算法——kd树
- shell脚本心得(r2笔记58天)
- C/C++——柔性数组
- 用shell脚本巧妙统计文件(r2笔记57天)
- MATLAB技巧——imshow多张图片
- MATLAB技巧——sort和sortrows函数
- Python对商品属性进行二次分类并输出多层嵌套字典
- 通过shell得到数据库中权限的脚本(r2笔记77天)
- 用Python实现PCA和MDA降维和聚类
- 通过shell解析dump生成parfile(r2笔记76天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 将MySQL复制限制为基于行的事件
- 在tinycorelinux上安装lxc,lxd (1)
- Ubuntu16.04下安装python3.6
- Mybatis 注解
- Python如何使用Matplotlib的作图
- 在tinycolinux上组建子目录引导和混合32位64位的rootfs系统
- 微服务中的负载均衡简单实现
- 3分钟短文:素未谋面,Laravel数据库模型初阶入门
- 在tinycolinux上编译seafile
- Alink漫谈(二十一) :回归评估之源码分析
- Linux环境下通过GDB调试C项目实战
- Alink漫谈(二十二) :源码分析之聚类评估
- Python3.x将代码打包成exe程序并添加图标
- 在tinycolinux上编译pypy和hippyvm
- IDEA 热部署配置 HotSwapAgent-IntelliJ-IDEA-plugin