在我们的业务版本迭代中,经常会出现新版本需要灰度上线,仅先用于部分门店(SAAS点餐系统),导致生产环境需要同时运行多个版本代码,一开始我们的临时处理方案是:在NGINX项目配置文件中,通过判断客户端上传的HEADER信息中的版本号,改变ROOT值,解析到相应的代码分支。这种方式每出一个新包都要手动做一次配置文件的修改,十分不便。
后来自己将服务器上的NGINX换成了Openresty(这个过程并不复杂,对已有业务也没影响),然后写了一个LUA脚本,可以通过后台去配置门店所需要使用的分支,并且按照设备号对应出的分支缓存到REDIS,后台再增加一个按设备号(支持批量)清缓存的功能,便实现了代码灰度发布的自由配置。
相关截图一(后台设置门店对应的接口分支名称):
相关截图二(查看设备号对应的分支名称及清除缓存按钮)
最后是改变ROOT的LUA代码:
local config = require "config"
local g = require "lib.g"
local redis = require "lib.redis"
local mysql = require "lib.mysql"
local header = ngx.req.get_headers()
local device_no = header.deviceno
local store_id = header.storeid
if(device_no == nil and store_id == nil)
then
g.log("empty device_no and store_id")
return
end
local redis = redis:new(config:get("redis"))
local key,path
if(device_no ~= nil) --设备号--
then
key = "lua|device_no|api_path"
path = redis:hget(key, device_no)
else --门店ID--
key = "lua|store_id|api_path"
path = redis:hget(key, store_id)
end
if(path == 'default')
then
return
end
if not g.empty(path)
then
ngx.var.branch = path
return path
end
if(device_no ~= nil) --设备号--
then
redis:hset(key, device_no, 'default')
g.log("no lua cache|device_no:" .. device_no)
else --门店ID--
redis:hset(key, store_id, 'default')
g.log("no lua cache|store_id:" .. store_id)
end
local db_config = config:get("mysql")
local db_base_config = db_config.db_base
local db_base = mysql:new(db_base_config)
local device_info
if(device_no ~= nil) --设备号--
then
--通过设备号获取门店ID
local sql = "select store_id from wt_device where device_no=:device_no"
local bind = {
device_no = device_no
}
device_info = db_base:findOne(sql, bind)
if g.empty(device_info)
then
g.log("empty device_info")
return
end
if g.empty(device_info.store_id)
then
g.log("empty device_info.store_id")
return
end
end
--通过门店ID获取路径配置
local sql = "select path_name from wt_store_config where store_id=:store_id"
local bind
if(device_no ~= nil) --设备号--
then
bind = {
store_id = device_info.store_id
}
else --门店ID--
bind = {
store_id = store_id
}
end
local store_config = db_base:findOne(sql, bind)
if g.empty(store_config)
then
g.log("empty store_config")
return
end
if g.empty(store_config.path_name)
then
g.log("empty store_config.path_name")
return
end
if(device_no ~= nil) --设备号--
then
redis:hset(key, device_no, store_config.path_name)
else --门店ID--
redis:hset(key, store_id, store_config.path_name)
end
ngx.var.branch = store_config.path_name
return

