OpenResty 学习
及其OpenResty最佳实践,这里面包含上面部分API的翻译
- ngx.location.capture
Nginx 子请求是一种非常强有力的方式,它可以发起非阻塞的内部请求访问目标 location。目标 location 可以是配置文件中其他文件目录,或 任何 其他 nginx C 模块,包括
ngx_proxy
、ngx_fastcgi
、ngx_memc
、ngx_postgres
、ngx_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 这个可读性比较好的覆盖率报告。
string.find\(([^)]*)\)
ngx.re.find($1, "jo")
ngx\.log\(ngx\.INFO, monitorlog\.svr_out\(".*?",\s*(.*)\)\)
dsklog.i($1)
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共享。
以及openresty最佳实践-LuaNginxModule-缓存string.find\(([^)]*)\)
ngx.re.find($1, "jo")
ngx\.log\(ngx\.INFO, monitorlog\.svr_out\(".*?",\s*(.*)\)\)
dsklog.i($1)
c语言学生的示例代码
回复删除c编程中的系统正常运行时间