OpenResty 使用示例
protocal 转换
location ~ ^/api/([-_a-zA-Z0-9/]+).json {
access_by_lua_file /path/to/lua/api/protocal_decode.lua;
content_by_lua_file /path/to/lua/api/$1.lua;
body_filter_by_lua_file /path/to/lua/api/protocal_encode.lua;
}
- API 设计,添加自己 的 HTTP api 规范地址
location ~ ^/app/([-_a-zA-Z0-9/]+) {
set $path $1;
content_by_lua_file /path/to/lua/app/root/$path.lua;
}
实现 IP 限制
init_by_lua_block {
local iputils = require("resty.iputils")
iputils.enable_lrucache()
local whitelist_ips = {
"127.0.0.1",
"10.10.10.0/24",
"192.168.0.0/16",
}
-- WARNING: Global variable, recommend this is cached at the module level
-- https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker
whitelist = iputils.parse_cidrs(whitelist_ips)
}
access_by_lua_block {
local iputils = require("resty.iputils")
if not iputils.ip_in_cidrs(ngx.var.remote_addr, whitelist) then
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
access_by_lua_block {
if ngx.var.remote_addr == "1.1.1.1" then
local headers = ngx.req.get_headers()
for k,v in pairs(headers) do
ngx.log(ngx.ERR , "header ", k, ": ", v)
end
ngx.log(ngx.ERR , "request_body: ", ngx.var.request_body)
end
}
代理其他API
使用 http 客户端代理 http 请求
opm install pintsized/lua-resty-http
location /test {
content_by_lua_block {
ngx.req.read_body()
local args, err = ngx.req.get_uri_args()
local http = require "resty.http"
local httpc = http.new()
httpc:set_timeout(1000) -- 1000ms
-- https://docs.github.com/zh/rest/guides/getting-started-with-the-rest-api?apiVersion=2022-11-28&tool=curl#using-body-parameters
local res, err = httpc:request_uri("https://api.github.com/repos/octocat/Spoon-Knife/issues", {
method = "POST",
body = args.data,
headers = {
["Accept"] = "Accept",
["Authorization"] = "Bearer YOUR-TOKEN",
}
})
if 200 ~= res.status then
ngx.exit(res.status)
end
if args.key == res.body then
ngx.say("valid request")
else
ngx.say("invalid request")
end
}
}
快速响应终端
ngx.say("xxx")
ngx.eof()
- 下面处理其它逻辑
限速
限速分以下几种
- limit_rate 限制响应速度
- limit_conn 限制连接数
- limit_req 限制请求数
限制响应速度
access_by_lua_block {
-- 设定当前请求的响应上限是 每秒 300K 字节
ngx.var.limit_rate = "300K"
}
限制连接数和请求数
需要使用 openresty/lua-resty-limit-traffic 库
lua_code_cache on;
# 注意 limit_conn_store 的大小需要足够放置限流所需的键值。
# 每个 $binary_remote_addr 大小不会超过 16 字节(IPv6 情况下),算上 lua_shared_dict 的节点大小,总共不到 64 字节。
# 100M 可以放 1.6M 个键值对
lua_shared_dict limit_conn_store 100M;
server {
listen 8080;
location / {
access_by_lua_file src/access.lua;
content_by_lua_file src/content.lua;
log_by_lua_file src/log.lua;
}
}
local limit_conn = require "resty.limit.conn"
-- new 的第四个参数用于估算每个请求会维持多长时间,以便于应用漏桶算法
local limit, limit_err = limit_conn.new("limit_conn_store", 10, 2, 0.05)
if not limit then
error("failed to instantiate a resty.limit.conn object: ", limit_err)
end
local _M = {}
function _M.incoming()
local key = ngx.var.binary_remote_addr
local delay, err = limit:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
if limit:is_committed() then
local ctx = ngx.ctx
ctx.limit_conn_key = key
ctx.limit_conn_delay = delay
end
if delay >= 0.001 then
ngx.log(ngx.WARN, "delaying conn, excess ", delay,
"s per binary_remote_addr by limit_conn_store")
ngx.sleep(delay)
end
end
function _M.leaving()
local ctx = ngx.ctx
local key = ctx.limit_conn_key
if key then
local latency = tonumber(ngx.var.request_time) - ctx.limit_conn_delay
local conn, err = limit:leaving(key, latency)
if not conn then
ngx.log(ngx.ERR,
"failed to record the connection leaving ",
"request: ", err)
end
end
end
return _M
local limit_conn = require "utils.limit_conn"
-- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。
if ngx.req.is_internal() then
return
end
limit_conn.incoming()
local limit_conn = require "utils.limit_conn"
limit_conn.leaving()