github代码:github.com/singgel/ngi…
一. 需求背景
需要把旧的推荐服务逐步切换到新的推荐服务上,需要灰度切换,流量比例和灰度策略可以控制。
二. 方案
当前数据请求流程是:外部请求—>易车nginx --->后端服务 ;
经过跟运维沟通发现,目前易车nginx 是公司级别的 不允许某个业务对配置的修改,所以我们在易车nginx 和 后端服务之间添加了一个新的转发组件;已经跟运维沟通过该方案可行。
小流量数据请求流程为:外部请求--->易车nginx---->小流量转发组件—>后端服务
目前对于转发组件的选型有两种:
1.使用nginx + lua来实现
2.使用openresty
经过调研我们打算使用的转发组件是 openresty 方案,在原有业务之上加入openresty代理,使用lua来控制流量比例和灰度策略;
OpenResty是在nginx的基础上,集成了很多第三方模块,比如默认带了lua模块,开发人员可以使用Lua语言对Nginx进行脚本编程,很多第三方模块跟openresty更加兼容,比如
github.com/openresty/l…, github.com/openresty/l…这些都是openresty的第三方模块,不过兼容nginx罢了。另外安装openresty有自己的repo仓库,安装其第三方模块也很简单方便;
该方案在 360 ,新浪等公司有较为成熟的运用;
具体安装配置方案已经在测试环境测试成功,具体如下诉 OpenResty方案测试
三.上线前准备:
- 在线上部署openresty
- 将易车ng 跟后端服务之间配置 转发
- 上线新的推荐逻辑
- 启动灰度策略
四.部署方案:
- 在首鸣机房部署好新的推荐服务并验证通过,首鸣机房只部署新的推荐服务
- 准备openResty ,部署在M5 机房 推荐服务api 所在的机器上,共十台机器部署10个openResty 。
openResty 分流策略存储在redis ,默认策略是100%流量进入旧的推荐服务;当无法获取redis策略时,所有流量进入旧的推荐服务;
业务请求进入入口nginx (目前在M5机房) 会轮询到 10 台 openResty ,当策略切换时只用修改响应的redis key 对应的值,不操作openResty ,解耦操作;
openResty 经过策略计算 将90%的请求轮询到 M5 所在的旧的推荐api 上, 将10%的请求通过机房专线轮询到首鸣所在的推荐服务上。
- 部署好openResty 并验证通过之后,在修改入口ng转发地址
- 部署大致示意如下:
可能存在的问题: 跨机房严重依赖内网专线,内网专线属于机房间内部线路,发生故障后维修时间难以保证,以天级别计算,存在重大隐患。
针对上述问题,我们的解决方案是,当流量进入M5之后,在openResty根据小量策略做判断,命中策略之后直接通过新服务域名转发到新推荐服务,未命中策略则还走之前的老的服务。
经过跟运维的沟通,通过域名转发请求的时候 会有限使用专线,如果专线有故障则会走公网;公网跟专线之间的切换由运维同事完成。
五. 待完成事项:
- 根据业务需求编写策略分流以及分流比例的脚本;
- 测试把策略保存到redis中,lua脚本定时从redis获取策略;
六. OpenResty方案测试
-
机器列表
192.168``.``87.237
// 部署openresty + lua, 实现灰度策略``172.20``.``4.127
// 部署业务1,测试环境使用nginx静态页代替``192.168``.``15.47
// 部署业务2, 测试环境使用nginx静态页代替
-
代理机器192.168.87.237部署openresty + lua:
// 安装openresty+lua, 参考http://openresty.org/cn/installation.html, http://openresty.org/cn/linux-packages.html, 这里使用预编译包:
// 添加centos openresty repo``yum install yum-utils``yum-config-manager --add-repo https:``//openresty.org/package/centos/openresty.repo
// install openresty with yum ``yum install openresty
// 新建test.lua.yiche.com vhost``mkdir /usr/local/openresty/nginx/conf.d``cat /usr/local/openresty/nginx/conf.d/test.lua.yiche.com.conf ``// 内容如下``init_by_lua_file /usr/local/openresty/site/lualib/init.lua; # lua初始化脚本``upstream tomcat_a.domian.com {`` ``server ``172.20``.``4.127``;``}``upstream tomcat_b.domain.com {`` ``server ``192.168``.``15.47``;``}``server {`` ``listen ``80``;`` ``server_name test.lua.yiche.com;`` ``set $default_backend ``'tomcat_a.domian.com'``;`` ``location / {`` ``proxy_next_upstream http_500 http_502 http_503 http_504 error timeout;`` ``proxy_set_header Host ``'test.lua.yiche.com'``;`` ``proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`` ``expires ``0``;`` ``set $backend $default_backend;`` ``rewrite_by_lua_file ``"/usr/local/openresty/site/lualib/diversion.lua"``; # 分流策略, 此脚本不会被一直缓存,改完以后不用reload配置也会片刻后就生效`` ``proxy_pass http:``//$backend;`` ``}``}
// 编辑初始化全局设置脚本``cat /usr/local/openresty/site/lualib/init.lua``global_configs = {`` ``[``"divEnable"``] = ``true
-- 分流开关``}
// 编辑分流策略脚本, 测试样例是如果分流开关开启,参数p如果是数字类型,请求转发到一个backend;参数p如果是字符串类型,请求转发到另外一个backend;``cat /usr/local/openresty/site/lualib/diversion.lua``if
not global_configs[``"divEnable"``] then`` ``return``end``local p = ngx.var.arg_p ``if
p then`` ``local nump = tonumber(p);`` ``if
nump then`` ``ngx.var.backend = ``"tomcat_a.domian.com"`` ``else`` ``ngx.var.backend = ``"tomcat_b.domain.com"`` ``end``end
-
后端业务机器,使用nginx静态页面代替业务
# ``172.20``.``4.127``, ``192.168``.``15.47``增加vhost,分别在/usr/share/nginx/html下放置一个静态页testlua.html, 页面编辑为不同的内容便于观察`` ``server {`` ``listen ``80``;`` ``server_name test.lua.yiche.com;`` ``access_log /var/log/nginx/test.lua.yiche.com.access.log main;`` ``location / {`` ``root /usr/share/nginx/html;`` ``index index.html index.htm;`` ``}``
-
测试, 本地绑定host (192.168.87.237 test.lua.yiche.com)
(1)访问test.lua.yiche.com/testlua.htm…, p设置为数字类型,页面访问到了172.20.4.127
(2)访问test.lua.yiche.com/testlua.htm…, p设置为字符串类型,页面访问到了192.168.15.47