說明:openresty可以理解為一個服務器它將nginx的核心包含了過來,並結合lua腳本語言實現一些對性能要求高的功能,該篇文章介紹了使用openresty
1.purview.lua
--調用json公共組件 cjson = require("cjson") fun = require("ttq.fun") -- 引用公用方法文件 conf = require("ttq.ini") --引用配置文件 reds = require("ttq.redis_pool") --引用redis連接池 mysqld = require("ttq.mysql_pool") --引用mysql連接池 --參數校驗 check_arg = fun:check_post_arg() --調用參數校驗方法 arg_tables = {} --存儲post的參數信息 if check_arg['status'] ==0 then --參數校驗通過,獲取返回的參數,並將參數拼接 arg_tables= check_arg['arg_tables'] get_info = string.format("%s:%s:%s",arg_tables['appid'],arg_tables['ip'],arg_tables['appkey']) else ngx.say(fun:resJson(-1,check_arg['msg'])) return; end --1.首先通過redis查找 --2.沒有找到再找數據庫 --3.根據appid查詢項目是否授權 --4.項目獲取權限成功,再查詢ip是否被限制了 local res,err,value = reds:get_key(get_info) if not res then ngx.say(fun:resJson(-1,err)) return end if value == ngx.null then --redis數據未空,根據appid查詢,查詢信息是否一致 local sql_appid = string.format("select * from ttq_appid_list where appid= '%s' and appkey='%s' limit 1 ",arg_tables['appid'],arg_tables['appkey']) local res,msg,result = mysqld:query(sql_appid) --連接失敗報錯 if not res then ngx.say(fun:resJson(-1,msg)) end --未查找數據報錯 if table.maxn(result)== 0 then ngx.say(fun:resJson(-1,'appid驗證失敗,被攔截')) return end --項目權限獲取成功,需要驗證ip是否被允許 local sql = string.format("select * from ttq_appid_white_list where appid='%s' and ip= '%s' limit 1 ",arg_tables['appid'],arg_tables['ip']) res,msg,result = mysqld:query(sql) if table.maxn(result)==0 then ngx.say(fun:resJson(-1,'該項目,非法操作或沒有授予權限,被攔截')) return end --所有驗證通過,最后寫入redis緩存 ok, err = reds:set_key(get_info,1) ngx.say(fun:resJson(0,'該項目鑒權成功,可以訪問')); return end --3.redis找到了信息鑒權成功 ngx.say(fun:resJson(0,"該項目鑒權成功,可以訪問!"))
2.ini.lua
--配置相關方法 local _CONF = {} --返回redis配置文件 function _CONF.redis() local redis_config = {host='127.0.0.1',pass='123456',port=6379} --redis配置項 return redis_config end --返回mysql配置文件 function _CONF.mysql() local mysql_config = {host='127.0.0.1',port=3306,database='test',user='root',password='123456'} --mysql的配置項 return mysql_config end return _CONF
3.mysql_pool.lua
--連接mysql local mysql = require "resty.mysql" local mysql_pool = {} function mysql_pool:get_connect() if ngx.ctx[mysql_pool] then return true,'返回mysql連接池成功',ngx.ctx[mysql_pool] end local db, err_mysql = mysql:new() if not db then return false,"failed to instantiate mysql" end db:set_timeout(1000) -- 1 sec local ok, err_mysql, errno, sqlstate = db:connect{ host = conf.mysql()['host'], port = conf.mysql()['port'], database = conf.mysql()['database'], user = conf.mysql()['user'], password = conf.mysql()['password'], max_packet_size = 1024 * 1024 } if not ok then --ngx.say(fun.resJson(-1,"mysql connect failed")) return false,"mysql conncet failed" end --存儲mysql連接池並返回 ngx.ctx[mysql_pool] = db return true,'mysql連接成功',ngx.ctx[mysql_pool] end --關閉mysql連接池 function mysql_pool:close() if ngx.ctx[mysql_pool] then ngx.ctx[mysql_pool]:set_keepalive(60000, 1000) ngx.ctx[mysql_pool] = nil end end --執行sql查詢 function mysql_pool:query(sql) --ngx.say(sql) local ret,msg,client = self:get_connect() --連接數據庫失敗,返回錯誤信息 if not ret then return false,msg end --連接成功后執行sql查詢,執行失敗返回錯誤信息 local res,errmsg,errno,sqlstate = client:query(sql) --self:close() if not res then return false,errmsg end --ngx.say(res[1]['appid']) --ngx.say(res[1]['ip']) --執行成功,返回信息 return true,"查詢信息成功",res end return mysql_pool
4.redis_pool.lua
local redis = require("resty.redis") local redis_pool = {} --連接redis function redis_pool:get_connect() if ngx.ctx[redis_pool] then return true,"redis連接成功",ngx.ctx[redis_pool] end local red = redis:new() red:set_timeout(1000) -- 1 sec local ok, err = red:connect(conf.redis()['host'],conf.redis()['port']) if not ok then return false,"failed to connect redis" end --設置redis密碼 local count, err = red:get_reused_times() if 0 == count then ok, err = red:auth(conf.redis()['pass']) if not ok then return false,"redis failed to auth" end elseif err then return false,"redis failed to get reused times" end --選擇redis數據庫 ok, err = red:select(0) if not ok then return false,"redis connect failed " end --建立redis連接池 ngx.ctx[redis_pool] = red return true,'redis連接成功',ngx.ctx[redis_pool] end --關閉連接池 function redis_pool:close() if ngx.ctx[redis_pool] then ngx.ctx[redis_pool]:set_keepalive(60000, 300) ngx.ctx[redis_pool] = nil end end ---獲取key的值 function redis_pool:get_key(str) local res,err,client = self:get_connect() if not res then return false,err end local keys = client:get(str) --self:close() return true,"獲取key成功",keys end --設置key的值 function redis_pool:set_key(str,value) local res,err,client = self:get_connect() if not res then return false,err end client:set(str,value) --self:close() return true,"成功設置key" end return redis_pool
5.fun.lua
local _M = {} --返回json信息公用方法 function _M:resJson(status,mes) local arr_return = {} arr_return['status'] = status arr_return['msg'] = mes return cjson.encode(arr_return) end --檢測post過來的參數合法性 function _M:check_post_arg() local rule_count = 3 --接收POST過來的數據 ngx.req.read_body() local arg = ngx.req.get_post_args() local arg_count = 0 --存儲參數個數 local arg_table = {appid,ip,appkey} local get_info --參數拼接字符串,方便redis操作 --遍歷post過來的參數 for k,v in pairs(arg) do arg_count = arg_count+1 arg_table[k] = v end --參數賦值 appid = arg_table['appid'] ip = arg_table['ip'] appkey = arg_table['appkey'] --判斷參數個數傳遞過來的參數要與規定的個數一致 if rule_count == arg_count then if string.len(appid) == 0 then return {status=-1,msg='參數傳遞錯誤,被攔截'} end if string.len(ip) == 0 then return {status=-1,msg='參數傳遞錯誤,被攔截'} end if string.len(appkey) == 0 then return {status=-1,msg='參數傳遞錯誤,被攔截'} end ---參數正確返回參數信息 return {status=0,msg='參數校驗成功',arg_tables=arg_table} else return {status=-1,msg='參數傳遞錯誤,被攔截'} end end return _M
6.配置nginx.conf文件
上面的lua文件都是放在/data/local/openresty/lualib/ttq/目錄下
location /lua{
lua_code_cache on;
content_by_lua_file /data/local/openresty/lualib/ttq/purview.lua;
}
7.mysql數據設計
SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `ttq_appid_list`; CREATE TABLE `ttq_appid_list` ( `id` int(4) unsigned NOT NULL AUTO_INCREMENT, `appid` varchar(20) NOT NULL DEFAULT 'appid相當於項目名稱', `appkey` varchar(20) NOT NULL COMMENT 'appid密碼', `create_time` int(11) NOT NULL COMMENT '生產appid時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='項目appid對應關系表'; DROP TABLE IF EXISTS `ttq_appid_white_list`; CREATE TABLE `ttq_appid_white_list` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `appid` char(20) NOT NULL COMMENT '項目標示或名稱', `ip` varchar(15) NOT NULL COMMENT '項目允許訪問對應的ip地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB
8.訪問方法
<form action="http://192.168.3.128:8083/lua" method="post">
<input type="text" name="appid" value='ttq'></input>
<input type="text" name="ip" value='192.168.3.2'></input>
<input type="text" name="appkey" value='67872'></input>
<input type="submit" value="鑒權check"></input>
</form>
9.壓力測試
壓力測試效果非常可觀
qps可以達到2225
代碼下載:
我的github代碼地址:https://github.com/lisiqiong/learning/tree/master/luacode