一. 需求背景

需要把旧的推荐服务逐步切换到新的推荐服务上,需要灰度切换,流量比例和灰度策略可以控制。

 

二. 方案

当前数据请求流程是:外部请求—>易车nginx --->后端服务 ;

经过跟运维沟通发现,目前易车nginx 是公司级别的 不允许某个业务对配置的修改,所以我们在易车nginx 和 后端服务之间添加了一个新的转发组件;已经跟运维沟通过该方案可行。
小流量数据请求流程为:外部请求--->易车nginx---->小流量转发组件—>后端服务

目前对于转发组件的选型有两种:

1.使用nginx + lua来实现

2.使用openresty

经过调研我们打算使用的转发组件是  openresty 方案,在原有业务之上加入openresty代理,使用lua来控制流量比例和灰度策略;

 

OpenResty是在nginx的基础上,集成了很多第三方模块,比如默认带了lua模块,开发人员可以使用Lua语言对Nginx进行脚本编程,很多第三方模块跟openresty更加兼容,比如

https://github.com/openresty/lua-nginx-modulehttps://github.com/openresty/lua-resty-redis这些都是openresty的第三方模块,不过兼容nginx罢了。另外安装openresty有自己的repo仓库,安装其第三方模块也很简单方便;

该方案在 360 ,新浪等公司有较为成熟的运用;

具体安装配置方案已经在测试环境测试成功,具体如下诉 OpenResty方案测试

 

三.上线前准备:

  1. 在线上部署openresty
  2. 将易车ng 跟后端服务之间配置 转发
  3. 上线新的推荐逻辑
  4. 启动灰度策略


四.部署方案:

  1. 在首鸣机房部署好新的推荐服务并验证通过,首鸣机房只部署新的推荐服务
     
  2. 准备openResty ,部署在M5  机房 推荐服务api 所在的机器上,共十台机器部署10个openResty 。
       openResty 分流策略存储在redis ,默认策略是100%流量进入旧的推荐服务;当无法获取redis策略时,所有流量进入旧的推荐服务;
       业务请求进入入口nginx (目前在M5机房) 会轮询到 10 台 openResty ,当策略切换时只用修改响应的redis key 对应的值,不操作openResty ,解耦操作;
       openResty 经过策略计算 将90%的请求轮询到 M5 所在的旧的推荐api 上, 将10%的请求通过机房专线轮询到首鸣所在的推荐服务上。
     
  3. 部署好openResty 并验证通过之后,在修改入口ng转发地址
  4. 部署大致示意如下:

 


 

可能存在的问题:跨机房严重依赖内网专线,内网专线属于机房间内部线路,发生故障后维修时间难以保证,以天级别计算,存在重大隐患。

针对上述问题,我们的解决方案是,当流量进入M5之后,在openResty根据小量策略做判断,命中策略之后直接通过新服务域名转发到新推荐服务,未命中策略则还走之前的老的服务。

经过跟运维的沟通,通过域名转发请求的时候 会有限使用专线,如果专线有故障则会走公网;公网跟专线之间的切换由运维同事完成。

 

五.  待完成事项:

  1. 根据业务需求编写策略分流以及分流比例的脚本;
  2. 测试把策略保存到redis中,lua脚本定时从redis获取策略;

六. OpenResty方案测试

  1.  机器列表
192.168.87.237 // 部署openresty + lua, 实现灰度策略
172.20.4.127 // 部署业务1,测试环境使用nginx静态页代替
192.168.15.47 // 部署业务2, 测试环境使用nginx静态页代替
  1.  代理机器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
  1. 后端业务机器,使用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;
}
  1. 测试, 本地绑定host (192.168.87.237 test.lua.yiche.com)

    (1)访问http://test.lua.yiche.com/testlua.html?p=11,  p设置为数字类型,页面访问到了172.20.4.127




    (2)访问http://test.lua.yiche.com/testlua.html?p=ss,  p设置为字符串类型,页面访问到了192.168.15.47