lua-resty-redis-fast

安装软件包要求用户具有 root 权限 机器必须能够访问以下的域名以下载软件包和 GPG 秘钥: 你可以通过执行以下命令验证域名可访问性: 首先我们需要配置二进制安装包的仓库,按照以下命令进行配置。

版本兼容性

本文保留旧实现的安装和使用说明,并补充面向 Nginx 1.31.8 的新版配置说明。

实现适用版本状态与使用说明
新版独立动态模块仅面向 Nginx 1.31.8,对应 OpenResty 1.31.8.x加载 ngx_redis_fast_module.so;Lua 接口差异见“新版使用”。
旧实现与 OpenResty 1.21.4.x、1.25.3.x、1.27.1.x 或 1.31.8.x 匹配的旧版软件包保留以下安装步骤和“旧版使用”说明。

旧版 OpenResty 用户应继续使用匹配的旧版私有库。本文的新接口说明依据新版源码;安装时请选择与 OpenResty 匹配的软件包,并根据所安装的实现选择配置。

与开源库的性能比较

在一个典型的并发读取应用场景中,lua-resty-redis-fast 的吞吐量达到开源 lua-resty-redis v0.34 的 3.47 倍,提升 246.9%。

指标lua-resty-redis-fastlua-resty-redis v0.34
GET 吞吐量中位数214,314 次/秒61,778 次/秒
GET p99 延迟中位数0.785 毫秒1.793 毫秒

性能比较采用相同 Lua 并发的共享连接模式,物理连接数不同。测试中 Fast 的连接上限为 6,当前默认上限为 3。实际提升取决于业务负载、连接配置和部署环境。

前提条件

  • 安装软件包要求用户具有 root 权限

  • 机器必须能够访问以下的域名以下载软件包和 GPG 秘钥:

    你可以通过执行以下命令验证域名可访问性:

    curl https://openresty.org/package/pubkey.gpg
    curl https://pkg2.openresty.com/xray-priv-libs/
    

配置软件包仓库

将 CLIENT_TOKEN 替换为订阅邮件中的有效 Token。先下载仓库配置脚本,再按照所用实现选择以下步骤。这些命令只配置仓库,不会安装私有库软件包。

curl -o get-xray-priv-lib-repo.sh https://pkg2.openresty.com.cn/scripts/get-xray-priv-lib-repo.sh

新旧实现都需要配置 OpenResty 开源软件仓库,以便安装 OpenResty 及其依赖。

OpenResty 1.31.8.x 新版实现的仓库

新版只需配置 lua-resty-redis-fast 私有仓库:

sudo bash get-xray-priv-lib-repo.sh -l lua-resty-redis-fast -t CLIENT_TOKEN

新版不需要安装 coro-nginx-module 或 coro-hiredis-nginx-module 软件包,也不需要加载它们的 Nginx 动态模块。

旧版实现的仓库

使用匹配的旧版软件包时,需要配置以下两个私有仓库:

sudo bash get-xray-priv-lib-repo.sh -l coro-nginx-module -t CLIENT_TOKEN

sudo bash get-xray-priv-lib-repo.sh -l lua-resty-redis-fast -t CLIENT_TOKEN

安装

针对 OpenResty-1.21.4.x

使用 yum 的系统,执行以下命令安装私有库:

sudo yum install --disablerepo="*" --enablerepo="coro-nginx-module,lua-resty-redis-fast" -y lua-resty-redis-fast-1.21.4

使用 dnf 的系统,执行以下命令安装私有库:

sudo dnf install --disablerepo="*" --enablerepo="coro-nginx-module,lua-resty-redis-fast" -y lua-resty-redis-fast-1.21.4

使用 apt 的系统,执行以下命令安装私有库:

sudo apt-get install -y lua-resty-redis-fast-1.21.4

针对 OpenResty 1.25.3.x

使用 yum 的系统,执行以下命令安装私有库:

sudo yum install --disablerepo="*" --enablerepo="coro-nginx-module,lua-resty-redis-fast"  -y lua-resty-redis-fast-1.25.3

使用 dnf 的系统,执行以下命令安装私有库:

sudo dnf install --disablerepo="*" --enablerepo="coro-nginx-module,lua-resty-redis-fast" -y lua-resty-redis-fast-1.25.3

使用 apt 的系统,执行以下命令安装私有库:

sudo apt-get install -y lua-resty-redis-fast-1.25.3

针对 OpenResty 1.27.1.x

使用 yum 的系统,执行以下命令安装私有库:

sudo yum install --disablerepo="*" --enablerepo="coro-nginx-module,lua-resty-redis-fast"  -y lua-resty-redis-fast-1.27.1

使用 dnf 的系统,执行以下命令安装私有库:

sudo dnf install --disablerepo="*" --enablerepo="coro-nginx-module,lua-resty-redis-fast" -y lua-resty-redis-fast-1.27.1

使用 apt 的系统,执行以下命令安装私有库:

sudo apt-get install -y lua-resty-redis-fast-1.27.1

针对 OpenResty 1.31.8.x

软件包名为 lua-resty-redis-fast-1.31.8,提供 RPM 和 DEB 软件包。新版需要 OpenResty 1.31.8.1 或更新的同系列版本。安装前应配置 OpenResty 开源软件仓库,以便包管理器解析依赖。软件包名称中的 1.31.8 只标识匹配的 Nginx 系列;升级后请确认加载的是新版独立动态模块。

使用 yum、dnf 或 apt 的系统,分别执行对应命令:

sudo yum install -y lua-resty-redis-fast-1.31.8
sudo dnf install -y lua-resty-redis-fast-1.31.8
sudo apt-get install -y lua-resty-redis-fast-1.31.8

安装 OpenResty

新版选择 OpenResty 1.31.8.x,使用旧版私有库时则选择匹配的 OpenResty 系列。安装或升级前,先查询软件仓库中的完整版本号,避免动态模块与 OpenResty 不兼容。

包管理器查询可用版本安装指定版本
yumyum list --showduplicates openrestysudo yum install openresty-VERSION
dnfdnf list --showduplicates openrestysudo dnf install openresty-VERSION
aptapt-cache madison openrestysudo apt-get install openresty=VERSION

将 VERSION 替换为查询结果中与所选私有库匹配的完整版本号。

使用

请区分新版独立动态模块与旧版 coro 实现。

新版使用

Nginx 配置

在 nginx.conf 的 main 上下文中加载新版软件包提供的动态模块:

load_module /usr/local/openresty/nginx/modules/ngx_redis_fast_module.so;

自定义安装目录时,应替换为实际模块路径。新版不再需要本库的 coro_preload、coro_stack_size 或两个 coro 动态模块;其他私有库仍需要的配置应予以保留。RPM 和 DEB 软件包使用相同的配置方式。软件包将 Lua 字节码模块安装到 /usr/local/openresty/site/lualib;自定义 lua_package_path 时应保留默认路径(;;),或包含 /usr/local/openresty/site/lualib/?.ljbc。Nginx worker 需要能够读取模块和所需证书、私钥文件。

本次 Lua 接口与行为差异

入口仍为 require "resty.redis.fast",常用命令和流水线方法继续可用,但连接所有权、连接池和会话操作需要按新版语义使用:

接口或配置新版行为与迁移要求
connect(host, port, options)支持 TCP、TLS 和 Unix domain socket;端口可为数字或数字字符串。主机名需要 Nginx resolver。重新连接会清除原有流水线和订阅状态,失败后对象保持关闭。
db、select(db)可在连接时选择数据库,支持数字或数字字符串。select() 只改变当前 Lua 实例的数据库绑定,不改变其他实例;事务、WATCH、订阅或不可迁移的会话状态中不能切换数据库。
max_connections默认 3,限制每个 worker、服务端地址和数据库的普通共享连接总数,跨自定义池名、认证和 TLS/RESP 设置计数;每个数据库独立计数,独占连接不受此限制。该地址和数据库的首个连接在连接池仍被使用期间确定上限。
pending_threshold默认 32,为每条连接上的待回复命令数软阈值。普通命令选择兼容连接中待回复最少的连接;全部达到阈值时,在连接预算允许的情况下扩容,否则继续共享最空闲的连接。
pool_size、backlog空闲连接数默认 128;设置 backlog 后,pool_size 还限制该池的普通物理连接数,backlog 限制等待建连的队列长度。它们与 max_connections 的限制范围不同。
set_keepalive(timeout, size)新版实际使用两个参数,更新空闲策略并关闭当前逻辑客户端;默认 60000 毫秒和 128 条空闲连接,超时为 0 时不自动过期。升级时需检查现有调用的超时和容量参数。
close()释放当前逻辑客户端;干净的普通物理连接可以继续缓存复用,独占或被修改的会话连接会关闭,不保证立即断开共享物理连接。
get_reused_times()返回最近借用的物理连接的复用计数,关闭时返回 0;后续操作可能使用另一条连接。
exclusive = true需要 WAIT 或 WAITAOF 时,必须在写入前以此选项连接,将写入和确认固定在同一条连接上;共享模式会拒绝这两个命令。
TLS 与认证TLS 证书链验证默认开启;ssl_ca_cert 指定 CA,ssl_verify_name 单独校验证书 DNS 名或 IP,SNI 不代替身份验证。可在 connect() 中提供 password、username。
RESP3在 connect() 中设置 resp3 = true;受保护的服务器还需同时提供认证信息。手动认证时,先以默认 RESP2 连接,AUTH 成功后再调用 hello(3)。
错误返回Redis 命令错误仍返回 false, err,传输错误返回 nil, err;数组或流水线错误项为 {false, err}。RESP3 的 false, nil 是有效结果,应检查 err ~= nil。

普通命令完成、读取超时或协程取消后会归还物理连接;当前 Lua 实例仍可继续执行普通命令,迟到回复会被丢弃,不会交给后续调用。改变会话状态的请求超时或取消后,需先执行 RESET,再执行其他命令或归还连接。超时不代表写入没有执行,不能无条件重试写操作。不要在并发操作间共享同一个 Lua 客户端对象。

AUTH、HELLO、RESET、事务、WATCH、订阅和阻塞命令使用独占连接。SELECT 在普通流水线中形成分段边界;事务或 WATCH 中的 SELECT 返回 nil, "SELECT cannot switch connections in a transaction",含此类切换的流水线会在发送任何命令前被拒绝。订阅和 MONITOR 命令不能放入流水线。

Lua 示例

以下代码可放入 content_by_lua_block 中:

local redis = require "resty.redis.fast"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)

local ok, err = red:connect("127.0.0.1", 6379, {
    db = 0,
    max_connections = 3,
    pending_threshold = 32,
})
if not ok then
    ngx.say("failed to connect: ", err)
    return
end

local res
res, err = red:set("dog", "an animal")
if err ~= nil then
    red:close()
    ngx.say("failed to set dog: ", err)
    return
end

res, err = red:get("dog")
if err ~= nil then
    red:close()
    ngx.say("failed to get dog: ", err)
    return
end

ngx.say("dog: ", res == ngx.null and "not found" or res)
ok, err = red:set_keepalive(60000, 128)
if not ok then
    red:close()
    ngx.say("failed to set keepalive: ", err)
end

完整参数和会话规则见API 手册。本库仅支持 HTTP 子系统,不提供 Redis Cluster 路由;访问 Redis Cluster 应使用 lua-resty-redis-cluster-fast。

旧版使用

以下保留旧实现的 Nginx 配置和 Lua 示例;新版请使用上面的配置并核对接口差异。

Nginx 配置

在使用前需要在配置文件 nginx.conf 中需要添加以下这些配置项,用以加载 lua-resty-redis-fast 相关的动态模块。

    ...

    load_module "/usr/local/openresty-coro-nginx-module/lib/ngx_http_coro_module.so";
    load_module "/usr/local/openresty-coro-nginx-module/lib/ngx_http_coro_hiredis_module.so";

    http {
        coro_preload /usr/local/openresty-plus/hiredis/lib/libhiredis.so;

        coro_stack_size  32768;
        ...
    }

Lua 接口

除了 set_keepalive 接口发生了变化,该库的其他接口和开源的 lua-resty-redis 保持一致,以下是一个简单的例子:

    load_module "/usr/local/openresty-coro-nginx-module/lib/ngx_http_coro_module.so";
    load_module "/usr/local/openresty-coro-nginx-module/lib/ngx_http_coro_hiredis_module.so";

    http {
        coro_preload /usr/local/openresty-plus/hiredis/lib/libhiredis.so;
        coro_stack_size  32768;

        server {
            location /test {
                # need to specify the resolver to resolve the hostname
                resolver 8.8.8.8;

                content_by_lua_block {
                    local redis = require "resty.redis.fast"
                    local red = redis:new()

                    red:set_timeout(1000) -- 1 sec

                    local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
                    if not ok then
                        ngx.say("failed to connect: ", err)
                        return
                    end

                    ok, err = red:select(1)
                    if not ok then
                        ngx.say("failed to select: ", err)
                        return
                    end

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

                    local ok, err = red:set_keepalive()
                    if not ok then
                        ngx.say("failed to set keepalive: ", err)
                        return
                    end
                }
            }
        }
    }