lua-resty-dymetrics

重新编译 OpenResty

动态指标模块需要给 Nginx 核心打补丁,因此需要重新编译 OpenResty。 这个补丁我们会通过邮件的方式进行交付。

下面的脚本用于给 OpenResty 1.19.9.1 版本的 Nginx 核心打补丁。 打完补丁后,用户可以根据原来的打包流程进行打包。

set -e
pkg=openresty-1.19.9.1
ngx=nginx-1.19.9

wget https://openresty.org/download/$pkg.tar.gz
tar -xf $pkg.tar.gz

cd $pkg/bundle/$ngx/
patch -p1 < ../../../nginx-1.19.9-proc_exit_handler.patch
cd ../../..

tar -czf $pkg.tar.gz  $pkg

配置二进制安装包仓库

首先我们需要配置二进制安装包的仓库,按照以下命令进行配置。(命令中的 CLIENT_TOKEN 需要替换成订阅邮件中的有效 Token)

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

sudo bash get-xray-priv-lib-repo.sh -l lua-resty-dymetrics-(customer-name) -t CLIENT_TOKEN

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

sudo bash get-xray-priv-lib-repo.sh -l openresty-priv-comm -t CLIENT_TOKEN

安装软件包

动态指标需要安装如下四个组件

  1. openresty-yajl
  2. openresty-yajl-devel
  3. dymetrics-nginx-module
  4. lua-resty-dymetrics-(customer-name)

其中,lua-resty-dymetrics-(customer-name) 这个组件可以根据客户定制, 因此不同的客户需要根据自己的名称安装响应的组件。下面命令中的 customer-name 需要根据实际情况进行替换。

使用 yum 包管理器的操作系统,执行以下命令进行私有库的安装。

sudo yum install -y dymetrics-nginx-module-1.19.9 openresty-yajl openresty-yajl-devel
sudo yum install -y lua-resty-dymetrics-(customer-name)

使用 dnf 包管理器的操作系统,执行以下命令进行私有库的安装。

sudo dnf install -y dymetrics-nginx-module-1.19.9 openresty-yajl openresty-yajl-devel
sudo dnf install -y lua-resty-dymetrics-(customer-name)

使用 apt 包管理器的操作系统,执行以下命令进行私有库的安装。

sudo apt-get install -y dymetrics-nginx-module-1.19.9 openresty-yajl openresty-yajl-dev
sudo apt-get install -y lua-resty-dymetrics-(customer-name)

使用

在使用前需要在配置文件 nginx.conf 中需要添加以下这些配置项。

  1. load_module 指令用于加载 ngx_http_lua_dymetrics_module 这个动态模块。
  2. lua_package_path 和 lua_package_cpath 指令 指定 lua 文件和 lua 加载的二进制共享库搜索路径。
  3. lua_shared_dymetrics 这个指令定义一个动态指标使用的共享内存区。
    load_module /usr/local/openresty/nginx/lib/ngx_http_lua_dymetrics_module.so;

    http {
        lua_package_path   "/usr/local/openresty/site/lualib/?.ljbc;;";
        lua_package_cpath  "/usr/local/openresty-yajl/lib/?.so;/usr/local/openresty/site/lualib/?.so;;";
        lua_shared_dymetrics  dymetrics 120M;
    }

    server {
        listen       80;
        server_name  localhost;
        set $app_uid "test.openresty.com";

        log_by_lua_block {
            if ngx.ctx.no_dymetrics_log then
                return
            end

            local ts = ngx.time()
            ts = ts - (ts % 60)

            local request_log = require "resty.dymetrics_http_reqs".request_log
            local conn_log= require "resty.dymetrics_http_conns".request_log
            local lua_vars = {}
            local app_id = 0

            request_log(ts, app_id, lua_vars)
            conn_log(ts, app_id, lua_vars)
        }

        location /metrics {
            allow 127.0.0.1;
            allow 18.140.243.108; # just an example
            deny all;

            content_by_lua_block {
                ngx.ctx.no_dymetrics_log = true
                local report_req_data = require "resty.dymetrics_http_reqs".report_data_prometheus
                local report_conn_data = require "resty.dymetrics_http_conns".report_data_prometheus
                local ts = ngx.time()
                ts = ts - 60 - (ts % 60)

                local data, err = report_req_data(ts, 0, "")
                if err ~= nil then
                   ngx.log(ngx.ERR, "prometheus: failed to get http request data:", err)
                else
                    ngx.say(data)
                end

                local data, err = report_conn_data(ts, 0, "")
                if err ~= nil then
                   ngx.log(ngx.ERR, "prometheus: failed to get http connection data:", err)
                else
                    ngx.say(data)
                end
            }
        }

        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

Lua APIs

dymetrics_http_reqs module

request_log

syntax: ok, err = reqs.request_log(time_key, app_id, lua_variables)

context: log_by_lua*

Calling this interface will add the current request to the statistics. If there is an error, it will return nil and the corresponding error message.

The value of time_key is the starting value of the statistics cycle. For example, if 60s is a statistics cycle, then “time_key” can be obtained as follows: time_key = ngx.time() - ngx.time() % 60. app_id is a key for Prometheus output. If app_id is not distinguished, pass value 0. lua_variables is an extension variable that is currently not used and requires passing an empty Lua table.

Here is an example:

log_by_lua_block {
    local ts = ngx.time()
    ts = ts - (ts % 60)

    local request_log = require "resty.dymetrics_http_reqs".request_log
    local lua_vars = {}
    local app_id = 0

    request_log(ts, app_id, lua_vars)
}

report_data

syntax: ok, err = reqs. report_data(time_key, node_id, partition_id, gateway_id)

context: content_by_lua_block*,ngx.timer.*

Get the dynamic indicators for the statistics cycle that starts with time_key. This time_key is for the previous statistics cycle, not the current one, because the current statistics cycle has not yet ended, and new requests will still be added to it. node_id represents the current machine’s ID. If it does not exist, it should be passed as 0. partition_id is a string representing the current partition’s ID. If it does not exist, it can be passed as nil. gateway_id is the ID of the gateway that the current node belongs to. If it does not exist, it can be passed as nil.

Here is an example:

content_by_lua_block {
    local report_req_data = require "resty.dymetrics_http_reqs".report_data
    local ts = ngx.time()
    ts = ts - 60 - (ts % 60)

    local data, err = report_req_data(ts, 0, "")
    if err ~= nil then
       ngx.log(ngx.ERR, "prometheus: failed to get http request data:", err)
    else
        ngx.say(data)
    end
}

report_data_prometheus

syntax: ok, err = reqs.report_data_prometheus(time_key, node_id, partition_id, gateway_id)

context: content_by_lua_block*,ngx.timer.*

Get the dynamic indicators for the statistics cycle that starts with time_key. This time_key is for the previous statistics cycle, not the current one, because the current statistics cycle has not yet ended, and new requests will still be added to it. node_id represents the current machine’s ID. If it does not exist, it should be passed as 0. partition_id is a string representing the current partition’s ID. If it does not exist, it can be passed as nil. gateway_id is the ID of the gateway that the current node belongs to. If it does not exist, it can be passed as nil.

Here is an example:

content_by_lua_block {
    local report_req_data = require "resty.dymetrics_http_reqs".report_data_prometheus
    local ts = ngx.time()
    ts = ts - 60 - (ts % 60)

    local data, err = report_req_data(ts, 0, "")
    if err ~= nil then
       ngx.log(ngx.ERR, "prometheus: failed to get http request data:", err)
    else
        ngx.say(data)
    end
}

dymetrics_http_conns module

request_log

syntax: ok, err = conns.request_log(time_key, app_id, lua_variables)

context: log_by_lua*

Calling this interface will add the current request to the statistics. If there is an error, it will return nil and the corresponding error message.

The value of time_key is the starting value of the statistics cycle. For example, if 60s is a statistics cycle, then “time_key” can be obtained as follows: time_key = ngx.time() - ngx.time() % 60. app_id is a key for Prometheus output. If app_id is not distinguished, pass value 0. lua_variables is an extension variable that is currently not used and requires passing an empty Lua table.

Here is an example:

log_by_lua_block {
    local ts = ngx.time()
    ts = ts - (ts % 60)

    local conn_log = require "resty.dymetrics_http_conns".request_log
    local lua_vars = {}
    local app_id = 0

    request_log(ts, app_id, lua_vars)
}

report_data

syntax: ok, err = conns.report_data(time_key, node_id, partition_id, gateway_id)

context: content_by_lua_block*,ngx.timer.*

Get the dynamic indicators for the statistics cycle that starts with time_key. This time_key is for the previous statistics cycle, not the current one, because the current statistics cycle has not yet ended, and new requests will still be added to it. node_id represents the current machine’s ID. If it does not exist, it should be passed as 0. partition_id is a string representing the current partition’s ID. If it does not exist, it can be passed as nil. gateway_id is the ID of the gateway that the current node belongs to. If it does not exist, it can be passed as nil.

Here is an example:

content_by_lua_block {
    local report_req_data = require "resty.dymetrics_http_conns".report_data
    local ts = ngx.time()
    ts = ts - 60 - (ts % 60)

    local data, err = report_req_data(ts, 0, "")
    if err ~= nil then
       ngx.log(ngx.ERR, "prometheus: failed to get http request data:", err)
    else
        ngx.say(data)
    end
}

report_data_prometheus

syntax: ok, err = conns.report_data_prometheus(time_key, node_id, partition_id, gateway_id)

context: content_by_lua_block*,ngx.timer.*

Get the dynamic indicators for the statistics cycle that starts with time_key. This time_key is for the previous statistics cycle, not the current one, because the current statistics cycle has not yet ended, and new requests will still be added to it. node_id represents the current machine’s ID. If it does not exist, it should be passed as 0. partition_id is a string representing the current partition’s ID. If it does not exist, it can be passed as nil. gateway_id is the ID of the gateway that the current node belongs to. If it does not exist, it can be passed as nil.

Here is an example:

content_by_lua_block {
    local report_req_data = require "resty.dymetrics_http_conns".report_data_prometheus
    local ts = ngx.time()
    ts = ts - 60 - (ts % 60)

    local data, err = report_req_data(ts, 0, "")
    if err ~= nil then
       ngx.log(ngx.ERR, "prometheus: failed to get http request data:", err)
    else
        ngx.say(data)
    end
}