目标利用openresty配合Lua脚本实现基于redis配置进行灰度发布及最小版本约束。实现如下功能:1、随机灰度2、基于用户ID灰度(用户ID%100<Radio)3、基于指定用户ID灰度(例如用户ID:2、3)4、基于App版本号进行灰度(例如内部版本号:31)5、全量灰度6、App最小版本约束代码约束1、App请求头中携带客户端类型(X-App-Type)、客户端版本号(X-App-Version)2、App请求头中携带Token信息(X-Client-Token),用于获取用户ID(测试脚本中token生成规则为 userid_token),实际使用中,需要根据token机制进行用户ID转换(修改getUserId方法)3、代码中自动忽略了版本号为空或为0的情况,如果需要判断,需要修改分发逻辑4、通过修改send_upgrade方法,自行配置版本过低的提示5、客户端版本号为数字6、客户端类型为数字;代码中100:代表ios 200:代表androidRedis 配置参考(gray.config){ "ratio": "30", "minVersion": "1", "versions": [ "10" ], "type": 1, "gray": "192.168.1.2:8080", "default": "192.168.1.2:8081", "userIds": [ "1" ]}Nginx 全局配置 增加如下代码....http{ .... lua_code_cache on; lua_shared_dict gray_cache 10m; ....}Nginx 转发配置server { listen 80; location / { set $target ''; default_type text/html; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; access_by_lua_file /etc/nginx/lua/gray.lua; proxy_pass http://$target$request_uri; }}Lua脚本local redis = require "resty.redis";local cjson = require("cjson")local function isEmpty(s) return s == nil or s == ''endlocal function stringToInt(str) if isEmpty(str) then return 0 end local number = tonumber(str) if not number then return 0 end return numberendlocal function left(str, split) local index = string.find(str, split) if not index then return nil end local result = string.sub(str, 0, index - 1) return resultendlocal function getUserId() -- 根据token计算用户ID,需根据自己的业务就行替换 local token = ngx.req.get_headers()["X-Client-Token"] if isEmpty(token) then return 0 end local uidStr = left(token, "_") if isEmpty(uidStr) then return 0 end return stringToInt(uidStr)endlocal function getClientVersion() local version = ngx.req.get_headers()["X-App-Version"] return stringToInt(version)endlocal function getClientType() -- 客户端类型,在这个地方 100表示ios 200表示 安卓 local version = ngx.req.get_headers()["X-App-Type"] return stringToInt(version)endlocal function close_redis(redis_cluster) if not redis_cluster then return end local pool_max_idle_time = 10000 local pool_size = 100 local ok, err = redis_cluster:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.log(ngx.ERR, "set keepalive fail ", err) endendlocal function read_gray_config(address, port, password, key, default_config) local redis_cache = redis:new(); redis_cache:set_timeout(1000); local ok, err = redis_cache:connect(address, port); if not ok then close_redis(redis_cache) ngx.log(ngx.ERR, "redis 连接错误: ", err) return default_config; end if not isEmpty(password) then local ok, err = redis_cache:auth(password) if not ok then ngx.log(ngx.ERR, "redis 携带密码连接错误: ", err) close_redis(redis_cache) return default_config; end end local res, err = redis_cache:get(key) if not res then ngx.log(ngx.ERR, "redis读取数据错误: ", err) close_redis(redis_cache) return default_config end local json = cjson.new() if not json then ngx.log(ngx.ERR, "创建json错误 ") close_redis(redis_cache) return default_config end if res == ngx.null then local ok, err = redis_cache:set(key, json.encode(default_config)) if not ok then ngx.log(ngx.ERR, "写入默认配置出错: ", err) end ngx.log(ngx.INFO, "灰度配置为空,采用默认配置") close_redis(redis_cache) return default_config else close_redis(redis_cache) return json.decode(res) endendlocal function load_gray(address, port, password, timeout, key, default_config) local share_cache = ngx.shared.gray_cache local cache_data = share_cache:get("config") local json = cjson.new() if not json then ngx.log(ngx.ERR, "创建json对象错误 ") return default_config end if cache_data == nil then cache_data = read_gray_config(address, port, password, key, default_config) if cache_data == nil then ngx.log(ngx.ERR, "获取配置信息返回null") else local ok, err = share_cache:set("config", json.encode(cache_data), timeout) if not ok then ngx.log(ngx.INFO, "刷新本地灰度配置信息失败", err) else ngx.log(ngx.INFO, "刷新本地灰度配置信息成功") end end return cache_data else ngx.log(ngx.INFO, "采用缓存配置信息") return json.decode(cache_data) endendlocal default_cache = { type = 0, -- 灰度类型 0、关闭灰度 1、随机灰度 2、根据用户ID灰度 3、指定用户ID灰度 4、指定用户版本灰度 5、全量灰度 default = "192.168.1.2:8080", -- 正常分发地址 gray = "192.168.1.2:8081", -- 灰度分发地址 userIds = { "0" }, -- 灰度用户ID,例如:{"2","3","4"} ratio = "0", -- 灰度分发比例 minVersion = "0", -- 客户端最小版本号 versions = { "0" } -- 灰度版本号,例如:{"30","31"}}local function contains(value, list) if list == nil or isEmpty(value) then return false end for k, v in ipairs(list) do if v == value then return true; end end return false;end-- 发送版本过低消息local function send_upgrade(minVersion,clientType) local upgrade_response = '{"code":403,"data":{"version":"0","message":"您当前的版本过低,请升级到最新版本!"}}' ngx.header.content_type = "application/json" ngx.say(string.format(upgrade_response,clientType,minVersion,minVersion))endlocal gray = load_gray("127.0.0.1", 6379, "", 10, "gray.config", default_cache)if gray then ngx.var.target = gray["default"] local gray_type = gray["type"] local iosMinVersion = gray["iosMinVersion"] local andoridMinVersion = gray["andoridMinVersion"] local clientType = getClientType() local request_uri = ngx.var.request_uri if (string.find(request_uri, "^/yuliao/uri/") == nil) then local clientVersion = getClientVersion() if clientType == 100 and iosMinVersion ~= nil and iosMinVersion > 0 then -- 判断ios最小版本 local clientVersion = getClientVersion() if clientVersion > 0 and clientVersion < iosMinVersion then ngx.log(ngx.INFO,"uri:",request_uri," send ios upgrade response") send_upgrade(iosMinVersion,clientType) return; end else if clientType == 200 and andoridMinVersion ~= nil and andoridMinVersion > 0 then -- 判断安卓最小版本 if clientVersion > 0 and clientVersion < andoridMinVersion and clientVersion ~= 1 then ngx.log(ngx.INFO,"uri:",request_uri," send android upgrade response") send_upgrade(andoridMinVersion,clientType) return; end end end end if gray_type == 1 then -- 随机灰度 local ratio = stringToInt(gray["ratio"]) local number = math.random(100) % 100 if number < ratio then ngx.var.target = gray["gray"] ngx.log(ngx.INFO, "随机灰度(YES):", " number:", number, " ratio:", ratio, " upstream:", ngx.var.target) else ngx.log(ngx.INFO, "随机灰度(NO):", " number:", number, " ratio:", ratio, " upstream:", ngx.var.target) end elseif gray_type == 2 then -- 用户ID灰度 local ratio = stringToInt(gray["ratio"]) local userId = getUserId() local number = userId % 100 if number < ratio then ngx.var.target = gray["gray"] ngx.log(ngx.INFO, "用户ID灰度(YES):", " userId:", userId, " ratio:", ratio, " upstream:", ngx.var.target) else ngx.log(ngx.INFO, "用户ID灰度(NO):", " userId:", userId, " ratio:", ratio, " upstream:", ngx.var.target) end elseif gray_type == 3 then -- 指定用户ID灰度 local userId = getUserId() if userId > 0 then userId = tostring(userId) local userIds = gray["userIds"] if contains(userId, userIds) then ngx.var.target = gray["gray"] ngx.log(ngx.INFO, "指定用户灰度(YES):", " userId:", userId, " upstream:", ngx.var.target) else ngx.log(ngx.INFO, "指定用户灰度(NO):", " userId:", userId, " upstream:", ngx.var.target) end else ngx.log(ngx.INFO, "指定用户灰度(NO):", " userId:", userId, " upstream:", ngx.var.target) end elseif gray_type == 4 then -- 指定用户版本灰度 if version > 0 then local versions = gray["versions"] version = tostring(version) if contains(version, versions) then ngx.var.target = gray["gray"] ngx.log(ngx.INFO, "指定版本灰度(YES):", " version:", version, " upstream:", ngx.var.target) else ngx.log(ngx.INFO, "指定版本灰度(NO):", " version:", version, " upstream:", ngx.var.target) end else ngx.log(ngx.INFO, "指定版本灰度(NO):", " version:", version, " upstream:", ngx.var.target) end elseif gray_type == 5 then ngx.var.target = gray["gray"] ngx.log(ngx.INFO, "系统全量灰度(YES):", " upstream:", ngx.var.target) endelse local json = cjson.new() ngx.header.content_type = "application/json" ngx.say(cjson.encode({ code = 500, message = '无法找到转发配置,请联系管理员!' })) ngx.log(ngx.ERR, "无法找到系统配信息,返回500")end
转载请注明来自夕逆IT,本文标题:《idmicom重置密码(Openresty灰度发布及版本约束)》
还没有评论,来说两句吧...