Skip to content

Latest commit

 

History

History
234 lines (197 loc) · 8.41 KB

openresty.md

File metadata and controls

234 lines (197 loc) · 8.41 KB

openresty

1. redis

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
        }
    }
}

2. 缓存

使用 Lua shared dict

我们看下面这段代码:

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;

如同它的名字一样,这个 cacheNginx 所有 worker 之间共享的,内部使用的 LRU 算法(最近最少使用)来判断缓存是否在内存占满时被清除。

使用Lua LRU cache

与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

可以看出来,这个 cacheworker 级别的,不会在 Nginx wokers 之间共享。并且,它是预先分配好 key 的数量,而 shared dict 需要自己用 keyvalue 的大小和数量,来估算需要把内存设置为多少。

如何选择

  1. shared.dict 使用的是共享内存,每次操作都是全局锁,如果高并发环境,不同 worker 之间容易引起竞争。所以单个 shared.dict 的体积不能过大。lrucacheworker 内使用的,由于 Nginx 是单进程方式存在,所以永远不会触发锁,效率上有优势,并且没有 shared.dict 的体积限制,内存上也更弹性,但不同 worker 之间数据不同享,同一缓存数据可能被冗余存储。

  2. Lua lru cache 提供的 API 比较少,现在只有 get、set 和 delete,而 ngx shared dict 还可以 add、replace、incr、get_stale(在 key 过期时也可以返回之前的值)、get_keys(获取所有 key,虽然不推荐,但说不定你的业务需要呢);

  3. 第二个是内存的占用,由于 ngx shared dict 是 workers 之间共享的,所以在多 worker 的情况下,内存占用比较少。

缓存失效风暴

跑较长时间的压力测试时,会发现有间隔的抖动,因为缓存失效时,并发请求同时进入查询db的语句中。 解决:加个锁,只让一个线程去查询db。 可以去查询 lua-resty-lock 怎么解决的,有完整代码

3. FFI

可以调用c的函数、数据结构 http://luajit.org/ext_ffi.html

4. 引入第三方模块

首先按需要的功能搜索,例如http,则在谷歌搜 resty + http; 按star数、更新时间、contributor数找. 把 lua-resty-http/lib/resty/ 的lua文件拷贝到本地对应的resty目录即可

5. 执行阶段

https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx_lua/phase.html

6. 子查询

Nginx 子请求是一种非常强有力的方式,它可以发起非阻塞的内部请求访问目标 location。 https://moonbingbing.gitbooks.io/openresty-best-practices/content/openresty/work_with_location.html https://github.com/openresty/lua-nginx-module#ngxlocationcapture

7. 请求头

(查文档) 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))

9. 响应头

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()

10. 异常处理

https://moonbingbing.gitbooks.io/openresty-best-practices/content/json/parse_exception.html

11. 长连接问题

https://moonbingbing.gitbooks.io/openresty-best-practices/content/redis/select-keeplive.html https://moonbingbing.gitbooks.io/openresty-best-practices/content/web/conn_pool.html 不能把未知状态连接放到池子里

12. ngx

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)))

8. 一些参数

lua_transform_underscores_in_response_headers 控制是否将 ngx.header.HEADER API 中指定的响应头名称中的下划线 (_) 转换为连字符 (-)。