用lua+redis实现一个简单的计数器功能 (二)
环境已经搭建完毕 传送门
- 计数方案
就目前来看nginx是最快的服务
我在设计方案时选择信任redis作为存储库,不做穿透处理,由于目前redis集群方案还不成熟,只在这里做了主备方案。想做集群方案的人可以考虑使用twemproxy
--如采用twemproxy 集群方案 不要选择信任redis集群,最好有穿透机制 一旦某机器当机,恢复会很麻烦
- 程序部分
为方便管理lua文件,修改nginx.conf并重启
lua_package_path '/var/www/lib/?.lua'; lua_package_cpath '/usr/local/nginx/so/?.so'; #加载动态库 init_by_lua_file '/usr/local/nginx/lua/init.lua';#将配置文件加载到nginx内存中 lua_shared_dict config 45m;//配置内存大小
include site/*.conf;
新建site/lua.conf
server { listen 80; server_name count.xxxxxx.com; location ^~/user_group_praise/ {
access_log off;
lua_code_cache off;
content_by_lua_file /usr/local/nginx/lua/count.lua;
} }
init.lua代码让配置文件常驻内存
local cjson = require "cjson"; local config = ngx.shared.config; local file = io.open("/usr/local/nginx/lua/count.cjson", "r"); local content = cjson.decode(file:read("*all")); file:close(); for name, value in pairs(content) do config:set(name, cjson.encode(value)); end
count.cjson
{ "user_group_praise": { "redis_host": "127.0.0.1", "redis_port": 6379, "cv_key": "user_praise", "key": [ "uid" ], "count_name": "用户直通赞数" } }
count.lua代码
ngx.header.content_type = "text/plain;charset=utf-8" local request_method = ngx.var.request_method local args = nil if "GET" == request_method then args = ngx.req.get_uri_args() elseif "POST" == request_method then ngx.req.read_body() args = ngx.req.get_post_args() end local uri = ngx.var.uri; uri = string.sub(uri,2,#uri); local uripos = string.find(uri , '/'); if(uripos == null) then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' 参数错误}'); ngx.exit(200); end local type = string.sub(uri , 1 ,uripos-1); local functionname = string.sub(uri , uripos+1 ,#uri); local cjson = require "cjson"; local config = ngx.shared.config; local conf_tab = config:get(type); if(conf_tab == null) then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' 无此计数类型}'); ngx.exit(200); end conf_tab = cjson.decode(conf_tab); local param_key_all = ''; --检测参数 if(functionname ~= 'get') then param_key_all = param_key_all..conf_tab['cv_key']; for key, val in pairs(conf_tab['key']) do if(args[val] == null) then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' 参数错误}'); ngx.exit(200); end param_key_all = param_key_all..':'..args[val]; end else if(args['json'] == null) then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' 参数错误}'); ngx.exit(200); end local param_tab = cjson.decode(args['json']); if(param_tab == null) then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' 参数错误}'); ngx.exit(200); end for key, val in pairs(param_tab) do param_key_all = param_key_all..','.."'"..conf_tab['cv_key']; for i=1,#conf_tab['key'],1 do if(val[conf_tab['key'][i]] == null) then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' 参数错误}'); ngx.exit(200); end param_key_all = param_key_all..':'..val[conf_tab['key'][i]]; end param_key_all = param_key_all.."'"; end param_key_all = string.sub(param_key_all,2,#param_key_all); end local redis = require("resty.redis"); local red = redis:new(); red:set_timeout(1000); -- 1 sec local ok, err = red:connect(conf_tab['redis_host'] , conf_tab['redis_port'] ); if not ok then ngx.say('{"errorCode":500,"errorMsg":"'..conf_tab['count_name']..' REDIS服务器连接错误}') ngx.exit(200); end if(functionname == 'get') then local cvval = red:eval("return redis.call('mget',"..param_key_all..")",0); local results = {}; results['data'] = cvval; results['errorCode'] = 0; results['errorMsg'] = 'ok'; ngx.say(cjson.encode(results)); elseif(functionname == 'inc') then cnum=red:incrby(param_key_all,1) ngx.say('{"errorCode":0,"errorMsg":"ok","data":'..cnum..'}') elseif(functionname == 'dec') then cnum=red:incrby(param_key_all,-1); ngx.say('{"errorCode":0,"errorMsg":"ok","data":'..cnum..'}') elseif(functionname == 'clear') then cnum=red:set(param_key_all,0); ngx.say('{"errorCode":0,"errorMsg":"ok","data":0}') else ngx.say('参数错误'); end red:set_keepalive(0, 100);