利用 Lua 的函数式编程简化 lua-resty-redis 的使用

VerleneFass 3年前
   <p>在利用 OpenResty 编写高性能服务的时候,很有可能会使用到 Redis。在 OpenResty 中,我们通常使用 lua-resty-redis 这个模块操作 Redis。</p>    <p>在 lua-resty-redis 的 <strong>示例</strong> 中,我们可以总结出以下几个步骤:</p>    <ul>     <li> <p>导入 resty.redis 模块</p> </li>     <li> <p>实例化 redis 对象:</p> </li>    </ul>    <pre>  <code class="language-lua">local red = redis:new()  red:set_timeout(1000)</code></pre>    <ul>     <li> <p>连接到服务器:</p> </li>    </ul>    <pre>  <code class="language-lua">local ok, err = red:connect("127.0.0.1", 6379)</code></pre>    <ul>     <li> <p>操作 Redis</p> </li>     <li> <p>将连接添加到连接池中</p> </li>    </ul>    <pre>  <code class="language-lua">local ok, err = red:set_keepalive(1000, 100)</code></pre>    <p>以上的几个步骤是标准的使用方式,我们需要在每个使用的地方重复上面的几个步骤。不知不觉,Lua 代码会变的庞大起来。</p>    <p>通过上面的步骤,我们不难发现: 除了 Redis 操作不一样,从实例化 Redis 到最后的连接资源管理都是一样的。这时候,我们就可以使用通用的处理流去标准化它了!</p>    <p>在OpenResty最佳实践中有章节介绍了对 lua-resty-redis 的第二次简化封装跟需要注意的事项。但是,我还是利用了 Lua 支持函数式编程的特性做了一个更加简化的版本:</p>    <pre>  <code class="language-lua">local redis = require "resty/redis"    local log = ngx.log  local ERR = ngx.ERR  local setmetatable = setmetatable    local _M = {      _VERSION = "1.0.0",      _AUTHOR = "Andy Ai"  }    local mt = { __index = _M }    local function errlog(...)      log(ERR, "Redis: ", ...)  end    function _M.exec(self, func)        local red = redis:new()      red:set_timeout(self.timeout)        local ok, err = red:connect(self.host, self.port)      if not ok then          errlog("Cannot connect, host: " .. self.host .. ", port: " .. self.port)          return nil, err      end        red:select(self.database)        local res, err = func(red)      if res then          local ok, err = red:set_keepalive(self.max_idle_time, self.pool_size)          if not ok then              red:close()          end      end      return res, err  end    function _M.new(opts)      local config = opts or {}      local self = {          host = config.host or "127.0.0.1",          port = config.port or 6379,          timeout = config.timeout or 5000,          database = config.database or 0,          max_idle_time = config.max_idle_time or 60000,          pool_size = config.pool_size or 100      }      return setmetatable(self, mt)  end    return _M</code></pre>    <p>从上面的代码中可以看出:</p>    <p>我们将 Redis 的操作看成一个函数,在这个函数执行之前进行实例化跟连接;在函数执行之后进行连接资源的管理。</p>    <p>而,在实际的使用过程中也变得非常简单:</p>    <pre>  <code class="language-lua">local redis = require("resty.rediscli")    local red = redis.new({host = "127.0.0.1"})  local res, err = red:exec(      function(red)          return red:get("123456")      end  )</code></pre>    <p>使用 Redis 的 Pipeline 也同样很简单:</p>    <pre>  <code class="language-lua">red:exec(      function(red)          red:init_pipeline()          red:set(key, value)          red:expire(key, expires)          return red:commit_pipeline()      end  )</code></pre>    <p>怎么样? 我们使用 Lua 支持函数编程的特性恰到好处地将代码很好地简化了。这样,不管是我们在实现逻辑,还是在维护逻辑都可以把精力更多地放在业务实现上,而不是处理这些琐碎资源管理。</p>    <p>上面的代码可以在 GitHub 上获取: lua-resty-utils 。另外,不需要担心它的稳定性!它已经被使用在 GrowingIO 的几个非常重要的服务上!</p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000007207616</p>    <p> </p>