lua-resty-couchbase

基于 cosocket API 的 ngx_lua Lua Couchbase 客户端驱动

$ opm get iqiyi/lua-resty-couchbase

名称

[!GitHub 问题](https://github.com/iqiyi/lua-resty-couchbase/issues) [!GitHub 分支](https://github.com/iqiyi/lua-resty-couchbase/network) [!GitHub 星星](https://github.com/iqiyi/lua-resty-couchbase/stargazers) [!GitHub 许可证](https://github.com/iqiyi/lua-resty-couchbase/blob/master/license.txt)

lua-resty-couchbase - 基于 cosocket API 的 ngx_lua Lua Couchbase 客户端驱动。

它在爱奇艺播放服务中稳定运行了 5 年以上,在线支持 QPS 达到 20 万。在 OpenResty 架构下,可以直接从 nginx 访问 CouchBase。

使用 cosocket 纯 lua 实现的 couchbase 的 client,已经在爱奇艺重要的播放服务稳定运行 5 年多,线上支持峰值 QPS 达到 20W,在 OpenResty 架构下面,可以直接从 nginx 上面访问 CouchBase.

状态

此库被认为已准备好投入生产。

描述

此 Lua 库是 ngx_lua nginx 模块的 CouchBase 客户端驱动

https://github.com/openresty/lua-nginx-module/#readme

此 Lua 库利用 ngx_lua 的 cosocket API,确保 100% 非阻塞行为。

请注意,至少需要 ngx_lua 0.5.14OpenResty 1.2.1.14

概要

        lua_package_path "/path/to/lua-resty-couchbase/lib/?.lua;;";
    
        lua_shared_dict ldict 10m;
    
        server {
            location /test {
                content_by_lua_block {
                    local cjson = require "cjson"
                    local couchbase = require "resty.couchbase"
    
                    local function get_from_service()
                        -- nothing
                        return "{}"
                    end
    
                    local conf = {
                        hosts = { "10.10.10.1:8091", "10.10.10.2:8091"},
                        bucket_name = "test",
                        bucketpwd = "test-password",
                    }
    
                    local client, err = couchbase:create_client(conf.hosts, conf.bucket_name, conf.bucketpwd)
                    if client == nil then
                        ngx.log(ngx.ERR, err)
                    end
    
                    -- test set_timeout
                    client:set_timeout(500)
    
                    local key = "test-key"
                    local key1 = "test-key1"
                    -- test set
                    client:set(key, "{}")
                    client:set(key1, "{}")
    
                    -- test get_bluk
                    local values, bluk_err = client:get_bluk(key, key1)
                    if not bluk_err then
                        ngx.say(cjson.encode(values))
                    end
    
                    -- test n1ql
                    local result, query_err = client:query('SELECT country FROM `travel-sample` WHERE name = "Excel Airways";')
                    if not query_err then
                        ngx.say(result)
                    end
    
                    -- test get get_from_replica
                    local value, get_err = client:get(key)
                    if value then
                        ngx.say(value)
                    else
                        if get_err then
                            if string.find(get_err, "Not found") then
                                ngx.log(ngx.INFO, "key not found: ", key, " error: ", get_err)
                                ngx.say(get_from_service())
                            else
                                local value_bak, err_bak = client:get_from_replica(key)
                                if value_bak then
                                    ngx.log(ngx.WARN, "get key from replica success: ", key)
                                    ngx.say(value_bak)
                                else
                                    ngx.log(ngx.ERR, "get replica error: ", key, "error: ", err_bak)
                                    ngx.say(get_from_service())
                                end
                            end
                        end
                    end
    
                    -- test close
                    client:close()
    
                }
            }
        }

调试

使用 lua-cjson 库将 couchbase 命令方法的返回值编码为 JSON 通常很方便。例如,

        local cjson = require "cjson"
        ...
        local res, err = client:get("h1234")
        if res then
            print("res: ", cjson.encode(res))
        end

自动错误日志记录

默认情况下,底层的 ngx_lua 模块在发生套接字错误时会进行错误日志记录。如果您已经在自己的 Lua 代码中进行适当的错误处理,那么建议您通过关闭 ngx_lualua_socket_log_errors 指令来禁用此自动错误日志记录,即

        lua_socket_log_errors off;

问题检查清单

  1. 确保您在 set_keepalive 中正确配置了连接池大小。基本上,如果您的 NGINX 处理 n 个并发请求,并且您的 NGINX 有 m 个工作进程,那么连接池大小应配置为 n/m。例如,如果您的 NGINX 通常处理 1000 个并发请求,并且您有 10 个 NGINX 工作进程,那么连接池大小应为 100。

  2. 确保您没有在 set_timeoutset_timeouts 方法中使用过短的超时设置。如果必须这样做,请尝试在超时后重新执行操作并关闭 "自动错误日志记录"(因为您已经在自己的 Lua 代码中进行了适当的错误处理)。

  3. 如果您的 NGINX 工作进程在负载下 CPU 使用率很高,那么 NGINX 事件循环可能会被 CPU 计算阻塞太多。尝试对典型的 NGINX 工作进程采样 C-land on-CPU Flame GraphLua-land on-CPU Flame Graph。您可以根据这些火焰图优化 CPU 密集型操作。

  4. 如果您的 NGINX 工作进程在负载下 CPU 使用率很低,那么 NGINX 事件循环可能会被一些阻塞系统调用(如文件 IO 系统调用)阻塞。您可以通过对典型的 NGINX 工作进程运行 epoll-loop-blocking-distr 工具来确认问题。如果是这样,那么您可以进一步对 NGINX 工作进程采样 C-land off-CPU Flame Graph 来分析实际的阻塞者。

限制

  • 此库不能在 init_by_lua*、set_by_lua*、log_by_lua* 和 header_filter_by_lua* 等代码上下文中使用,因为这些上下文不可用 ngx_lua cosocket API。

待办事项

错误和补丁

请通过以下方式报告错误或提交补丁

  1. 在 gitlab 上创建工单。

作者

goecho <gaofeng@qiyi.com>

版权和许可

此模块根据 BSD 许可证授权。

版权所有 (C) 2015-2020,goecho gaofeng@qiyi.com,爱奇艺公司。

版权所有。

只要满足以下条件,就可以在源代码和二进制形式中重新分发和使用此软件,无论是否修改。

  • 源代码的重新分发必须保留上述版权声明、此条件列表以及以下免责声明。

  • 二进制形式的重新分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表以及以下免责声明。

本软件按“原样”提供,版权持有者和贡献者不提供任何明示或暗示的保证,包括但不限于对适销性和适用于特定目的的暗示保证。在任何情况下,版权持有者或贡献者均不对任何直接、间接、偶然、特殊、示例性或后果性损害(包括但不限于获取替代商品或服务的费用;使用、数据或利润损失;或业务中断)负责,无论任何责任理论,无论是合同、严格责任或侵权行为(包括疏忽或其他原因),无论是否因使用本软件而产生,即使已告知可能发生此类损害。

另请参阅

  • ngx_lua 模块:https://github.com/openresty/lua-nginx-module/#readme

作者

goecho (gaofeng@qiyi.com)

许可证

2bsd

依赖项

luajit >= 2.1.0,ngx_http_lua >= 0.10.6

版本