7.9.4 常用模块与生态库(lua-resty-*, 第三方模块)

本节聚焦 OpenResty 常用模块与生态库的实战使用,覆盖 lua-resty-* 官方库与常见第三方模块的选型、安装、配置要点与典型场景;并提供安装命令、配置示例、排错与练习。

一、原理草图:OpenResty 模块生态与请求流

文章图片

二、安装与依赖管理(opm/luarocks)

# 1) 安装 OpenResty(以 Linux 包管理为例)
sudo yum install -y openresty

# 2) 检查版本与 LuaJIT
/usr/local/openresty/bin/openresty -V
/usr/local/openresty/luajit/bin/luajit -v

# 3) 安装常用 lua-resty 库(opm)
/usr/local/openresty/bin/opm get openresty/lua-resty-core
/usr/local/openresty/bin/opm get openresty/lua-resty-lrucache
/usr/local/openresty/bin/opm get openresty/lua-resty-redis
/usr/local/openresty/bin/opm get openresty/lua-resty-http

# 4) 安装第三方库(luarocks)
/usr/local/openresty/luajit/bin/luarocks install lua-resty-openidc
/usr/local/openresty/luajit/bin/luarocks install lua-resty-session

# 5) 模块路径检查
/usr/local/openresty/luajit/bin/luajit -e 'print(package.path)'

命令解释:
- opm get 安装 OpenResty 官方库并与版本兼容。
- luarocks install 适合第三方库;建议生产使用私有镜像与版本锁定。
- openresty -V 检查编译模块,避免缺失 --with-http_ssl_module 等关键选项。

三、常用库与模块示例(含配置与预期效果)

1)lua-resty-lrucache + lua-resty-lock:本地缓存与防击穿

# /usr/local/openresty/nginx/conf/nginx.conf
worker_processes 1;
events { worker_connections 1024; }
http {
  lua_shared_dict lock_dict 10m;

  server {
    listen 8080;

    location /cache {
      content_by_lua_block {
        local lrucache = require "resty.lrucache"
        local lock = require "resty.lock"

        local cache, err = lrucache.new(200)
        if not cache then ngx.say("cache init failed: ", err); return end

        local key = "hot_key"
        local val = cache:get(key)
        if val then ngx.say("L1 hit: ", val); return end

        local l = lock:new("lock_dict", {timeout=2, exptime=5})
        local elapsed, err = l:lock(key)
        if not elapsed then ngx.say("lock failed: ", err); return end

        -- 模拟回源
        val = "value_from_backend"
        cache:set(key, val, 10)

        l:unlock()
        ngx.say("L1 miss, set: ", val)
      }
    }
  }
}

预期效果:首次访问输出 L1 miss,后续 10 秒内输出 L1 hit

2)lua-resty-redis:Redis 连接池与超时

location /redis {
  content_by_lua_block {
    local redis = require "resty.redis"
    local red = redis:new()
    red:set_timeouts(1000, 1000, 1000) -- 连接/读/写超时ms

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then ngx.say("connect failed: ", err); return end

    local res, err = red:get("foo")
    if err then ngx.say("get failed: ", err); return end

    red:set_keepalive(10000, 100) -- 连接池
    ngx.say("redis foo: ", res)
  }
}

命令解释:set_keepalive 必须执行,避免连接泄漏;set_timeouts 防止慢连接拖垮 worker。

3)lua-resty-http:外部 API 调用

location /http {
  content_by_lua_block {
    local http = require "resty.http"
    local httpc = http.new()
    httpc:set_timeout(2000)

    local res, err = httpc:request_uri("https://httpbin.org/get", {
      method = "GET",
      ssl_verify = false
    })
    if not res then ngx.say("request failed: ", err); return end
    ngx.say(res.status, " ", res.body)
  }
}

4)ngx_brotli:压缩模块(需编译)

# 源码编译示例(OpenResty + brotli)
cd /usr/local/src
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli && git submodule update --init

# 进入 OpenResty 源码目录编译
./configure --add-module=/usr/local/src/ngx_brotli
make && make install
# 启用 brotli
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/javascript application/json;

四、第三方模块实践示例
1)lua-resty-openidc:OIDC 鉴权(简化版)

location /secure {
  access_by_lua_block {
    local openidc = require "resty.openidc"
    local opts = {
      discovery = "https://issuer/.well-known/openid-configuration",
      client_id = "demo",
      client_secret = "secret",
      redirect_uri = "https://example.com/secure"
    }
    local res, err = openidc.authenticate(opts)
    if err then ngx.status=401; ngx.say("auth failed: ", err); return ngx.exit(401) end
  }
  proxy_pass http://backend;
}

2)ngx_http_geoip2_module:地域分流

geoip2 /etc/nginx/GeoLite2-City.mmdb {
  $geoip2_country_name country names en;
}
map $geoip2_country_name $upstream {
  default backend_default;
  China backend_cn;
}
upstream backend_cn { server 10.0.0.10:8080; }
upstream backend_default { server 10.0.0.11:8080; }

server {
  listen 8080;
  location / { proxy_pass http://$upstream; }
}

五、关键配置与命令解释

# 共享字典用于锁与缓存
lua_shared_dict lock_dict 10m;
lua_shared_dict cache_dict 100m;

# 统一超时
lua_socket_connect_timeout 1s;
lua_socket_read_timeout 2s;
lua_socket_send_timeout 2s;

# Lua 代码路径
lua_package_path "/usr/local/openresty/lualib/?.lua;;";

说明:共享字典过小会造成 evictions 增高;超时统一避免慢请求累积。

六、常见故障排查
1)库找不到

# 现象:module 'resty.http' not found
# 处理:检查 package.path
/usr/local/openresty/luajit/bin/luajit -e 'print(package.path)'
# 确认 /usr/local/openresty/lualib/?.lua 在路径中

2)连接池泄漏

# 现象:Redis 连接数激增
# 排查:确认 set_keepalive 是否执行
grep -R "set_keepalive" -n /usr/local/openresty/nginx/lua

3)共享字典溢出

# 查看 OpenResty 共享字典指标(需 stub_status 或自定义统计)
curl -s http://127.0.0.1/status | grep evictions

七、练习
1)使用 lua-resty-redis/cache 接口的回源结果缓存到 Redis,设置 30 秒 TTL。
2)为 /secure 接口添加 lua-resty-session,将登录态保存在 Redis。
3)启用 lua-resty-logger-socket 将访问日志投递到本地 UDP 5514 端口,并用 nc -lu 5514 验证输出。

八、最小可运行验证

# 启动 OpenResty
sudo /usr/local/openresty/bin/openresty -p /usr/local/openresty/nginx -c conf/nginx.conf
# 验证
curl -s http://127.0.0.1:8080/cache
curl -s http://127.0.0.1:8080/http

预期:/cache 首次为 miss,再次为 hit;/http 返回 httpbin 状态与内容。