redis接口的二次封装
生活随笔
收集整理的這篇文章主要介紹了
redis接口的二次封装
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
先看一下官方的調用示例代碼:
local redis = require "resty.redis" local red = redis:new()red:set_timeout(1000) -- 1 seclocal ok, err = red:connect("127.0.0.1", 6379) if not ok thenngx.say("failed to connect: ", err) return endok, err = red:set("dog", "an animal") if not ok thenngx.say("failed to set dog: ", err) return endngx.say("set result: ", ok)-- put it into the connection pool of size 100, -- with 10 seconds max idle time local ok, err = red:set_keepalive(10000, 100) if not ok thenngx.say("failed to set keepalive: ", err) return end這是一個標準的redis接口調用,如果你的代碼中redis被調用頻率不高,那么這段代碼不會有任何問題。但如果你的項目重度依賴redis,工程中有大量的代碼在重復這樣一個動作,創建連接-->數據操作-->關閉連接(或放到連接池)這個完整的鏈路調用完畢,甚至還要考慮不同的return情況做不同處理,就很快發現代碼中有大量的重復。
Lua是不支持面向對象的。很多人用盡各種招術利用元表來模擬??墒?#xff0c;Lua的發明者似乎不想看到這樣的情形,因為他們把取長度的__len方法以及析構函數__gc留給了 C API。純 Lua 只能望洋興嘆。
我們期望的代碼應該是這樣的:
local red = redis:new() local ok, err = red:set("dog", "an animal") if not ok thenngx.say("failed to set dog: ", err) return endngx.say("set result: ", ok)local res, err = red:get("dog") if not res thenngx.say("failed to get dog: ", err) return endif res == ngx.null thenngx.say("dog not found.") return endngx.say("dog: ", res)并且他自身具備以下幾個特征:
- new、connect函數合體,使用時只負責申請,盡量少關心什么時候具體連接、釋放
- 默認redis數據庫連接地址,但是允許自定義
- 每次redis使用完畢,自動釋放redis連接到連接池供其他請求復用
- 要支持redis的重要優化手段 pipeline
不賣關子,只要干貨,我們最后是這樣干的,可以這里看到gist代碼
-- file name: resty/redis_iresty.lua local redis_c = require "resty.redis"local ok, new_tab = pcall(require, "table.new") if not ok or type(new_tab) ~= "function" thennew_tab = function (narr, nrec) return {} end endlocal _M = new_tab(0, 155) _M._VERSION = '0.01'local commands = {"append", "auth", "bgrewriteaof","bgsave", "bitcount", "bitop","blpop", "brpop","brpoplpush", "client", "config","dbsize","debug", "decr", "decrby","del", "discard", "dump","echo","eval", "exec", "exists","expire", "expireat", "flushall","flushdb", "get", "getbit","getrange", "getset", "hdel","hexists", "hget", "hgetall","hincrby", "hincrbyfloat", "hkeys","hlen","hmget", "hmset", "hscan","hset","hsetnx", "hvals", "incr","incrby", "incrbyfloat", "info","keys","lastsave", "lindex", "linsert","llen", "lpop", "lpush","lpushx", "lrange", "lrem","lset", "ltrim", "mget","migrate","monitor", "move", "mset","msetnx", "multi", "object","persist", "pexpire", "pexpireat","ping", "psetex", "psubscribe","pttl","publish", --[[ "punsubscribe", ]] "pubsub","quit","randomkey", "rename", "renamenx","restore","rpop", "rpoplpush", "rpush","rpushx", "sadd", "save","scan", "scard", "script","sdiff", "sdiffstore","select", "set", "setbit","setex", "setnx", "setrange","shutdown", "sinter", "sinterstore","sismember", "slaveof", "slowlog","smembers", "smove", "sort","spop", "srandmember", "srem","sscan","strlen", --[[ "subscribe", ]] "sunion","sunionstore", "sync", "time","ttl","type", --[[ "unsubscribe", ]] "unwatch","watch", "zadd", "zcard","zcount", "zincrby", "zinterstore","zrange", "zrangebyscore", "zrank","zrem", "zremrangebyrank", "zremrangebyscore","zrevrange", "zrevrangebyscore", "zrevrank","zscan","zscore", "zunionstore", "evalsha" }local mt = { __index = _M }local function is_redis_null( res )if type(res) == "table" thenfor k,v in pairs(res) doif v ~= ngx.null thenreturn falseendendreturn trueelseif res == ngx.null thenreturn trueelseif res == nil thenreturn trueendreturn false end-- change connect address as you need function _M.connect_mod( self, redis )redis:set_timeout(self.timeout)return redis:connect("127.0.0.1", 6379) endfunction _M.set_keepalive_mod( redis )-- put it into the connection pool of size 100, with 60 seconds max idle timereturn redis:set_keepalive(60000, 1000) endfunction _M.init_pipeline( self )self._reqs = {} endfunction _M.commit_pipeline( self )local reqs = self._reqsif nil == reqs or 0 == #reqs thenreturn {}, "no pipeline"elseself._reqs = nilendlocal redis, err = redis_c:new()if not redis thenreturn nil, errendlocal ok, err = self:connect_mod(redis)if not ok thenreturn {}, errendredis:init_pipeline()for _, vals in ipairs(reqs) dolocal fun = redis[vals[1]]table.remove(vals , 1)fun(redis, unpack(vals))endlocal results, err = redis:commit_pipeline()if not results or err thenreturn {}, errendif is_redis_null(results) thenresults = {}ngx.log(ngx.WARN, "is null")end-- table.remove (results , 1)self.set_keepalive_mod(redis)for i,value in ipairs(results) doif is_redis_null(value) thenresults[i] = nilendendreturn results, err endfunction _M.subscribe( self, channel )local redis, err = redis_c:new()if not redis thenreturn nil, errendlocal ok, err = self:connect_mod(redis)if not ok or err thenreturn nil, errendlocal res, err = redis:subscribe(channel)if not res thenreturn nil, errendres, err = redis:read_reply()if not res thenreturn nil, errendredis:unsubscribe(channel)self.set_keepalive_mod(redis)return res, err endlocal function do_command(self, cmd, ... )if self._reqs thentable.insert(self._reqs, {cmd, ...})returnendlocal redis, err = redis_c:new()if not redis thenreturn nil, errendlocal ok, err = self:connect_mod(redis)if not ok or err thenreturn nil, errendlocal fun = redis[cmd]local result, err = fun(redis, ...)if not result or err then-- ngx.log(ngx.ERR, "pipeline result:", result, " err:", err)return nil, errendif is_redis_null(result) thenresult = nilendself.set_keepalive_mod(redis)return result, err endfunction _M.new(self, opts)opts = opts or {}local timeout = (opts.timeout and opts.timeout * 1000) or 1000local db_index= opts.db_index or 0for i = 1, #commands dolocal cmd = commands[i]_M[cmd] =function (self, ...)return do_command(self, cmd, ...)endendreturn setmetatable({timeout = timeout,db_index = db_index,_reqs = nil }, mt) endreturn _M調用示例代碼:
local redis = require "resty.redis_iresty" local red = redis:new()local ok, err = red:set("dog", "an animal") if not ok thenngx.say("failed to set dog: ", err) return endngx.say("set result: ", ok)在最終的示例代碼中看到,所有的連接創建、銷毀連接、連接池部分,都被完美隱藏了,我們只需要業務就可以了。媽媽再也不用擔心我把redis搞垮了。
Todo list:目前resty.redis并沒有對redis 3.0的集群API做支持,既然統一了redis的入口、出口,那么對這個redis_iresty版本做適當調整完善,就可以支持redis 3.0的集群協議。由于我們目前還沒引入redis集群,這里也希望有使用的同學貢獻自己的補丁或文章。
總結
以上是生活随笔為你收集整理的redis接口的二次封装的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lua的require使用
- 下一篇: nginx_lua 学习网站