博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
为 OpenResty 项目编写自定义 Nginx C 模块
阅读量:6915 次
发布时间:2019-06-27

本文共 2873 字,大约阅读时间需要 9 分钟。

有些时候,我们需要通过 Lua 代码操作 Nginx 里面的某些状态,但是想要的 API 并不存在于 OpenResty 之内。这时候,可以选择编写一个 Nginx C 模块,然后暴露出可供 Lua 调用的接口。

本文中,我们会分别探讨,如何通过 Nginx 变量或 FFI 的方式去提供 Lua 调用得到的接口。

文中的示例代码可以在 找到。

通过 Nginx 变量提供接口

ngx.var.variable= 在调用的时候,会先查找变量 variable 对应的 handler(一个在 Nginx 内注册的 C 函数),如果 handler 存在,会去调用该 handler。

这意味着,如果我们定义了一个 Nginx 变量和对应的 handler,我们就可以通过在 Lua 代码里调用 ngx.var.variable= 来触发该 handler。

空说无益,先上示例。

在 Nginx 里面我们可以通过 limit_ratelimit_rate_after 两个指令来限制响应给客户端的速率。前者决定了限速的多少,后者决定了从什么时候开始限速。当然更多的时候我们需要动态去调整这两个指标。

limit_rate 对应有一个 Nginx 内置的变量, $limit_rate,我们可以修改该变量来达到动态调整的目的。相关的 Lua 代码是 ngx.var.limit_rate = limit_rate。但是并不存在 $limit_rate_after 这样一个变量。

不用担心。因为我们可以自己加上。

// ngx_http_example_or_module.c// 定义变量和它的 getter/setterstatic ngx_http_variable_t  ngx_http_example_or_variables[] = {    { ngx_string("limit_rate_after"), ngx_http_variable_request_set_size,      ngx_http_variable_request_get_limit_rate_after,      offsetof(ngx_http_request_t, limit_rate_after),      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },    { ngx_null_string, NULL, NULL, 0, 0, 0 }};// getter 和 setter 的实现在 GitHub 上的示例代码里有,这里就不贴上了。

通过 FFI 提供接口

不过在大多数情况下,我们并不需要借助变量来间接调用 Nginx C 函数。我们完全可以借助 LuaJIT 的 FFI,直接调用 Nginx C 函数。

就是一个现成的例子。

下面让我们再看另外一个例子,通过 Lua 代码来获取当前的 Nginx 错误日志等级。

在开发中,我们有时需要在测试环境中通过日志来记录某个 table 的值,比如 ngx.log(ngx.INFO, cjson.encode(res))

在生产环境里,我们会设置日志等级为 error,这样就不会输出 table 的值。但是日志等级无论是多少,cjson.encode 都是必然会被调用的。
不幸的是,这行代码所在的路径非常热,我们需要避免无谓的 json encode 操作。如果能获取实际的日志等级,判断是否为 error,来决定是否调用
cjson.encode,就能省下这一笔开销。

要实现这一功能,仅需加个获取当前配置的日志等级的 Nginx C 函数和对应的 Lua 接口。

我们可以像这样提供一个 Lua 接口:

-- lib/example_or.lua...if not pcall(ffi.typeof, "ngx_http_request_t") then    ffi.cdef[[        struct ngx_http_request_s;        typedef struct ngx_http_request_s  ngx_http_request_t;    ]]endffi.cdef[[int ngx_http_example_or_ffi_get_error_log_level(ngx_http_request_t *r);]]function _M.get_error_log_level()    local r = getfenv(0).__ngx_req    return tonumber(C.ngx_http_example_or_ffi_get_error_log_level(r))end

对应的 Nginx C 函数很简单:

intngx_http_example_or_ffi_get_error_log_level(ngx_http_request_t *r){    ngx_log_t                   *log;    int                          log_level;    if (r && r->connection && r->connection->log) {        log = r->connection->log;    } else {        log = ngx_cycle->log;    }    log_level = log->log_level;    if (log_level == NGX_LOG_DEBUG_ALL) {        log_level = NGX_LOG_DEBUG;    }    return log_level;}

使用时直接拿它跟特定的 Nginx 日志等级常量比较即可:

-- config.lua-- 目前 Nginx 不支持动态变更日志等级,所以可以把日志等级缓存起来local example_or = require "lib.example_or"_M.log_leve = example_or.get_error_log_level()-- in other filelocal config = require "common.config"local log_level = config.log_levelif log_level >= ngx.WARN then    -- 错误日志等级是 warn 或者 info 一类    ngx.log(ngx.WARN, "log a warning event")else    -- 错误日志等级是 error 一类    ngx.log(ngx.WARN, "do not log another warning event")end

转载地址:http://vqicl.baihongyu.com/

你可能感兴趣的文章
LINUX环境OGG同步测试
查看>>
excel VBA编程入门教程
查看>>
Apache Rewrite实现URL的跳转和域名跳转
查看>>
5.3Python数据处理篇之Sympy系列(三)---简化操作
查看>>
系统调优之二CPU子系统
查看>>
Windows Server 2012 NIC功能
查看>>
Goldengate双向复制配置
查看>>
sshd 已死 但是subsys被锁或者Sshd dead but subsys locked
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
在主引导记录(MBR)的救援模式下如何重新安装GRUB引导装载程序
查看>>
我的友情链接
查看>>
git的基本使用和示例
查看>>
用户管理
查看>>
从输入 URL 到页面加载完的过程中都发生了什么事情?
查看>>
揭秘Windows Server2012 核心虚拟化技术Hyper-V
查看>>
java参数传递(值传递还是引用传递)
查看>>
去除文本中重复的行方法
查看>>
On Stack Replacement and JIT
查看>>
CDN业务检测(蓝汛/帝联)
查看>>