...menustart
...menuend
- string.gsub / string.match 的方法无法jit 优化,尽量使用 ngx_lua 模块提供的 ngx.re.match 等接口
- openresty 正则表达式 : https://moonbingbing.gitbooks.io/openresty-best-practices/lua/re.html
- 前者主要用来预分配 Lua table 空间,后者主要用来高效的释放 table 空间,并且它们都是可以被 JIT 编译的
- 具体可以参考一下 OpenResty 捆绑的 lua-resty-* 库
- 在 Lua 中,函数 time、date 和 difftime 提供了所有的日期和时间功能。
- 这些函数通常会引发不止一个昂贵的系统调用,同时无法为 LuaJIT JIT 编译,对性能造成较大影响。
- 推荐使用 ngx_lua 模块提供的带缓存的时间接口
- ngx.today, ngx.time, ngx.utctime, ngx.localtime, ngx.now, ngx.http_time,以及 ngx.cookie_time 等。
- if 指令是 rewrite 模块中的一部分, 是实时生效的指令
- 在 if 里写入一些非 rewrite 指令, 会出现不可预期的问题
- 唯一正确的修复方式是完全禁用 if 中的非 rewrite 指令
- https://moonbingbing.gitbooks.io/openresty-best-practices/openresty/inline_var.html
ngx.var.VARIABLE
- 外部重定向: ngx.redirect
- 内部重定向: ngx.exec
- 发起非阻塞的内部请求
ngx.location.capture
- 在 OpenResty 的体系中,可以通过共享内存的方式完成不同工作进程的数据共享
- 可以通过 Lua 模块方式完成单个进程内不同请求的数据共享
- 单个请求内不同阶段的数据共享使用 ngx.ctx
- ngx.ctx 是一个表
- 它用来存储基于请求的 Lua 环境数据,其生存周期与当前请求相同 (类似 Nginx 变量)。
- 每个请求,包括子请求,都有一份自己的 ngx.ctx 表
- https://moonbingbing.gitbooks.io/openresty-best-practices/openresty/share_var.html
- https://moonbingbing.gitbooks.io/openresty-best-practices/openresty/safe_sql.html
- 对于 MySQL ,可以调用 ndk.set_var.set_quote_sql_str ,对用户提交的内容 进行一次过滤即可。
- 利用 proxy_pass
- 利用 cosocket
- 日志 统一放到 log_by_lua* 阶段
- 配合 ngx.ctx
- add
body_filter_by_lua 'ngx.log(ngx.INFO,ngx.arg[1])';
to right location
body_filter_by_lua_file lua/body_filter_gameserver.lua ;
注意: body_filter 在一个请求中可能会被多次调用,每次filter一个chunk
-- lua/body_filter_gameserver.lua
local req_headers = ngx.req.get_headers()
local clientCodeVersion = tonumber( req_headers["clientCodeVersion"] ) or -1
if clientCodeVersion >= 20 and ngx.arg[1] and not ngx.is_subrequest then
-- ngx.log( ngx.INFO, "client version code: " .. clientCodeVersion )
local key = string.sub( "UMC_HDA_OHIAOSJDAS" , 1, 16)
local iv = "0123456789ABCDEF"
local src = ngx.arg[1]
local aes = require "resty.aes"
local str = require "resty.string"
local aes_128_cbc_with_iv = assert(aes:new(key,
nil, aes.cipher(128,"cbc"), {iv=iv}))
-- AES 128 CBC with IV and no SALT
local encrypted = aes_128_cbc_with_iv:encrypt( src )
-- ngx.log("AES 128 CBC (WITH IV) Encrypted HEX: ", str.to_hex(encrypted))
-- ngx.log( ngx.INFO , "AES 128 CBC (WITH IV) Decrypted: ", aes_128_cbc_with_iv:decrypt(encrypted))
ngx.arg[1] = ngx.encode_base64(encrypted) .. ','
ngx.log( ngx.INFO, ngx.arg[1] .. src .. ngx.req.get_method() )
end
-- head_filter.lua
-- 假设上游的服务器返回了 Content-Length 报头,而 body_filter_by_lua* 又修改了响应体的实际大小。
-- 客户端收到这个报头后,按其中的 Content-Length 去处理,顺着一头栽进坑里。由于Nginx 的流式响应,发出去的报头就像泼出去的水,要想修改只能提前进行。
-- OpenResty 提供了跟 body_filter_by_lua* 相对应的 header_filter_by_lua*。
-- header_filter 会在 Nginx 发送报头之前调用,所以可以在这里置空 Content-Length 报头:
ngx.header.content_length = nil
- 使用 Lua shared dict
- 所有 worker 之间共享的,内部使用的 LRU 算法
- shared.dict 使用的是共享内存,每次操作都是全局锁
- 使用Lua LRU cache
- worker 级别的,不会在 Nginx wokers 之间共享
- 永远不会触发锁,效率上有优势,并且没有 shared.dict 的体积限制,内存上也更弹性
- 不同 worker 之间数据不同享,同一缓存数据可能被冗余存储。
- ngx.sleep(0.1)
- https://moonbingbing.gitbooks.io/openresty-best-practices/ngx_lua/timer.html
- ngx.timer.at
- ngx.timer 运行在自己的 coroutine 里面
- 如何只启动一个 timer 工作?
- 在 OpenResty 里面,只有在 init_by_lua* 和 init_worker_by_lua* 阶段才能定义真正的全局变量。
- 其他阶段里面,OpenResty 会设置一个隔离的全局变量表,以免在处理过程污染了其他请求。
- 即使在上述两个可以定义全局变量的阶段,也尽量避免这么做。
- 全局变量能解决的问题,用模块变量也能解决, 而且会更清晰、更干净。
- 由于 Lua VM 会把 require 进来的模块缓存到 package.loaded 表里,除非设置了 lua_code_cache off, 模块里定义的变量都会被缓存起来。
- 重要的是,模块变量在每个请求中是共享的
- 模块变量的共享特性,在高并发情况下,可能会出现问题
- 每个请求的数据在传递和存储时须特别小心,只应通过你自己的函数参数来传递,或者通过 ngx.ctx 表。
- 前者效率显然较高,而后者胜在能跨阶段使用。
- limit_rate 限制响应速度
- Nginx 有一个 $limit_rate,这个变量反映的是当前请求每秒能响应的字节数
- limit_conn 限制连接数
- limit_req 限制请求数
- 参考 官方 lua-resty-limit-traffic
- OpenResty 引用第三方 resty 库非常简单,只需要将相应的文件拷贝到 resty 目录下即可。
- seq_id = ngx.worker.id()
- https://moonbingbing.gitbooks.io/openresty-best-practices/web/time_wait.html
- 主动关闭的一方会进入 TIME_WAIT 状态,并且在此状态停留两倍的 MSL 时长。
- Nginx 在某些情况下,会主动关闭客户端的请求,
- 问题出在 User-Agent,Nginx 认为终端的浏览器版本太低,不支持 keep alive,所以直接 close 了。
keepalive_disable none;
- ngx.say 会自动添加一个换行符
- 支持 array table, 可以直接concat后输出
- remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。
- 但是实际场景中,我们即使有代理,也需要将$remote_addr设置为真实的用户IP,以便记录在日志当中,当然nginx是有这个功能,但是需要编译的时候添加--with-http_realip_module 这个模块,默认是没有安装的。
- openResty 使用了
--with-http_realip_module