server {
location /test {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1 sec
-- 会优先从连接池中获取
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
-- 请注意这里 auth 的调用过程
local count
count, err = red:get_reused_times()
-- get_reused_times为0表示未被使用过,即是新建的连接
if 0 == count then
ok, err = red:auth("password")
if not ok then
ngx.say("failed to auth: ", err)
return
end
elseif err then
ngx.say("failed to get reused times: ", err)
return
end
ok, err = red:set("dog", "an animal")
if not ok then
ngx.say("failed to set dog: ", err)
return
end
ngx.say("set result: ", ok)
-- 连接池大小是100个,并且设置最大的空闲时间是 10 秒
-- 放回连接池,若池子不存在会新建
local ok, err = red:set_keepalive(10000, 100)
if not ok then
ngx.say("failed to set keepalive: ", err)
return
end
}
}
}
我们看下面这段代码:
function get_from_cache(key)
local cache_ngx = ngx.shared.my_cache
local value = cache_ngx:get(key)
return value
end
function set_to_cache(key, value, exptime)
if not exptime then
exptime = 0
end
local cache_ngx = ngx.shared.my_cache
local succ, err, forcible = cache_ngx:set(key, value, exptime)
return succ
end
这里面用的就是 ngx shared dict cache
。你可能会奇怪,ngx.shared.my_cache
是从哪里冒出来的?没错,少贴了 nginx.conf
里面的修改:
lua_shared_dict my_cache 128m;
如同它的名字一样,这个 cache
是 Nginx
所有 worker
之间共享的,内部使用的 LRU
算法(最近最少使用)来判断缓存是否在内存占满时被清除。
与lua_shared_dict不同,lrucache预设key的个数而不是内存大小。直接复制下春哥的示例代码:
local _M = {}
-- alternatively: local lrucache = require "resty.lrucache.pureffi"
local lrucache = require "resty.lrucache"
-- we need to initialize the cache on the Lua module level so that
-- it can be shared by all the requests served by each nginx worker process:
local c = lrucache.new(200) -- allow up to 200 items in the cache
if not c then
return error("failed to create the cache: " .. (err or "unknown"))
end
function _M.go()
c:set("dog", 32)
c:set("cat", 56)
ngx.say("dog: ", c:get("dog"))
ngx.say("cat: ", c:get("cat"))
c:set("dog", { age = 10 }, 0.1) -- expire in 0.1 sec
c:delete("dog")
end
return _M
可以看出来,这个 cache
是 worker
级别的,不会在 Nginx wokers
之间共享。并且,它是预先分配好 key
的数量,而 shared dict
需要自己用 key
和 value
的大小和数量,来估算需要把内存设置为多少。
-
shared.dict
使用的是共享内存,每次操作都是全局锁,如果高并发环境,不同worker
之间容易引起竞争。所以单个shared.dict
的体积不能过大。lrucache
是worker
内使用的,由于Nginx
是单进程方式存在,所以永远不会触发锁,效率上有优势,并且没有shared.dict
的体积限制,内存上也更弹性,但不同worker
之间数据不同享,同一缓存数据可能被冗余存储。 -
Lua lru cache
提供的 API 比较少,现在只有 get、set 和 delete,而ngx shared dict
还可以 add、replace、incr、get_stale(在 key 过期时也可以返回之前的值)、get_keys(获取所有 key,虽然不推荐,但说不定你的业务需要呢); -
第二个是内存的占用,由于 ngx shared dict 是 workers 之间共享的,所以在多 worker 的情况下,内存占用比较少。
跑较长时间的压力测试时,会发现有间隔的抖动,因为缓存失效时,并发请求同时进入查询db的语句中。 解决:加个锁,只让一个线程去查询db。 可以去查询 lua-resty-lock 怎么解决的,有完整代码
可以调用c的函数、数据结构 http://luajit.org/ext_ffi.html
首先按需要的功能搜索,例如http,则在谷歌搜 resty + http; 按star数、更新时间、contributor数找. 把 lua-resty-http/lib/resty/ 的lua文件拷贝到本地对应的resty目录即可
https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx_lua/phase.html
Nginx 子请求是一种非常强有力的方式,它可以发起非阻塞的内部请求访问目标 location。 https://moonbingbing.gitbooks.io/openresty-best-practices/content/openresty/work_with_location.html https://github.com/openresty/lua-nginx-module#ngxlocationcapture
(查文档) headers, err = ngx.req.get_headers(max_headers?, raw?) 请求头数量设置0,则不限制数量;设置n,则只能取n个请求头参数,用于防止攻击;返回的 Lua table中的所有头名称都默认转换为纯小写形式,除非raw=true
location /request_header {
default_type 'text/plain';
content_by_lua_file 'lua/xwctest/request_header.lua';
}
location /api/sub_request_header.json {
default_type 'text/plain';
content_by_lua_file 'lua/xwctest/sub_request_header.lua';
}
--- request_header.lua
ngx.req.set_header('Foo','Bar')
local res = ngx.location.capture('/api/sub_request_header.json')
if res.status == ngx.HTTP_OK then
ngx.say(res.body)
else
ngx.say(res.status)
end
--- sub_request_header.lua
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by x00026369.
--- DateTime: 25/1/2023 下午8:02
---
local function print_table(t)
local function parse_array(key,tab)
local str = ''
for _, v in pairs(tab) do
str = str .. key .. ' ' .. v ..'\r\n'
end
return str
end
local str = ''
for k,v in pairs(t) do
if type(v) == "table" then
str = str .. parse_array(k,v)
else
str = str .. k .. ' ' .. v .. '\r\n'
end
end
return str
end
local headers = ngx.req.get_headers()
ngx.say(print_table(headers))
https://github.com/openresty/lua-nginx-module#ngxheaderheader
注: 当我们使用 ngx.header.Foo 去访问域的时候,会映射到index,跳转到c函数再做访问 当我们使用 ngx.header.Foo='bar' 去修改域的时候,会映射到ngx.newindex,跳转到c函数再做修改 不建议通过pair遍历ngx.header,应当通过 ngx.resp.get_headers()
https://moonbingbing.gitbooks.io/openresty-best-practices/content/json/parse_exception.html
https://moonbingbing.gitbooks.io/openresty-best-practices/content/redis/select-keeplive.html https://moonbingbing.gitbooks.io/openresty-best-practices/content/web/conn_pool.html 不能把未知状态连接放到池子里
ngx是什么
ngx.say(type(ngx)) -- table ,说明是table
为什么可以直接访问? 通过全局变量 _G 直接遍历 _G 会发现没有ngx;在lua中,访问lua table中不存在的域,则会定义到metatable即元表的index中??
local function format_table(t)
local str = ''
for k,v in pairs(t) do
str = str .. k .. ' - ' .. type(v) .. '\r\n'
end
return str
end
--ngx.say("hello")
--ngx.say(type(ngx))
ngx.say(format_table(getmetatable(_G)))
ngx.say(format_table((_G)))
lua_transform_underscores_in_response_headers 控制是否将 ngx.header.HEADER API 中指定的响应头名称中的下划线 (_) 转换为连字符 (-)。