OpenResty入门:OpenResty使用缓存加速

2026-03-16 13 阅读 中间件
所属系列: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 部署 OpenrestymacOS 部署 OpenrestyWindows 部署 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 分布式共享 多实例共享 有网络延迟