github代码:https://github.com/singgel/nginx-luaDemo/
一. 需求背景
需要把旧的推荐服务逐步切换到新的推荐服务上,需要灰度切换,流量比例和灰度策略可以控制。
二. 方案
当前数据请求流程是:外部请求—>易车nginx --->后端服务 ;
经过跟运维沟通发现,目前易车nginx 是公司级别的 不允许某个业务对配置的修改,所以我们在易车nginx 和 后端服务之间添加了一个新的转发组件;已经跟运维沟通过该方案可行。
小流量数据请求流程为:外部请求--->易车nginx---->小流量转发组件—>后端服务
目前对于转发组件的选型有两种:
1.使用nginx + lua来实现
2.使用openresty
经过调研我们打算使用的转发组件是 openresty 方案,在原有业务之上加入openresty代理,使用lua来控制流量比例和灰度策略;
OpenResty是在nginx的基础上,集成了很多第三方模块,比如默认带了lua模块,开发人员可以使用Lua语言对Nginx进行脚本编程,很多第三方模块跟openresty更加兼容,比如
https://github.com/openresty/lua-nginx-module, https://github.com/openresty/lua-resty-redis这些都是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)访问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