一种对ngx_lua请求进行开关控制的实现

        为了提高性能,用OpenResty(nginx+lua)开发了一个轻量Web应用。主用用于提供统一高性能查询接口(统一走该轻应用域名)和极大的减少浏览器的http连接数量(使用ngx.location.capture_multi )。

        为了更进一步进行限流与控制管理,就需要对该应用的请求及请求内部子接口加上开关控制。笔者接手该需求时,项目已经存在,并且之前并没有接触过Lua,所以只能一边百度一边尝试了(事实证明,Lua真的非常容易上手)。

        开关要求是放在统一的配置平台上,其他应用获取开关配置是通过引入配置平台提供的jar包然后调用RPC服务实现的。所以用轻应用直接获取配置平台的开关是实现不了了╰(〒皿〒)╯。后来妥协下就在后台Java应用中加了个http接口,通过接口去取配置平台的数据。但是如果每次访问轻应用都要访问http接口获取配置,我估计我到不了双十一就会被杀了祭天(°ཀ°)。所以为了活命,就想到了是否可以本地缓存配置呢?是滴,可以!百度说 nginx+lua可以共享内存。只需要开辟一个共享内存块,然后我去配置平台上获取数据放置到共享内存中,然后我就不用每次去拿配置了。Ok,思路差不多有了,剩下的就是百度一下,细节完善了。。。

         说了一堆姑且称之为背景的废话,接下来就是 show me the code! 

1.Java端的提供的查询接口:

根据名称获取开关值,如果开关名称是 allSwitch就获取所有开关值

@RequestMapping("/getSwitchValue")
    public String getSwitchValue(HttpServletResponse response,
                                 @RequestParam(value = "switchName", required = true) String switchName){
        //返回结果对象
        AjaxResultVo result = new AjaxResultVo();
        //开关结果:key-开关名称、Value-开关值
        Map<String,Object > switchResult = new HashMap<String, Object>(16);
        //开关名称为空,直接返回报错
        if (StringUtils.isEmpty(switchName)) {
            result.setReturnCode(ApiConstantsUtil.COMMON_ERR_CODE);
            result.setReturnMsg(ApiConstantsUtil.COMMON_ERR_MSG);
            return ajaxJson(response, gson.toJson(result));
        }
        try {
            if (StringUtils.equals(ALL_SWTICHT, switchName)) {
                switchResult = ScmPropertiesUtils.getAllConfigSwitch();
            } else {//根据开关名称获取开关值
                switchResult.put(switchName, ScmPropertiesUtils.getConfigSwitch(switchName));
            }
        } catch (Exception e) {
            LOGGER.logException(e);
            LOGGER.warn("query Lua Swtich Error!");
            result.setReturnCode(ApiConstantsUtil.COMMON_ERR_CODE);
            result.setReturnMsg(ApiConstantsUtil.COMMON_ERR_MSG);
            return ajaxJson(response, gson.toJson(result));
        }
        // 开关值放入结果中
        result.setReturnMap(switchResult);
        return ajaxJson(response, gson.toJson(result));
    }

获取的开关结果示例(实际内容为各模块名称与开关值):

2. 准备工作完成了,现在开撸Lua端吧

首先开启ngx-lua的共享内存。

这一步很简单就是改下nginx的nginx.conf配置,添加一条 lua_shared_dict switchCache 1m;

第二步是自定义一个函数库,将获取与重置缓存的方法放入库中。注意该文件要放置在Lua的库路径下,或者添加一个Lua库路径用于放置该文件。想看Lua的require机制的看这个 http://blog.chinaunix.net/uid-552961-id-2736410.html

文件commonFunctions.lua

--[[
	该文件用于放置一些自定义的常用函数
]]--

local _M = {_VERSION = "0.1"}

cjson = require("cjson");
--[[
	重新加载缓存
]]--
function reloadSwitchCache(cacheTime)
	-- 请求参数
	local reqBaseUrl="/lua/getSwitchValue.do?switchName=allSwitch";
	-- 发请求获取开关值,
	local resSwitchValue = ngx.location.capture(reqBaseUrl) 
	--[[
		请求成功,获取开关值,重置缓存
	]]--
	if resSwitchValue and resSwitchValue.status ==  ngx.HTTP_OK then
		local jsonSwitchValue = cjson.decode(resSwitchValue.body);
		-- 返回编码为成功
		if jsonSwitchValue.returnCode =="0" then
			for key, value in pairs(jsonSwitchValue.returnMap) do  
				ngx.shared.switchCache:set(key,value,cacheTime)  
			end 
		end
	end	
end


--[[
	根据开关名称获取开关值
]]--
function _M.getSwitchValue(switchName)
	-- 从缓存中获取开关值
    local switchValue = ngx.shared.switchCache:get(switchName)
	if switchValue == nil or switchValue =="" then	
		-- 缓存时间默认60秒
		local cacheTime = 60
		-- 缓存重载
		reloadSwitchCache(cacheTime)
		switchValue = ngx.shared.switchCache:get(switchName)
	end
	return switchValue
end



return _M

第三具体模块的使用。下面的示例演示了order复合接口的开关控制演示

gcjson = require("cjson");
commonFunction = require("commonFunction")
stringUtils = require("stringUtils")
local requestmap= ngx.req.get_uri_args();
ngx.header["Cache-Control"] = "no-cache,no-store,max-age=0";
-- 最终返回值数据集、默认成功
local data ={};
data["nowDate"]=os.date("%Y-%m-%d %H:%M:%S",os.time());
data["status"]="success"
data["code"]="0"
-- 默认开关值
local orderModuleSwitch = {"1","1","1","1","1","1"};
--获取开关
local switchValue = commonFunction.getSwitchValue("orderModuleSwitch")
if switchValue and switchValue ~="" then
	orderModuleSwitch =stringUtils.splitString(switchValue,",")
end
-- 第一字符开关表示总开关,总开关关闭,直接返回
if orderModuleSwitch[1] == "0" then
	data["status"]="failure"
	data["code"]="switch_off"
	data["msg"]="main Switch off"
	ngx.say(gcjson.encode(data));
	do return end;
end
-- 请求参数数组
local requestArray;

-- 1.待收货数量接口
local reqQueryOrderCount="/test/test/test/queryOrderCount.do";
-- 2.待支付接口
local reqQueryOrderList="/test/test/test/queryOrderList.do";
-- 3.待评价数量
local reqMyEbuyReviews="/test/test.do";
-- 4.常购清单降价
local reqQueryRecommendPriceLabel="/test/test/test/queryRecommendPriceLabel.do";
-- 5.预约
local reqMyAppointment="/test/test/test/myappointYG.do?terminal=2";


-- 群发请求url数组,根据开关动态插入内容
local requestArray ={};
if orderModuleSwitch[2] and orderModuleSwitch[2] == "1" then
	table.insert(requestArray,{reqQueryOrderCount});
end
if orderModuleSwitch[3] and orderModuleSwitch[3] == "1" then
	table.insert(requestArray,{reqQueryOrderList});
end
if orderModuleSwitch[4] and orderModuleSwitch[4] == "1" then
	table.insert(requestArray,{reqMyEbuyReviews});
end
if orderModuleSwitch[5] and orderModuleSwitch[5] == "1" then
	table.insert(requestArray,{reqQueryRecommendPriceLabel});
end
if orderModuleSwitch[6] and orderModuleSwitch[6] == "1" then
	table.insert(requestArray,{reqMyAppointment});
end

local resps ={}
-- 有请求才发送
if #requestArray >0 then
	resps = { ngx.location.capture_multi(requestArray) }
end

-- 定义请求返回变量(如果开关未开,就不会使用)
local resQueryOrderCount,resQueryOrderList,resMyEbuyReviews,resQueryRecommendPriceLabel,resMyAppointment,resReplacement;
-- lua语言中table的下标是从1开始的,第一个开关是总控,从第二个遍历
local curIndex = 1;
if orderModuleSwitch[2] == "1" then
	resQueryOrderCount = resps[curIndex];
	curIndex = curIndex + 1;
end
if orderModuleSwitch[3] == "1" then
	resQueryOrderList = resps[curIndex];
	curIndex = curIndex + 1;
end
if orderModuleSwitch[4] == "1" then
	resMyEbuyReviews = resps[curIndex];
	curIndex = curIndex + 1;
end
if orderModuleSwitch[5] == "1" then
	resQueryRecommendPriceLabel = resps[curIndex];
	curIndex = curIndex + 1;
end
if orderModuleSwitch[6] == "1" and myAppointmentSwicth == "1" then
	resMyAppointment = resps[curIndex];
	curIndex = curIndex + 1;
end

--[[
	请求成功,封装返回数据
]]--
if resQueryOrderCount and resQueryOrderCount.status ==  ngx.HTTP_OK then
 data["queryOrderCount"]=resQueryOrderCount.body;
end;
if resQueryOrderList and resQueryOrderList.status ==  ngx.HTTP_OK then
 data["queryOrderList"]=resQueryOrderList.body;
end;
if resMyEbuyReviews and resMyEbuyReviews.status ==  ngx.HTTP_OK then
 data["myEbuyReviews"]=resMyEbuyReviews.body;
end;
if resQueryRecommendPriceLabel and resQueryRecommendPriceLabel.status ==  ngx.HTTP_OK then
 data["queryRecommendPriceLabel"]=resQueryRecommendPriceLabel.body;
end;
if resMyAppointment and resMyAppointment.status ==  ngx.HTTP_OK then
 data["myAppointment"]=resMyAppointment.body;
end;


-- 返回
ngx.say(gcjson.encode(data));

调用该模块时,关闭后两个开关接口时的结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

单剑撩花猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值