OpenResty学习笔记

Nginx 子请求是一种非常强有力的方式,它可以发起非阻塞的内部请求访问目标 location。目标 location 可以是配置文件中其他文件目录,或 任何 其他 nginx C 模块,包括 ngx_proxyngx_fastcgingx_memcngx_postgresngx_drizzle,甚至 ngx_lua 自身等等 。

需要注意的是,子请求只是模拟 HTTP 接口的形式, 没有 额外的 HTTP/TCP 流量,也 没有 IPC (进程间通信) 调用。所有工作在内部高效地在 C 语言级别完成。

子请求与 HTTP 301/302 重定向指令 (通过 ngx.redirect) 完全不同,也与内部重定向 ((通过 ngx.exec) 完全不同。

在发起子请求前,用户程序应总是读取完整的 HTTP 请求体 (通过调用 ngx.req.read_body 或设置 lua_need_request_body指令为 on).

发送一个 POST 子请求,可以这样做:


 res = ngx.location.capture(
     '/foo/bar',
     { method = ngx.HTTP_POST, body = 'hello, world' }
 )

因为 Nginx 内核限制,子请求不允许类似 @foo 命名 location。请使用标准 location,并设置 internal 指令,仅服务内部请求。

其中@用来定义一个命名location。主要用于内部重定向,不能用来处理正常的请求。其用法如下:

location / {
    try_files $uri $uri/ @custom
}
location @custom {
    # ...do something
}

再看一个例子:

local res = ngx.location.capture(
‘/print_param’,
{
method = ngx.HTTP_POST,
args = ngx.encode_args({a = 1, b = ‘2&’}),
body = ngx.encode_args({c = 3, d = ‘4&’})
}
)

这里使用ngx.encode_args来设置参数

  • OpenResty中提供的组件

在OpenResty中存在很多默认提供的组件,可以方便使用,其中所有的组件列表在:openresty组件

比如cjson,可以直接local cjson = require(“cjson”)来获取。参见lua-cjson-library中的Github仓库的README

其他常见的redis组件:lua-resty-redis-library;ngx组件:lua-nginx-module(这里面的Github就是上面提到的)

  • ngx.eof() 返回后继续执行

在一些请求中,我们会做一些日志的推送、用户数据的统计等和返回给终端数据无关的操 作。而这些操作,即使你用异步非阻塞的方式,在终端看来,也是会影响速度的。这个和我 们的原则:终端请求,需要用最快的速度返回给终端,是冲突的。 这时候,最理想的是,获取完给终端返回的数据后,就断开连接,后面的日志和统计等动 作,在断开连接后,后台继续完成即可。 怎么做到呢?我们先看其中的一种方法:

local response, user_stat = logic_func.get_response(request)

ngx.say(response)

ngx.eof()

if user_stat then

local ret = db_redis.update_user_data(user_stat)

end

没错,最关键的一行代码就是ngx.eof(),它可以即时关闭连接,把数据返回给终端,后面的 数据库操作还会运行。

比如上面代码中的 local response, user_stat = logic_func.get_response(request) 运行了 0.1 秒,而 db_redis.update_user_data(user_stat) 运行了 0.2 秒,在没有使用 ngx.eof() 之前,终端感知到的是 0.3 秒,而加上 ngx.eof() 之 后,终端感知到的只有 0.1 秒。

需要注意的是,你不能任性的把阻塞的操作加入代码,即使在 ngx.eof()之后。 虽然已经返回 了终端的请求,但是,Nginx 的 worker 还在被你占用。所以在 keep alive 的情况下,本次请 求的总时间,会把上一次 eof() 之后的时间加上。 如果你加入了阻塞的代码,Nginx 的高并 发就是空谈。 有没有其他的方法来解决这个问题呢?我们会在ngx.timer.at里面给大家介绍更优雅的方案。

  • ngx.timer.at(delay, handler)  定时任务

ngx.timer.at 的 delay 参数,指定的是以秒为单位的延迟触发时间。跟 OpenResty 的其他 函数一样,指定的时间最多精确到毫秒。如果你想要的是一个当前阶段结束后立刻执行的回 调,可以直接设置 delay 为 0。 handler 回调第一个参数 premature,则是用于标识触发该 回调的原因是否由于 timer 的到期。Nginx worker 的退出,也会触发当前所有有效的 timer。 这时候 premature 会被设置为 true。回调函数需要正确处理这一参数(通常直接返回即 可)。

举个栗子:

local delay = 5

local handler — do some routine job in Lua just like a cron job

handler = function (premature)

if premature then

return

end

local ok, err = ngx.timer.at(delay, handler)

if not ok then

ngx.log(ngx.ERR, “failed to create the timer: “, err)

return

end

end

local ok, err = ngx.timer.at(delay, handler)

if not ok then

ngx.log(ngx.ERR, “failed to create the timer: “, err)

return

end

从示例代码中我们可以看到, ngx.timer.at 创建的回调是一次性的。如果要实现“定期”运 行,需要在回调函数中重新创建 timer 才行。

  • 代码覆盖率

step1:sudo luarocks install luacov

step2:在nginx.conf和local.conf中http上下文中加入

init_by_lua_block {

require ‘luacov.tick’

jit.off()

}

可能还需要lua_package_path中加上/usr/local/share/lua/5.1/?.lua;

step3:(重启OpenResty)运行单元测试
step4:运行结束后就会生成 luacov.stats.out 这个统计文件。然后 cd 到这个目录 下,运行: luacov,就可以看到生成 luacov.report.out 这个可读性比较好的覆盖率报告。

  • os.getenv(ENV_NAME) 获取系统的环境变量

需要在nginx.conf中使用env ENV_NAME来把这个环境变量列出来,然后lua代码中的os.getenv()才能获得环境变量的值

  • 连接池

在 OpenResty 中,所有具备 set_keepalive 的类、库函数,说明他都是支持连接池的。

以redis为例,看其连接池的使用方法:

local redis = require "resty.redis"
local red = redis:new()

local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
  ngx.say("failed to connect: ", err)
  return
end

-- red:set_keepalive(10000, 100)       -- 坑①,数据没有传完不要放到池子里

ok, err = red:set("dog", "an animal")
if not ok then
  -- red:set_keepalive(10000, 100)   -- 坑②不要把状态未知的链接放入池子里
  return
end

-- 坑③这不是坑,正确操作
red:set_keepalive(10000, 100)
  • 共享内存

在openresty中,对于nginx的每个请求,各自都有自己独立的全局变量内存。

在每个worker Process中,module的代码和数据是共享的,只会在第一次时加载,后面的请求不会重复加载。(所以存在对module的修改会在request之间互相影响)

对于同一个worker Process,init_worker_by_lua_file中的代码只会执行一遍。而且是在启动时,不是在请求中。

不同的worker Process需要使用shared DICT来共享数据

lua-resty-lrucache因为存在Lua VM中,而Lua VM与worker相关,所以应该也不能跨worker共享。

另外可以参考:data-sharing-within-an-nginx-workerlua-variable-scope

以及openresty最佳实践-LuaNginxModule-缓存

string.find\(([^)]*)\)
ngx.re.find($1, “jo”)
ngx\.log\(ngx\.INFO, monitorlog\.svr_out\(“.*?”,\s*(.*)\)\)
dsklog.i($1)

转载需保留链接来源:软件玩家 » OpenResty学习笔记

赞 (0) 打赏

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

感恩您的支助!

微信扫一扫打赏