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
                }
            }
        }
    }