lua-resty-redis

repository·master·Indexed 24 days ago

https://github.com/openresty/lua-resty-redis

A non-blocking Redis client driver for OpenResty/ngx_lua that leverages the cosocket API for high-performance, asynchronous Redis communication within Nginx. It supports connection pooling via set_keepalive, Redis pipelining, SSL connections, and integration with Redis Modules.

Tokens
2.1K
Snippets
5
Records
8
Agent score
34%

What's inside lua-resty-redis

  1. Important Limitations and Best Practices

    master

    To avoid race conditions and errors in a high-concurrency Nginx environment, follow these rules:

    1. Do NOT store the resty.redis object in a Lua module-level variable. This will cause it to be shared across all concurrent requests in a worker process, leading to "socket busy" or "bad request" errors. Always use function local variables or the ngx.ctx table.
    2. Context Restriction: This library cannot be used in Nginx phases where the cosocket API is unavailable, such as init_by_lua*, set_by_lua*, log_by_lua*, or header_filter_by_lua*.
    3. Connection Pool Sizing: A good rule of thumb for set_keepalive is: pool_size = (Total Redis Max Connections) / (Nginx Workers * Nginx Instances).
    4. Error Logging: If you implement custom error handling, disable Nginx's automatic socket error logging by setting lua_socket_log_errors off; in your Nginx configuration to avoid duplicate logs.
  2. Use Redis Pipelining

    master

    Pipelining allows you to send multiple commands to the server in a single run, reducing network round-trips.

    Workflow:

    1. Call red:init_pipeline() to enable mode.
    2. Execute multiple Redis command methods (they will be cached locally).
    3. Call red:commit_pipeline() to send all commands and receive all replies as a single multi-bulk reply.
    4. Use red:cancel_pipeline() if you wish to discard the cached commands instead of executing them.

    Return Values: commit_pipeline returns a Lua table containing the results of all commands. If a command fails, the corresponding element in the table will be {false, err_message}.

    red:init_pipeline()
    red:set("cat", "Marry")
    red:set("horse", "Bob")
    red:get("cat")
    red:get("horse")
    
    local results, err = red:commit_pipeline()
    if not results then
        ngx.say("failed to commit: ", err)
        return
    end
    
    for i, res in ipairs(results) do
        if type(res) == "table" then
            if res[1] == false then
                ngx.say("command ", i, " failed: ", res[2])
            end
        else
            -- process scalar result
        end
    end
  3. How Redis replies are mapped to Lua types

    master

    The library maps Redis protocol replies to native Lua types:

    • Status reply (+): Returns a Lua string with the prefix stripped.
    • Integer reply: Returns a Lua number.
    • Error reply (ERR): Returns false and a string describing the error.
    • Bulk reply:
      • Non-nil: Returns a Lua string.
      • Nil: Returns ngx.null.
    • Multi-bulk reply:
      • Non-nil: Returns a Lua table of values. If any element is an error, that element is the table {false, err_message}.
      • Nil: Returns ngx.null.
  4. Quickstart: Connect and use Redis commands

    master

    To use lua-resty-redis, create a new object using redis:new(), set timeouts, and connect via IP, hostname, or Unix domain socket. Note that if connecting via hostname, you must specify a resolver in your Nginx configuration.

    All Redis commands are available as lowercase methods on the redis object. Methods return the result and an error string on failure.

    -- In nginx.conf
    # resolver 8.8.8.8;
    
    -- In content_by_lua_block
    local redis = require "resty.redis"
    local red = redis:new()
    
    red:set_timeouts(1000, 1000, 1000) -- 1 sec
    
    -- Connect via IP
    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("failed to connect: ", err)
        return
    end
    
    -- Use commands (e.g., SET)
    ok, err = red:set("dog", "an animal")
    
    -- Use commands (e.g., GET)
    local res, err = red:get("dog")
    if not res then
        ngx.say("failed to get dog: ", err)
        return
    end
    
    if res == ngx.null then
        ngx.say("dog not found.")
        return
    end
    
    ngx.say("dog: ", res)
  5. Install lua-resty-redis

    master

    If you are using the OpenResty bundle, the library is included by default and no installation is required. You can simply use require "resty.redis".

    If you are using a custom Nginx + ngx_lua build, you must install it from source and configure the lua_package_path in your nginx.conf to include the library's path.

    # Clone latest release (example for v0.29)
    wget https://github.com/openresty/lua-resty-redis/archive/refs/tags/v0.29.tar.gz
    
    # Extract
    tar -xvzf v0.29.tar.gz
    
    # go into directory
    cd lua-resty-redis-0.29
    
    export LUA_LIB_DIR=/usr/local/openresty/site/lualib
    
    # Compile and Install
    make install

    In nginx.conf:

    http {
        lua_package_path "/path/to/lua-resty-redis/lib/?.lua;;";
        ...
    }
  6. Use Redis Modules (e.g., RedisBloom)

    master

    You can use Redis Modules by registering a module prefix. This allows you to call module-specific commands using a syntax like red:prefix():command().

    Example with RedisBloom (bf):

    local redis = require "resty.redis"
    -- register the module prefix "bf"
    redis.register_module_prefix("bf")
    
    local red = redis:new()
    local ok, err = red:connect("127.0.0.1", 6379)
    
    -- call BF.ADD command
    local res, err = red:bf():add("dog", 1)
    
    -- call BF.EXISTS command
    local res, err = red:bf():exists("dog")
  7. Manage connection pooling with set_keepalive

    master

    To reuse connections and avoid the overhead of frequent handshakes, use set_keepalive instead of close. This puts the current connection into the ngx_lua cosocket connection pool.

    Syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size)

    • max_idle_timeout: Maximum time (in ms) the connection can stay idle in the pool.
    • pool_size: Maximum size of the pool per Nginx worker process.

    Note: Calling set_keepalive immediately turns the current redis object into a closed state. Subsequent operations on that object (other than connect) will fail.

    -- Put connection into pool with 10s max idle time and pool size of 100
    local ok, err = red:set_keepalive(10000, 100)
    if not ok then
        ngx.say("failed to set keepalive: ", err)
        return
    end
  8. Connect to Redis with options

    master

    The connect method allows connecting to a host/port or a Unix domain socket. It supports an optional options_table for advanced configuration.

    Syntax:

    • ok, err = red:connect(host, port, options_table?)
    • ok, err = red:connect("unix:/path/to/unix.sock", options_table?)

    Options Table Keys:

    • ssl: Boolean. If true, uses SSL to connect (default: false).
    • ssl_verify: Boolean. If true, verifies the server SSL certificate (default: false). Requires lua_ssl_trusted_certificate configuration.
    • server_name: String. Specifies the SNI for TLS.
    • pool: String. Custom name for the connection pool.
    • pool_size: Number. Size of the connection pool (per Nginx worker). If omitted and backlog is provided, defaults to lua_socket_pool_size.
    • backlog: Number. Limits the total number of opened connections for this pool. If the pool is full, connections are queued up to this limit.