OpenResty实战系列 | Redis协程网络库 lua-resty-redis

2024-07-30 16:10:52 浏览数 (2)

简介

lua-resty-redis 是由著名OpenResty社区成员Agent Zhang(章亦春)创建的。这是一个与OpenResty集成的Lua模块,允许你在Nginx环境中直接进行Redis操作。利用OpenResty的强大功能,lua-resty-redis提供了异步非阻塞的Redis API,帮助开发者构建高性能、高并发的应用。

lua-resty-redis的核心在于其非阻塞I/O模型。它基于OpenResty的ngx.socket.tcp()接口实现,利用了LuaJIT的高效性能和Nginx事件循环机制。这意味着在处理大量并发请求时,即使Redis服务器繁忙,也不会导致Nginx的工作线程被阻塞,从而提高了整体系统的响应速度。

此外,该库提供了丰富的Redis命令支持,包括但不限于数据读写、哈希操作、集合操作、有序集合、发布订阅等。它的API清晰简洁,易于理解和使用,使得你可以快速地将Redis功能融入到你的OpenResty应用程序中。

应用场景

  • 缓存管理:利用Redis的高速读写能力,可以作为动态内容的高速缓存层,降低对后端数据库的压力。
  • 分布式会话:借助lua-resty-redis,可以在多台服务器之间共享用户的会话状态。
  • 消息队列:通过其发布的订阅功能,可以构建简单的消息队列系统,实现任务的异步处理。
  • 实时数据分析:利用Redis的数据结构,如计数器、集合等,进行实时统计和分析。

主要特点

  • 非阻塞I/O - 基于OpenResty的异步TCP套接字接口,确保高并发场景下的效率。
  • Redis命令支持 - 提供了Redis的所有主要命令,方便你执行各种操作。
  • 错误处理 - 提供了良好的错误处理机制,便于定位和解决问题。
  • 可扩展性 - 可以自定义连接池策略,适应不同规模和需求的系统。
  • 简洁API - 易于理解和使用的API,加速开发过程。

安装

这里通过OPM工具包安装,更多请查看OpenResty实战系列 | 包管理工具OPM和LuaRocks

代码语言:javascript复制
opm get openresty/lua-resty-redis

版本信息

代码语言:javascript复制
# opm info openresty/lua-resty-redis
Name             : lua-resty-redis
Version          : 0.27
Abstract         : Lua redis client driver for the ngx_lua based on the cosocket API
Author           : Yichun "agentzh" Zhang (agentzh)
Account          : openresty
Code Repo        : https://github.com/openresty/lua-resty-redis
License          : BSD 2-Clause "Simplified" or "FreeBSD" license
Original Work    : yes

基础使用

使用 lua-resty-redis 设置和获取一个键值对基本示例redis_test_01.lua文件

代码语言:javascript复制
--[[-----------------------------------------------------------------------      
* |  Copyright (C) Shaobo Wan (Tinywan) 
* |------------------------------------------------------------------------
--]]

-- redis config
local redis = require "resty.redis"
local red = redis:new()

red:set_timeouts(1000, 1000, 1000) -- 1 秒

-- 通过宿主机链接
local ok, err = red:connect("192.168.13.168", 6379)

if not ok then
    ngx.say("[x] failed to connect: ", err)
    return
end

-- 设置权限
local res, err = red:auth("123456")
if not res then
    ngx.say("[x] failed to authenticate: ", err)
    return
end

-- 设置权限
-- 请注意这里 auth 的调用过程
local count, err = red:get_reused_times()
ngx.say("[x] get_reused_times count: ", count)

if 0 == count then
    res, err = red:auth("123456")
    if not res then
        ngx.say("[x]failed to authenticate: ", err)
        return
    end
elseif err then
    ngx.say("[x]failed to get reused times: ", err)
    return
end

ngx.say("[x] set result: ", ok)

local res, err = red:get("name")
if not res then
    ngx.say("[x] failed to get name: ", err)
    return
end

if res == ngx.null then
    ngx.say("[x] name not found.")
    return
end

ngx.say("[x] get name : ", res)

 -- 连接池大小是100个,并且设置最大的空闲时间是 10 秒
 local ok, err = red:set_keepalive(10000, 100)
 if not ok then
     ngx.say("[x] failed to set keepalive: ", err)
     return
 end

red:get_reused_times()方法

  • 如果当前连接不是从内建连接池中获取的,该方法总是返回 0 ,也就是说,该连接还没有被使用过。
  • 如果连接来自连接池,那么返回值永远都是非零。

这个方法可以用来确认当前连接是否来自池子。对于 Redis 授权,实际上只需要建立连接后,首次认证一下,后面只需直接使用即可。换句话说,从连接池中获取的连接都是经过授权认证的,只有新创建的连接才需要进行授权认证。所以大家就看到了 count, err = red:get_reused_times() 这段代码,并有了下面 if 0 == count then 的判断逻辑。

通过curl脚本测试请求打印结果

代码语言:javascript复制
$ curl -i http://openresty.tinywan.com/lua_redis_test
HTTP/1.1 200 OK
Server: openresty/1.17.8.2
Date: Tue, 23 Jul 2024 07:16:23 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding

[x] set result: OK
[x] get name : Tinywan

如果指定的密码是错误的,那么上面的示例将向HTTP客户端输出以下内容:

代码语言:javascript复制
failed to authenticate: ERR invalid password

事务支持

这个库支持Redis事务redis_transactions_test.lua 文件

代码语言:javascript复制
--[[-----------------------------------------------------------------------      
* |  Copyright (C) Shaobo Wan (Tinywan) 
* |------------------------------------------------------------------------
--]]

local cjson = require "cjson"

-- redis config
local redis = require "resty.redis"
local red = redis:new()

red:set_timeouts(1000, 1000, 1000) -- 1 秒

-- 通过宿主机链接
local ok, err = red:connect("192.168.13.168", 6379)

if not ok then
    ngx.say("[x] failed to connect: ", err)
    return
end

-- 设置权限
local res, err = red:auth("123456")
if not res then
    ngx.say("[x] failed to authenticate: ", err)
    return
end

-- 设置权限
local ok, err = red:multi()
if not ok then
    ngx.say("failed to run multi: ", err)
    return
end
ngx.say("[x] multi ans: ", cjson.encode(ok))

local ans, err = red:set("resty_name", "Tinywan")
if not ans then
    ngx.say("[x] failed to run sort: ", err)
    return
end
ngx.say("[x] set ans: ", cjson.encode(ans))

local ans, err = red:lpop("resty_name")
if not ans then
    ngx.say("[x] failed to run sort: ", err)
    return
end
ngx.say("[x] set ans: ", cjson.encode(ans))

ans, err = red:exec()
ngx.say("[x] exec ans: ", cjson.encode(ans))

red:close()

通过curl脚本测试请求打印结果

代码语言:javascript复制
$ curl -i http://openresty.tinywan.com/lua_redis_transactions_test
HTTP/1.1 200 OK
Server: openresty/1.17.8.2
Date: Tue, 23 Jul 2024 07:29:53 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding

[x] multi ans: "OK"
[x] set ans: "QUEUED"
[x] set ans: "QUEUED"
[x] exec ans: ["OK",[false,"WRONGTYPE Operation against a key holding the wrong kind of value"]]

小结

lua-resty-redis是一个强大的工具,能够帮助开发者充分利用OpenResty和Redis的优势,构建高性能的Web服务。无论是简单的缓存解决方案还是复杂的分布式系统,它都能提供稳定且高效的支撑。

0 人点赞