OpenResty入门:OpenResty使用缓存加速
一、前言
在 Web 应用中,缓存是提升性能的关键技术之一。通过将频繁访问的数据存储在高速存储介质中,可以大幅减少对后端服务的请求压力。OpenResty 提供了多种缓存能力,可以在网关层面实现高效的数据缓存。本文将介绍 OpenResty 的各种缓存应用场景及配置方法。
1、本文主要内容
- 基于 ngx.shared.DICT 的简单数据缓存
- 基于 Lua 内存的 API 响应缓存
- 基于 proxy_cache 的页面/接口响应缓存
- 基于 Redis 的分布式缓存
- 主动更新与异步更新策略
2、本文环境信息
| 工具/环境 | 版本说明 |
|---|---|
| OpenResty | 1.27.1.2 |
| Redis | 7.0+(Docker) |
| 操作系统 | Windows 10/11 或 Linux/macOS |
3、准备工作
1、安装部署 OpenResty
Linux 部署 Openresty|macOS 部署 Openresty|Windows 部署 Openresty|使用 Docker 部署 Openresty
安装后配置把 conf/myconf/*.conf 加入 nginx.conf 中,后续示例中的配置默认都在 conf/myconf 目录下
2、引入 Lua 依赖库
为保证版本一致性,从 GitHub 克隆指定版本:
# 创建目录(Windows 使用 Git Bash)
mkdir -p ~/openresty/lib
cd ~/openresty/lib
# 安装 lua-resty-http(HTTP 客户端)
git clone -b release/0.17.2 https://github.com/ledgetech/lua-resty-http.git
# 安装 lua-resty-redis(Redis 客户端)
git clone -b v0.33 https://github.com/openresty/lua-resty-redis.git
# 复制库文件到统一目录
cp -r ~/openresty/lib/lua-resty-http/lib/resty ~/openresty/lib/
cp -r ~/openresty/lib/lua-resty-redis/lib/resty ~/openresty/lib/
nginx.conf 配置:
在nginx.conf 中的http配置块中添加 Lua 包路径:
# Windows
lua_package_path "C:/Users/ken/openresty/lib/?.lua;;";
# macOS
lua_package_path "/Users/ken/openresty/lib/?.lua;;";
# Linux
lua_package_path "/home/ken/openresty/lib/?.lua;;";
📌
cjson使用 OpenResty 内置库,无需额外安装
3、使用 Docker 启动后端服务和 Redis
# 启动后端服务
docker run -d --name backend -p 8000:8080 kentalk/httpserver:latest
# 启动 Redis
docker run -d --name redis -p 6379:6379 redis:latest
测试后端服务:
curl localhost:8000
二、简单数据缓存(ngx.shared.DICT)
1、场景说明
ngx.shared.DICT 是 OpenResty 内置的共享字典缓存,存储在内存中,访问速度极快。适用于存储计数器、限流状态、临时配置等简单数据。
2、配置实现
创建 conf/myconf/dict_cache_8011.conf:
lua_shared_dict my_cache 1m;
server {
listen 8011;
server_name localhost;
location / {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.my_cache
local count = shared_dict:get("visit_count") or 0
count = count + 1
shared_dict:set("visit_count", count)
ngx.say('{"visit_count": ' .. count .. '}')
}
}
location = /reset {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.my_cache
shared_dict:set("visit_count", 0)
ngx.say('{"message": "计数器已重置"}')
}
}
location = /status {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.my_cache
local keys = shared_dict:get_keys()
local result = {}
for _, key in ipairs(keys) do
result[key] = shared_dict:get(key)
end
ngx.say(require("cjson").encode(result))
}
}
}
3、测试验证
重载配置
nginx -s reload
访问测试
curl http://localhost:8011/
curl http://localhost:8011/
curl http://localhost:8011/
# 每次请求 visit_count 会递增
# {"visit_count": 1}
# {"visit_count": 2}
# {"visit_count": 3}
# 查看缓存状态
curl http://localhost:8011/status
# {"visit_count": 3}
# 重置计数器
curl http://localhost:8011/reset
📌
lua_shared_dict定义的缓存在所有 worker 进程之间共享,Restart 后数据会丢失
三、API 响应缓存(Lua 内存)
1、场景说明
将后端 API 的响应缓存到 Lua 内存中,减少重复请求。适用于高频访问且数据变化频率低的 API。
2、配置实现
创建 conf/myconf/api_cache_8012.conf:
lua_shared_dict api_response_cache 1m;
server {
listen 8012;
server_name localhost;
location / {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.api_response_cache
local cache_key = ngx.var.uri
local cached = shared_dict:get(cache_key)
if cached then
ngx.log(ngx.INFO, "Cache hit: ", cache_key)
ngx.say(cached)
return
end
ngx.log(ngx.INFO, "Cache miss: ", cache_key)
local http = require("resty.http")
local httpc = http.new()
httpc:set_timeout(3000)
local res, err = httpc:request_uri("http://127.0.0.1:8000", {
method = "GET",
path = "/"
})
if not res then
ngx.status = 502
ngx.say('{"error": "Backend error: ' .. (err or 'unknown') .. '"}')
return
end
shared_dict:set(cache_key, res.body, 60) -- 缓存60秒
ngx.say(res.body)
}
}
location = /clear {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.api_response_cache
local keys = shared_dict:get_keys()
for _, key in ipairs(keys) do
shared_dict:delete(key)
end
ngx.say('{"message": "Cache cleared"}')
}
}
}
3、测试验证
重载配置
nginx -s reload
访问测试
# 第1次请求
curl http://localhost:8012/
# 预期输出(缓存未命中,输出time字段与请求时间一致)
{"code":0,"message":"success","data":{"hostname":"409bd7e28495","os":"linux","ip":"172.17.0.2","time":"2026-03-15 21:40:00","msg":"Hello, World! ---This is an HTTP server designed by ken.io."}}
# 第2次请求
curl http://localhost:8012/
# 预期输出(缓存命中,输出time字段跟上次请求一致)
{"code":0,"message":"success","data":{"hostname":"409bd7e28495","os":"linux","ip":"172.17.0.2","time":"2026-03-15 21:40:00","msg":"Hello, World! ---This is an HTTP server designed by ken.io."}}
# 清除缓存
curl http://localhost:8012/clear
# 第3次请求
curl http://localhost:8012/
# 预期输出(缓存未命中,输出time字段与请求时间一致)
{"code":0,"message":"success","data":{"hostname":"409bd7e28495","os":"linux","ip":"172.17.0.2","time":"2026-03-15 21:41:20","msg":"Hello, World! ---This is an HTTP server designed by ken.io."}}
💡 缓存有效期为 60 秒,过期后自动失效,缓存命中情况也可以从nginx日志中查看
📌 缓存 key 使用 URI,对于带参数的请求可以拼接 query_string 作为 key
四、页面响应缓存(Proxy Cache)
1、场景说明
使用 Nginx 的 proxy_cache 将后端响应缓存到磁盘,适用静态页面、接口响应等场景。可以大幅减少后端压力,支持海量请求。
2、配置实现
创建 conf/myconf/proxy_cache_8013.conf:
# Windows 使用临时目录,Linux/macOS 使用 /tmp
proxy_cache_path C:/Users/ken/AppData/Local/Temp/openresty_cache levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
listen 8013;
server_name localhost;
location / {
proxy_cache api_cache;
proxy_cache_key $uri$args;
proxy_cache_valid 200 60s;
proxy_cache_valid 404 10s;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
}
}
3、测试验证
# 第一次请求
curl -i http://localhost:8013/
# X-Cache-Status: MISS
# 第二次请求
curl -i http://localhost:8013/
# X-Cache-Status: HIT,可以观察到缓存命中,返回的time字段跟上次请求一致
# 查看缓存文件
ls -la C:/Users/ken/AppData/Local/Temp/openresty_cache/
📌 配置说明:
proxy_cache_path- 缓存路径和配置proxy_cache_valid- 不同状态码的缓存时间proxy_cache_use_stale- 后端异常时返回过期缓存proxy_cache_lock- 防止缓存击穿
💡
X-Cache-Status头可以快速判断缓存命中情况:MISS/BYPASS/EXPIRED/STALE/HIT
五、分布式缓存(Redis)
1、场景说明
多实例部署时,各 OpenResty 节点需要共享缓存数据。Redis 是最常用的分布式缓存方案,OpenResty 通过 lua-resty-redis 可以方便地操作 Redis。
2、配置实现
创建 conf/myconf/redis_cache_8014.conf:
server {
listen 8014;
server_name localhost;
location / {
default_type application/json;
content_by_lua_block {
local redis = require("resty.redis")
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "Redis connect error: ", err)
ngx.status = 500
ngx.say('{"error": "Redis connection failed"}')
return
end
local cache_key = "api:response:" .. ngx.var.uri
local cached, err = red:get(cache_key)
if cached ~= ngx.null and cached then
ngx.say(cached)
red:close()
return
end
local http = require("resty.http")
local httpc = http.new()
httpc:set_timeout(3000)
local res, err = httpc:request_uri("http://127.0.0.1:8000", {
method = "GET",
path = "/"
})
if not res then
ngx.status = 502
ngx.say('{"error": "Backend error: ' .. (err or 'unknown') .. '"}')
red:close()
return
end
red:setex(cache_key, 60, res.body) -- 缓存60秒
ngx.say(res.body)
red:close()
}
}
location = /clear {
content_by_lua_block {
local redis = require("resty.redis")
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if ok then
local keys = red:keys("api:response:*")
for _, key in ipairs(keys) do
red:del(key)
end
ngx.say('{"message": "Cache cleared, ' .. #keys .. ' keys deleted"}')
red:close()
else
ngx.say('{"error": "Redis connection failed"}')
end
}
}
}
3、测试验证
确保 Redis 服务已启动:
重载配置
nginx -s reload
访问测试
# 第1次请求(缓存未命中)
curl http://localhost:8014/
# 第2次请求(缓存命中)
curl http://localhost:8014/
# 清除缓存
curl http://localhost:8014/clear
# 第3次请求(缓存未命中)
curl http://localhost:8014/
# 可以观察到缓存命中,返回的time字段跟上次请求一致, 也可以通过查看Redis中的缓存数据确认
📌 Redis 缓存优势:
- 多实例共享
- 数据持久化
- 支持复杂数据结构
⚠️ 生产环境建议使用连接池和超时控制
六、缓存更新策略
1、场景说明
缓存数据需要与后端保持一致,常见更新策略有:主动更新、异步更新。
2、主动更新
主动更新通过接口主动刷新缓存,不依赖请求触发。
创建 conf/myconf/cache_active_8015.conf:
lua_shared_dict cache_active 1m;
server {
listen 8015;
server_name localhost;
location / {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.cache_active
local cache_key = "api:data"
local cached = shared_dict:get(cache_key)
if cached then
ngx.say(cached)
return
end
ngx.say('{"error": "Cache not ready, please refresh"}')
}
}
location = /refresh {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.cache_active
local http = require("resty.http")
local httpc = http.new()
httpc:set_timeout(3000)
local res, err = httpc:request_uri("http://127.0.0.1:8000", {
method = "GET",
path = "/"
})
if res then
shared_dict:set("api:data", res.body) -- 手动刷新,无过期时间
ngx.say('{"message": "Cache refreshed"}')
else
ngx.status = 502
ngx.say('{"error": "Backend error: ' .. (err or 'unknown') .. '"}')
end
}
}
}
📌 主动更新:需要配合定时任务或手动调用 /refresh
3、异步更新
异步更新在返回缓存的同时,后台发起子请求更新缓存,实现" stale-while-revalidate "效果。
创建 conf/myconf/cache_async_8016.conf:
lua_shared_dict cache_async 1m;
server {
listen 8016;
server_name localhost;
location / {
default_type application/json;
content_by_lua_block {
local shared_dict = ngx.shared.cache_async
local cache_key = "api:data"
local cached = shared_dict:get(cache_key)
if cached then
ngx.say(cached)
local delay = 5
local handler = function()
local http = require("resty.http")
local httpc = http.new()
httpc:set_timeout(3000)
local res = httpc:request_uri("http://127.0.0.1:8000", {
method = "GET",
path = "/"
})
if res then
shared_dict:set(cache_key, res.body) -- 后台异步更新
end
end
-- 5秒后更新缓存,避免对后端压力过大
ngx.timer.at(delay, handler)
return
end
local http = require("resty.http")
local httpc = http.new()
httpc:set_timeout(3000)
local res, err = httpc:request_uri("http://127.0.0.1:8000", {
method = "GET",
path = "/"
})
if res then
shared_dict:set(cache_key, res.body) -- 首次请求更新缓存,无过期时间
ngx.say(res.body)
else
ngx.status = 502
ngx.say('{"error": "Backend error: ' .. (err or 'unknown') .. '"}')
end
}
}
}
📌 异步更新:首次请求较慢,后续请求快速且后台更新缓存
4、测试验证
主动更新测试:
重载配置
nginx -s reload
访问测试
curl http://localhost:8015/
# 返回 "Cache not ready"
curl http://localhost:8015/refresh
# 刷新缓存
curl http://localhost:8015/
# 返回缓存数据
异步更新测试:
curl http://localhost:8016/
# 首次请求,后台同时更新缓存
curl http://localhost:8016/
# 后续请求,响应快速
💡 推荐:实际生产中可根据场景组合使用多种更新策略
七、最佳实践
1、缓存键设计
- 使用有意义的键名,如
cache:user:123 - 包含版本号,方便缓存管理
- 考虑键的过期时间设计
2、缓存失效处理
- 设置合理的 TTL
- 使用版本号或时间戳控制更新
- 考虑使用缓存锁防止缓存击穿
3、性能优化建议
- ngx.shared.DICT 适合小数据量、高频访问
- Proxy Cache 适合静态内容
- Redis 适合多实例共享场景
- 合理使用连接池
- 设置合理的超时时间
4、缓存监控
- 记录缓存命中率
- 监控缓存大小
- 设置告警阈值
| 缓存类型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| ngx.shared.DICT | 计数器、状态 | 性能极高 | 单机、内存有限 |
| Lua 内存缓存 | API 响应 | 灵活、可编程 | 内存占用 |
| Proxy Cache | 静态内容 | 支持磁盘存储 | 配置复杂 |
| Redis | 分布式共享 | 多实例共享 | 有网络延迟 |