lolly/docs/lua/caching/cache_handler.lua
xfy 6543422281 docs: 添加 Nginx 配置和 Lua 脚本示例文档
- config: 反向代理、缓存、负载均衡、安全、SSL 等配置模板
- lua: API 网关、认证、动态路由、限流、WebSocket 等脚本示例

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 17:59:22 +08:00

139 lines
3.5 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- cache_handler.lua
--
-- 缓存处理逻辑:读取、写入、失效
-- 配合 nginx.conf 中的 set 指令使用:
-- set $cache_key "prefix:$request_uri"
-- set $cache_ttl 60
--
-- 在 location 块中使用:
-- content_by_lua_file /path/to/cache_handler.lua;
local cjson = require "cjson.safe"
local _M = {}
-- 默认 TTL
local DEFAULT_TTL = 60
--- 获取缓存数据
-- @param dict 共享字典对象
-- @param key 缓存键
-- @return data 解码后的数据, ttl 剩余秒数
function _M.get(dict, key)
local value = dict:get(key)
if not value then
return nil, 0
end
local ttl = dict:ttl(key) or 0
return cjson.decode(value), ttl
end
--- 设置缓存数据
-- @param dict 共享字典对象
-- @param key 缓存键
-- @param data 要缓存的数据table
-- @param ttl 过期时间(秒),默认 60
-- @return ok, err, forcible
function _M.set(dict, key, data, ttl)
ttl = ttl or DEFAULT_TTL
local json, err = cjson.encode(data)
if not json then
return nil, "encode failed: " .. tostring(err)
end
return dict:set(key, json, ttl)
end
--- 手动失效单个 key
-- @param dict 共享字典对象
-- @param key 缓存键
-- @return true
function _M.invalidate(dict, key)
dict:delete(key)
return true
end
--- 批量失效(按前缀匹配)
-- @param dict 共享字典对象
-- @param prefix key 前缀
-- @return count 清除数量
function _M.invalidate_prefix(dict, prefix)
local keys = dict:get_keys(0)
local count = 0
for _, key in ipairs(keys) do
if string.sub(key, 1, #prefix) == prefix then
dict:delete(key)
count = count + 1
end
end
return count
end
--- 失效全部缓存
-- @param dict 共享字典对象
function _M.invalidate_all(dict)
dict:flush_all()
dict:flush_expired(0)
end
--- 获取缓存统计
-- @param dict 共享字典对象
-- @return stats table
function _M.stats(dict)
local keys = dict:get_keys(0)
return {
capacity = dict:capacity(),
free_space = dict:free_space(),
key_count = #keys,
}
end
--- 主入口:作为 content_by_lua_file 使用时调用
-- 从 nginx 变量读取 key/ttl执行 cache-then-origin 流程
-- 外部数据通过 _M.fetch_data 回调提供
function _M.run(fetch_data)
local cache = ngx.shared.response_cache
local key = ngx.var.cache_key
if not key then
ngx.log(ngx.ERR, "cache_key variable not set")
ngx.status = 500
ngx.say(cjson.encode({error = "cache key not configured"}))
return
end
local ttl = tonumber(ngx.var.cache_ttl) or DEFAULT_TTL
-- 尝试缓存命中
local cached, remaining = _M.get(cache, key)
if cached then
ngx.header["X-Cache"] = "HIT"
ngx.header["X-Cache-TTL"] = tostring(remaining)
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode(cached))
return
end
-- 缓存未命中,调用外部数据源
local data, err = fetch_data()
if not data then
ngx.status = 502
ngx.say(cjson.encode({error = err or "upstream error"}))
return
end
-- 写入缓存
local ok, set_err, forcible = _M.set(cache, key, data, ttl)
if not ok then
ngx.log(ngx.ERR, "cache set failed: ", tostring(set_err))
end
if forcible then
ngx.log(ngx.WARN, "cache LRU eviction occurred")
end
ngx.header["X-Cache"] = "MISS"
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode(data))
end
return _M