zoukankan      html  css  js  c++  java
  • openresty开发系列27--openresty中封装redis操作

    openresty开发系列27--openresty中封装redis操作


    在关于web+lua+openresty开发中,项目中会大量操作redis,

    重复创建连接-->数据操作-->关闭连接(或放到连接池)这个完整的链路调用完毕,
    甚至还要考虑不同的 return 情况做不同处理,就很快发现代码中有大量的重复

    推荐一个二次封装的类库
    ---------------------------------
    # 加入openresty的lib库目录
    # vim openresty/lualib/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" then
        new_tab = function (narr, nrec) return {} end
    end

    local _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" then
            for k,v in pairs(res) do
                if v ~= ngx.null then
                    return false
                end
            end
            return true
        elseif res == ngx.null then
            return true
        elseif res == nil then
            return true
        end

        return false
    end

    function _M.close_redis(self, redis)  
        if not redis then  
            return  
        end  
        --释放连接(连接池实现)
        local pool_max_idle_time = self.pool_max_idle_time --最大空闲时间 毫秒  
        local pool_size = self.pool_size --连接池大小  
        
        local ok, err = redis:set_keepalive(pool_max_idle_time, pool_size)  
        if not ok then  
            ngx.say("set keepalive error : ", err)  
        end  
    end  

    -- change connect address as you need
    function _M.connect_mod( self, redis )
        redis:set_timeout(self.timeout)
            
        local ok, err = redis:connect(self.ip, self.port)
        if not ok then  
            ngx.say("connect to redis error : ", err)  
            return self:close_redis(redis)  
        end
        
        if self.password then ----密码认证
            local count, err = redis:get_reused_times()
            if 0 == count then ----新建连接,需要认证密码
                ok, err = redis:auth(self.password)
                if not ok then
                    ngx.say("failed to auth: ", err)
                    return
                end
            elseif err then  ----从连接池中获取连接,无需再次认证密码
                ngx.say("failed to get reused times: ", err)
                return
            end
        end

        return ok,err;
    end

    function _M.init_pipeline( self )
        self._reqs = {}
    end

    function _M.commit_pipeline( self )
        local reqs = self._reqs

        if nil == reqs or 0 == #reqs then
            return {}, "no pipeline"
        else
            self._reqs = nil
        end

        local redis, err = redis_c:new()
        if not redis then
            return nil, err
        end

        local ok, err = self:connect_mod(redis)
        if not ok then
            return {}, err
        end

        redis:init_pipeline()
        for _, vals in ipairs(reqs) do
            local fun = redis[vals[1]]
            table.remove(vals , 1)

            fun(redis, unpack(vals))
        end

        local results, err = redis:commit_pipeline()
        if not results or err then
            return {}, err
        end

        if is_redis_null(results) then
            results = {}
            ngx.log(ngx.WARN, "is null")
        end
        -- table.remove (results , 1)

        --self.set_keepalive_mod(redis)
        self:close_redis(redis)  

        for i,value in ipairs(results) do
            if is_redis_null(value) then
                results[i] = nil
            end
        end

        return results, err
    end


    local function do_command(self, cmd, ... )
        if self._reqs then
            table.insert(self._reqs, {cmd, ...})
            return
        end

        local redis, err = redis_c:new()
        if not redis then
            return nil, err
        end

        local ok, err = self:connect_mod(redis)
        if not ok or err then
            return nil, err
        end

        redis:select(self.db_index)
        
        local 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, err
        end

        if is_redis_null(result) then
            result = nil
        end

        --self.set_keepalive_mod(redis)
        self:close_redis(redis)  

        return result, err
    end

    for i = 1, #commands do
        local cmd = commands[i]
        _M[cmd] =
                function (self, ...)
                    return do_command(self, cmd, ...)
                end
    end

    function _M.new(self, opts)
        opts = opts or {}
        local timeout = (opts.timeout and opts.timeout * 1000) or 1000
        local db_index= opts.db_index or 0
        local ip = opts.ip or '127.0.0.1'
        local port = opts.port or 6379
        local password = opts.password
        local pool_max_idle_time = opts.pool_max_idle_time or 60000
        local pool_size = opts.pool_size or 100

        return setmetatable({
                timeout = timeout,
                db_index = db_index,
                ip = ip,
                port = port,
                password = password,
                pool_max_idle_time = pool_max_idle_time,
                pool_size = pool_size,
                _reqs = nil }, mt)
    end

    return _M

    ---------------------------------------
    调用案例

    local redis = require "resty.redis_iresty"

    local opts = {
        ip = "10.11.0.215",
        port = "6379",
        password = "redis123",
        db_index = 1
    }

    local red = redis:new(opts)

    local ok, err = red:set("dog", "an animal")
    if not ok then
        ngx.say("failed to set dog: ", err)
        return
    end

    ngx.say("set result: ", ok)


    ---------------------------------------
    管道

    redis.set
    redis.get


    red:init_pipeline()
    red:set("cat", "Marry")
    red:set("horse", "Bob")
    red:get("cat")
    red:get("horse")
    local results, err = red:commit_pipeline()
    if not results then
        ngx.say("failed to commit the pipelined requests: ", err)
        return
    end

    for i, res in ipairs(results) do
        ngx.say(res,"<br/>");
    end


    在日常的开发中我们不需要重复造轮子,只要这个轮子 稳定,高效就可以直接拿过来用

  • 相关阅读:
    在win7系统下安装把Ubuntu17.04安装在另一个硬盘开机无法进入Ubuntu问题的一种解决办法。【转】
    快速上手Ubuntu之安装篇——安装win7,Ubuntu16.04双系统【转】
    win7 64位系统与Ubuntu14.04 64位系统双系统安装【转】
    kernel中对文件的读写【学习笔记】【原创】
    快速解决Android中的selinux权限问题【转】
    Android如何配置init.rc中的开机启动进程(service)【转】
    linux内核驱动中对文件的读写 【转】
    很好的 DHCP协议与dhcpcd分析【转】
    android DHCP流程【转】
    Android wifi 从连接态自动断开的解决办法(dhcp导致)【转】
  • 原文地址:https://www.cnblogs.com/reblue520/p/11434632.html
Copyright © 2011-2022 走看看