openresty 学习笔记三:连接redis和进行相关操作

openresty 因其非阻塞的调用,令服务器拥有高性能高并发,当涉及到数据库操作时,更应该选择有高速读写速度的redis进行数据处理。避免其应为读写数据而造成瓶颈。

openresty 默认就带了redis的库,这里先梳理下其自带redis连接库的操作流程,再根据存在问题进行二次封装。

自带redis连接库的操作流程

首先是连接redis

local redis = require "resty.redis"
local redisCache = redis.new()
local ok ,err = redisCache.connect(redisCache,127.0.0.1,6379)
redisCache:set_timeout(1000) if not ok then
ngx.log(ngx.ERR, "failed to connect: ", err)
return
end

  

简单的reids操作

local code , err = redisCache:get("code")
local res , err = redisCache:incr("code")
local ok , err = redisCache:set("code",1)

  

然后操作后要进行close

ok , err = redisCache:close()

  

如果项目中有大量redis操作,使用这个标准库就变成会有大量的代码在重复创建连接-->数据操作-->关闭连接(或放到连接池),甚至还要考虑不同的 return 情况做不同处理。

redis 库的二次封装

针对以上问题,也通过学习openresty的最佳实践,直接用上其中已经做好的二次封装。这个封装是要实现一下目的

  1. new、connect 函数合体,使用时只负责申请,尽量少关心什么时候具体连接、释放;
  2. 默认 redis 数据库连接地址,但是允许自定义;
  3. 每次 redis 使用完毕,自动释放 redis 连接到连接池供其他请求复用;
  4. 要支持 redis 的重要优化手段 pipeline;

我只理解了前三点,最后的pipeline也还不是很懂,先贴上代码:

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 -- change connect address as you need
function _M.connect_mod( self, redis )
redis:set_timeout(self.timeout)
return redis:connect(self.db_host, self.db_port)
end function _M.set_keepalive_mod( redis )
-- put it into the connection pool of size 100, with 60 seconds max idle time
return redis:set_keepalive(60000, 1000)
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) for i,value in ipairs(results) do
if is_redis_null(value) then
results[i] = nil
end
end return results, err
end function _M.subscribe( self, channel )
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 local res, err = redis:subscribe(channel)
if not res then
return nil, err
end local function do_read_func ( do_read )
if do_read == nil or do_read == true then
res, err = redis:read_reply()
if not res then
return nil, err
end
return res
end redis:unsubscribe(channel)
self.set_keepalive_mod(redis)
return
end return do_read_func
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 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) return result, err
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 db_host= opts.host or '127.0.0.1'
local db_port= opts.port or 6379 for i = 1, #commands do
local cmd = commands[i]
_M[cmd] =
function (self, ...)
return do_command(self, cmd, ...)
end
end return setmetatable({
timeout = timeout,
db_index = db_index,
db_host = db_host,
db_port = db_port,
_reqs = nil }, mt)
end return _M

  

这个二次封装除了解决上面几个问题,其实还重写了两个方法,不过订阅和发布暂时没用到,所以还没去细看这一部分。

使用示例

_config.redisConfig = {
timeout = 1000,
host = '127.0.0.1',
port = 6379,
} local redis = require (ngx.var.SERVER_DIR .. ".common.myRedis")
local redisCache = redis:new(config.redisConfig) local accessDetails , err = redisCache:get("code")

  

甚至可以直接

local redisCache = redis:new()

openresty 学习笔记三:连接redis和进行相关操作的更多相关文章

  1. openresty 学习笔记四:连接mysql和进行相关操作

    openresty 学习笔记四:连接mysql和进行相关操作 毕竟redis是作为缓存,供程序的快速读写,虽然reidis也可以做持久化保存,但还是需要一个做数据存储的数据库.比如首次查询数据在red ...

  2. C# LINQ学习笔记三:LINQ to OBJECT之操作字符串

    本笔记摘抄自:https://www.cnblogs.com/liqingwen/p/5814204.html,记录一下学习过程以备后续查用. 一.统计单词在字符串中出现的次数 请注意,若要执行计数, ...

  3. openresty 学习笔记一:环境安装

    openresty 学习笔记一:环境安装 openresty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库.第三方模块以及大多数的依赖项.用于方便地搭 ...

  4. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  5. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  6. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

  7. VSTO学习笔记(三) 开发Office 2010 64位COM加载项

    原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...

  8. 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记

    回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...

  9. kvm虚拟化学习笔记(三)之windows kvm虚拟机安装

    KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51 ...

随机推荐

  1. 代码安全丨第二期:URL重定向(跳转)漏洞

    URL重定向: URL重定向(URLredirection)漏洞,又称跳转漏洞,指的是网络应用程序接受用户可控的输入作为到外部站点的链接,然后在重定向中使用该链接.该安全漏洞给网络钓鱼攻击提供了极大的 ...

  2. PAT归纳总结——关于图的一些总结

    在刷题的过程中经常会碰到一些关于图论的问题,下面我将根据自己刷题的经验来对这些问题做一个总结. 图的表示方法 解决图论中的问题首先要解决的问题就是图的表示方法这一问题,图的表示方法主要有两种,一种使用 ...

  3. 4- MySQL创建表以及增删改查

    查看表结构 查看表的结构,使用命令:desc 表明: 创建表(命令) 格式:使用create table创建表,必须给出下列信息: 1.新表的名字. 2.表中列的名字和定义,用逗号隔开. 语法: cr ...

  4. jQuery监控键盘事件

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  5. 【beego】beego的路由设置

    beego 存在三种方式的路由:固定路由.正则路由.自动路由 基础路由 从 beego 1.2 版本开始支持了基本的 RESTful 函数式路由,应用中的大多数路由都会定义在 routers/rout ...

  6. node-mongo-服务器封装

    分为三个文件 mongo.js基本的封装了下mongo数据库操作 workmongo.js 里面有路由和解析操作(可以根据具体业务进行扩充) mainmongo.js 服务器相关 调用例子: 查询数据 ...

  7. CTF密码学常见加解密总结

    CTF密码学常见加解密总结 2018年03月10日 19:35:06 adversity` 本文链接:https://blog.csdn.net/qq_40836553/article/details ...

  8. JetBrains系列软件用法

    IDEA JSON格式化 IDEA的JSON_Formatter插件,下载地址 安装方式:File->Settings->Plugins,然后选择INstall plugin from d ...

  9. 前端Excel表格导入导出,包括合并单元格,表格自定义样式等

    表格数据导入 读取导入Excel表格数据这里采用的是 xlsx 插件 npm i xlsx 读取excel需要通过 XLSX.read(data, {type: type}) 方法来实现,返回一个叫W ...

  10. linux 查看运行java所在目录

    通过ps及top命令查看进程信息时,只能查到相对路径,查不到的进程的详细信息 需要查看pos_service.jar的绝对路径(在哪里目录下)  使用:ll /proc/PID Linux在启动一个进 ...