ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Docker构建OpenResty+Lua+MySQL+Redis集群高性能网关实践

Docker构建OpenResty+Lua+MySQL+Redis集群高性能网关实践 1. 项目概述与核心价值最近在折腾一个需要高性能网关和动态配置管理的项目核心需求是网关能根据业务规则实时调整路由策略并且这些策略的配置信息需要从数据库读取同时为了扛住高并发缓存是必不可少的。传统的做法可能是用Nginx搭个反向代理后面挂一堆脚本或者用其他语言写个中间件去查数据库和缓存但这样架构复杂性能损耗也大。我第一时间就想到了OpenResty它把Nginx和LuaJIT打包在一起让你可以直接用Lua脚本在Nginx的各个处理阶段“嵌入”业务逻辑性能几乎无损。但问题来了开发环境、测试环境、生产环境怎么保持一致依赖的OpenResty版本、Lua模块、MySQL客户端、Redis客户端每台机器装一遍不仅麻烦还容易出幺蛾子。这时候Docker的价值就凸显出来了。把OpenResty、Lua环境、MySQL客户端库、Redis Cluster客户端库全部打包进一个镜像里一次构建处处运行。这不仅仅是环境隔离更是将整个应用的运行时环境包括复杂的依赖关系变成了一个可版本化、可复制的标准件。你本地开发用的镜像和最终上线的镜像可以做到完全一致彻底告别“在我机器上是好的”这种经典问题。这个项目标题“docker构建openrety lua mysql rediscluster”本质上就是在打造一个为现代Web应用特别是微服务网关、API聚合层、实时业务处理等场景量身定制的、开箱即用的高性能运行时容器。它解决的不仅仅是部署问题更是提升了开发、测试、运维整个生命周期的标准化和效率。2. 整体架构设计与组件选型考量当我们决定把OpenResty、Lua、MySQL客户端、Redis Cluster客户端塞进一个Docker镜像时这不仅仅是一个简单的软件堆叠而是一个有明确架构意图的设计。我们需要仔细思考每个组件的角色、它们之间的交互方式以及如何在Docker这个沙盒里让它们和谐共处。2.1 核心组件角色与交互逻辑在这个镜像里OpenResty是绝对的核心它不再是单纯的Web服务器而是一个可编程的应用网关/服务器。Lua是赋予其灵魂的脚本语言通过ngx_lua模块我们可以在请求处理的各个阶段如access_by_lua*,content_by_lua*,log_by_lua*注入业务逻辑。MySQL和Redis Cluster客户端库则是这个灵魂与外部数据世界沟通的桥梁。它们的典型协作流程是这样的一个HTTP请求到达OpenResty。在access阶段Lua脚本可能去Redis Cluster里查询一个用户令牌token的有效性这个查询是毫秒级的快速决定是否放行。通过验证后在content阶段业务逻辑可能需要根据请求参数去MySQL数据库里查询更详细的商品信息或用户配置。最后在生成响应或记录日志时可能又会将一些结果回写到Redis Cluster中用作缓存或发布消息。整个过程中Lua脚本是粘合剂和控制器。它调用lua-resty-mysql库连接MySQL调用lua-resty-redis库连接Redis Cluster。这里的“连接”通常指的是使用连接池每个工作进程维护一组到后端服务的连接避免每次请求都建立新的TCP连接这是高性能的关键。2.2 Docker镜像分层与构建策略直接apt-get install把所有东西装进一个镜像层是最简单的但会制造一个臃肿、层数混乱、难以维护的“巨无霸”。优秀的Docker镜像应该像洋葱一样分层清晰。我们的构建策略可以这样规划基础层选择一个轻量级的Linux发行版作为基础比如debian:bullseye-slim或alpine:latest。Alpine更小但某些库的兼容性可能需要额外处理。Debian系列更通用生态更完整。这里我选择debian:bullseye-slim在体积和兼容性上取个平衡。系统依赖层在这一层安装编译OpenResty和运行Lua模块所需的系统库如gcc,make,libpcre3-dev,libssl-dev,perl等。这是构建环境的准备。OpenResty编译安装层从官网下载指定版本的OpenResty源码包编译并安装到镜像内。比起直接使用某些第三方打包的版本自己编译能更精确地控制包含哪些模块比如确保http_ssl_module、stream_lua_module等被启用。Lua依赖层使用OpenResty自带的包管理工具opm或luarocks安装项目所需的Lua第三方库。重点是lua-resty-mysql和lua-resty-redis。对于Redis Clusterlua-resty-redis本身支持集群模式但需要我们在Lua代码中实现集群节点寻址和重定向逻辑MOVED/ASK。也可以考虑使用封装得更好的lua-resty-redis-cluster库。应用代码与配置层这是最上层也是变化最频繁的一层。将你的Nginx配置文件nginx.conf、Lua脚本文件、项目配置文件等复制进镜像。通过Docker的COPY指令这些内容会形成一个独立的层后续更新代码只需要重建这一层充分利用Docker的缓存机制加速构建。注意务必区分“构建时依赖”和“运行时依赖”。像gcc、make这样的工具只在构建OpenResty时需要最终镜像应该尽可能精简。我们可以使用Docker的“多阶段构建”功能在一个临时镜像中完成编译然后将编译好的可执行文件和库文件复制到一个干净的、只包含运行时依赖的最终镜像中这能显著减小镜像体积。2.3 关键版本与兼容性锁定在Dockerfile里对于关键组件必须明确指定版本号而不是使用latest标签。这是保证构建一致性的生命线。OpenResty: 例如OPENRESTY_VERSION1.21.4.1LuaRocks: 例如LUAROCKS_VERSION3.9.1依赖库在opm或luarocks安装命令中也应尽量指定版本如opm get ledgetech/lua-resty-http 0.16.1网络上的热词如“openresty:1.21.4.1”正好给了我们一个明确的版本参考。锁定版本可以避免因为上游镜像更新导致未知的兼容性问题让你的镜像在任何时候重建都能得到相同的结果。3. 详细Dockerfile解析与实操构建下面我将拆解一个完整的、包含多阶段构建的Dockerfile并解释每一行关键指令背后的意图和注意事项。这个Dockerfile的目标是构建一个包含OpenResty、LuaRocks、lua-resty-mysql和lua-resty-redis的优化镜像。3.1 多阶段构建Dockerfile详解# 第一阶段构建阶段使用一个完整的构建环境 FROM debian:bullseye-slim AS builder # 定义构建参数方便版本管理 ARG OPENRESTY_VERSION1.21.4.1 ARG LUAROCKS_VERSION3.9.1 # 安装编译OpenResty所需的系统依赖 RUN apt-get update \ apt-get install -y --no-install-recommends \ build-essential \ ca-certificates \ curl \ libpcre3-dev \ libssl-dev \ zlib1g-dev \ perl \ rm -rf /var/lib/apt/lists/* # 清理apt缓存减小本层体积 # 下载并解压OpenResty源码 WORKDIR /tmp RUN curl -fSL https://openresty.org/download/openresty-${OPENRESTY_VERSION}.tar.gz -o openresty.tar.gz \ tar -xzf openresty.tar.gz \ rm openresty.tar.gz # 编译并安装OpenResty到指定目录 WORKDIR /tmp/openresty-${OPENRESTY_VERSION} RUN ./configure --prefix/usr/local/openresty \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-threads \ --with-file-aio \ --with-http_addition_module \ --with-http_gzip_static_module \ --with-http_sub_module \ --with-stream \ --with-stream_ssl_module \ --with-stream_realip_module \ --with-stream_ssl_preread_module \ --without-http_redis2_module \ # 明确排除不用的模块 --without-http_redis_module \ --with-luajit \ --with-cc-opt-O2 \ make -j$(nproc) \ # 使用多核并行编译加速构建 make install # 安装LuaRocks (OpenResty的Lua包管理器) WORKDIR /tmp RUN curl -fSL https://luarocks.org/releases/luarocks-${LUAROCKS_VERSION}.tar.gz -o luarocks.tar.gz \ tar -xzf luarocks.tar.gz \ rm luarocks.tar.gz WORKDIR /tmp/luarocks-${LUAROCKS_VERSION} RUN ./configure --prefix/usr/local/openresty/luajit \ --with-lua/usr/local/openresty/luajit \ --lua-suffixjit \ --with-lua-include/usr/local/openresty/luajit/include/luajit-2.1 \ make build \ make install # 使用LuaRocks为OpenResty安装必要的Lua库 # 注意这里使用--tree参数将库安装到OpenResty的site目录避免污染系统路径 RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-mysql \ /usr/local/openresty/luajit/bin/luarocks install lua-resty-redis # 第二阶段运行阶段创建一个干净的最终镜像 FROM debian:bullseye-slim # 维护者信息可选 LABEL maintaineryour-emailexample.com # 仅安装OpenResty运行时的必要依赖 RUN apt-get update \ apt-get install -y --no-install-recommends \ libpcre3 \ libssl1.1 \ zlib1g \ ca-certificates \ rm -rf /var/lib/apt/lists/* # 从构建阶段拷贝编译好的OpenResty和LuaRocks COPY --frombuilder /usr/local/openresty /usr/local/openresty # 将OpenResty的可执行文件目录加入PATH环境变量 ENV PATH/usr/local/openresty/nginx/sbin:/usr/local/openresty/bin:$PATH # 创建必要的目录 RUN mkdir -p /var/log/nginx /var/cache/nginx /usr/local/openresty/nginx/conf/conf.d # 拷贝自定义的Nginx配置和Lua脚本 COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf COPY conf.d/ /usr/local/openresty/nginx/conf/conf.d/ COPY lua/ /usr/local/openresty/nginx/lua/ # 暴露端口根据你的nginx.conf中的监听端口修改 EXPOSE 80 443 # 以非root用户运行安全最佳实践 RUN useradd -r -s /bin/false nginxuser \ chown -R nginxuser:nginxuser /var/log/nginx /var/cache/nginx USER nginxuser # 启动命令 STOPSIGNAL SIGQUIT CMD [/usr/local/openresty/nginx/sbin/nginx, -g, daemon off;]3.2 配套Nginx与Lua脚本配置要点Dockerfile准备好了但要让OpenResty真正工作起来还需要精心配置nginx.conf和Lua脚本。nginx.conf 关键配置片段# 在http块中设置Lua包路径让nginx能找到我们安装的库 http { lua_package_path /usr/local/openresty/lualib/?.lua;;; lua_package_cpath /usr/local/openresty/lualib/?.so;;; # 初始化共享字典用于Lua层共享数据如缓存连接池信息 lua_shared_dict my_shared_data 10m; # 配置upstream这里可以是你的业务后端服务器 upstream backend { server backend_service:8080; } server { listen 80; server_name localhost; # 一个示例location演示集成Lua location /api/test { # 使用content_by_lua_block直接内嵌Lua代码适合简单逻辑 content_by_lua_block { local mysql require resty.mysql local redis require resty.redis ngx.say(Hello, OpenResty with MySQL Redis Cluster!) -- 这里可以编写连接数据库和缓存的逻辑 } } # 更常见的做法是调用外部Lua脚本文件便于管理和复用 location /api/user { access_by_lua_file /usr/local/openresty/nginx/lua/auth_check.lua; content_by_lua_file /usr/local/openresty/nginx/lua/user_info.lua; log_by_lua_file /usr/local/openresty/nginx/lua/access_log.lua; } # 反向代理到后端服务 location / { proxy_pass http://backend; proxy_set_header Host $host; } } }Lua脚本示例 (lua/user_info.lua)local mysql require resty.mysql local redis require resty.redis -- 从MySQL获取用户信息 local function get_user_from_mysql(user_id) local db, err mysql:new() if not db then ngx.log(ngx.ERR, failed to instantiate mysql: , err) return nil end db:set_timeout(1000) -- 1秒超时 local ok, err, errcode, sqlstate db:connect{ host your_mysql_host, port 3306, database your_db, user your_user, password your_password, charset utf8mb4, max_packet_size 1024 * 1024, } if not ok then ngx.log(ngx.ERR, failed to connect to mysql: , err, : , errcode, , sqlstate) return nil end -- 使用参数化查询防止SQL注入 local res, err, errcode, sqlstate db:query(SELECT name, email FROM users WHERE id .. ngx.quote_sql_str(user_id)) if not res then ngx.log(ngx.ERR, bad result: , err, : , errcode, : , sqlstate, .) db:close() return nil end -- 将连接放回连接池而非关闭 local ok, err db:set_keepalive(10000, 100) -- 连接池保留10秒最大100个连接 if not ok then ngx.log(ngx.ERR, failed to set keepalive: , err) db:close() end return res[1] -- 返回第一条记录 end -- 从Redis Cluster获取缓存简化示例实际集群需处理MOVED/ASK local function get_user_from_redis(user_id) local red redis:new() red:set_timeout(1000) -- 注意lua-resty-redis需要自己实现集群逻辑。这里连接一个节点。 -- 生产环境应考虑使用lua-resty-redis-cluster或自己封装集群逻辑。 local ok, err red:connect(redis_cluster_node1, 6379) if not ok then ngx.log(ngx.ERR, failed to connect to redis: , err) return nil end local cache_key user: .. user_id local user_data, err red:get(cache_key) if not user_data then ngx.log(ngx.ERR, failed to get key: , err) red:close() return nil end if user_data ngx.null then user_data nil -- 缓存未命中 end local ok, err red:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, failed to set keepalive for redis: , err) red:close() end return user_data end -- 主逻辑 local user_id ngx.var.arg_id or 1 local user_info -- 1. 先查缓存 local cached get_user_from_redis(user_id) if cached then ngx.say(Data from Redis Cache: , cached) return end -- 2. 缓存未命中查数据库 user_info get_user_from_mysql(user_id) if user_info then ngx.say(Data from MySQL: , require(cjson).encode(user_info)) -- 3. 将结果写入缓存异步或同步根据业务决定 -- 这里省略写入Redis的代码 else ngx.status ngx.HTTP_NOT_FOUND ngx.say(User not found) end3.3 镜像构建与运行命令有了Dockerfile和配置文件构建和运行就很简单了。假设你的项目目录结构如下your-project/ ├── Dockerfile ├── nginx.conf ├── conf.d/ │ └── (其他自定义配置) └── lua/ ├── auth_check.lua ├── user_info.lua └── ...构建镜像# 在项目根目录执行 docker build -t my-openresty-app:1.0 .-t参数给镜像打上标签。这个构建过程会利用缓存如果之前的层没有变化构建速度会非常快。运行容器docker run -d \ --name my-openresty-container \ -p 8080:80 \ -v $(pwd)/nginx-custom.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro \ -v $(pwd)/lua-scripts:/usr/local/openresty/nginx/lua:ro \ --network my_network \ my-openresty-app:1.0-d: 后台运行。--name: 指定容器名称。-p: 端口映射将宿主机的8080端口映射到容器的80端口。-v: 挂载卷。这是非常关键的一步。它允许你在不重建镜像的情况下动态修改Nginx配置和Lua脚本。ro表示只读挂载防止容器内进程意外修改你的宿主机文件。--network: 将容器加入自定义的Docker网络这样它才能通过服务名如your_mysql_host访问到同样在这个网络中的MySQL和Redis Cluster容器。4. 核心难点解析Redis Cluster集成与连接管理在单机Redis上lua-resty-redis用起来很直观。但切换到Redis Cluster后最大的挑战来自于集群的数据分片和节点重定向机制。客户端连接任意一个节点如果操作的key不属于该节点服务端会返回MOVED或ASK错误并告知正确的节点地址。lua-resty-redis是一个基础客户端它不会自动处理这些重定向。4.1 集群客户端方案选型你有几个选择使用封装好的库比如lua-resty-redis-cluster。这个库在lua-resty-redis基础上封装了集群逻辑。它会维护一个集群节点槽位映射表slots map在发起请求前先计算key对应的槽位和节点直接连接正确的节点并在收到MOVED响应时更新映射表。这是最推荐的方式省心省力。手动实现集群逻辑基于lua-resty-redis自己写。你需要实现初始时从一个种子节点获取集群的CLUSTER SLOTS信息解析出所有主节点的地址和负责的槽位范围。提供一个包装函数根据key计算CRC16后取模16384得到槽位查找对应的节点。发起请求如果收到MOVED错误解析出新节点地址更新本地映射然后重试请求。处理ASK错误发生在迁移过程中它要求你先向目标节点发送一个ASKING命令然后再重试原命令。处理节点故障和自动发现。这相当复杂除非有极特殊需求否则不要重复造轮子。4.2 使用lua-resty-redis-cluster实战假设我们选择方案一。首先需要在Dockerfile的构建阶段安装这个库# 在Dockerfile的第一阶段builder中安装完基础库后添加 RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-redis-cluster然后在Lua代码中它的使用方式与单机版有所不同local redis_cluster require resty.rediscluster local config { name my_redis_cluster, -- 集群名称 serv_list { -- 集群种子节点列表至少写两个以防一个挂掉 {ip redis-node-1, port 6379}, {ip redis-node-2, port 6379}, {ip redis-node-3, port 6379}, }, keepalive_timeout 60000, -- 连接池空闲超时毫秒 keepalive_cons 1000, -- 连接池大小 connection_timout 1000, -- 连接超时 max_redirection 5, -- 最大重定向次数 -- auth your_password, -- 如果集群有密码 } local red_c, err redis_cluster:new(config) if not red_c then ngx.log(ngx.ERR, failed to create redis cluster: , err) return end -- 使用方式与普通redis对象类似但库内部会处理重定向 local user_data, err red_c:get(user: .. user_id) if not user_data then ngx.log(ngx.ERR, failed to get key from cluster: , err) else ngx.say(Got data from Redis Cluster: , user_data) end -- 记得在请求结束时将连接放回连接池对于长连接场景 -- 对于短连接库会自动管理。但显式调用set_keepalive是好的实践。 local ok, err red_c:set_keepalive() if not ok then ngx.log(ngx.ERR, failed to set keepalive: , err) end实操心得lua-resty-redis-cluster在初始化时会通过种子节点获取整个集群的拓扑信息。务必确保serv_list中的种子节点是可达的并且最好是集群中稳定的主节点。在生产环境中建议将这个列表配置在外部如环境变量或配置中心而不是硬编码在Lua脚本里。4.3 MySQL与Redis连接池最佳实践在高并发场景下为每个请求创建新的数据库连接是灾难性的。必须使用连接池。MySQL连接池 (lua-resty-mysql):如前面代码所示关键在db:set_keepalive(max_idle_timeout, pool_size)。max_idle_timeout: 连接在池中空闲的最大时间毫秒超时后会被关闭。设置太短会导致频繁建连太长可能占用资源。根据流量模式调整比如3000030秒或6000060秒。pool_size: 每个Nginx工作进程维护的连接池大小。这个大小不是全局的而是每个进程独立的。如果Nginx配置了worker_processes 4;且pool_size100那么最大可能同时存在400个数据库连接。需要根据数据库的max_connections设置来合理规划。Redis Cluster连接池 (lua-resty-redis-cluster):该库的连接池是内置的通过keepalive_cons和keepalive_timeout配置。原理是库内部为每个集群节点维护一个连接池。需要注意的是由于集群节点可能很多总连接数 节点数 ×keepalive_cons× Nginx工作进程数。要防止连接数膨胀。一个关键的坑连接池与请求边界。OpenResty的连接池是与当前请求的cosocket非阻塞socket绑定的。务必确保在Lua代码执行路径的末尾或在ngx.on_abort处理中调用set_keepalive。如果因为异常提前return而漏掉了这个连接就不会被回收到池里而是被直接关闭造成资源浪费和连接泄漏。好的习惯是使用pcall包装数据库操作并在finally逻辑里确保连接被妥善处理。5. 性能调优、监控与问题排查镜像跑起来了业务逻辑也通了接下来就要让它跑得又快又稳。OpenResty的性能调优涉及多个层面。5.1 OpenResty与Docker性能调优要点Nginx Worker配置在nginx.conf的main上下文中调整。worker_processes auto; # 通常设置为CPU核心数 worker_rlimit_nofile 65535; # 每个worker能打开的文件描述符数量需要大于worker_connections events { worker_connections 4096; # 每个worker的最大连接数 use epoll; # Linux下高性能I/O模型 multi_accept on; }在Docker中需要确保容器的ulimit设置足够高以支持worker_rlimit_nofile。可以在docker run时使用--ulimit nofile65535:65535来设置。Lua代码缓存一定要开启Lua代码缓存否则每次请求都会重新加载和编译Lua脚本性能极差。http { lua_code_cache on; # 生产环境必须为 on ... }在开发时可以设置为off以便热重载脚本但上线前务必改回on。共享字典大小lua_shared_dict定义的内存区域是所有worker共享的用于缓存、计数器等。根据用途合理分配大小过小会导致频繁淘汰或写入失败。lua_shared_dict my_cache 100m; # 缓存 lua_shared_dict my_locks 1m; # 锁 lua_shared_dict my_counters 10m; # 计数器Docker容器资源限制使用docker run的-m、--cpus等参数为容器分配合理的内存和CPU资源防止单个容器耗尽宿主机资源。5.2 监控与日志配置“看不见的问题才是大问题”。必须建立有效的监控。OpenResty状态监控启用ngx_http_stub_status_module模块我们编译时已包含。location /nginx_status { stub_status on; access_log off; allow 172.17.0.0/16; # 只允许Docker内部网络访问 deny all; }访问这个端点可以得到活跃连接数、请求统计等信息可以集成到Prometheus中。Lua脚本日志使用ngx.log(ngx.ERR, ...)打印错误日志使用ngx.log(ngx.INFO, ...)打印信息日志。日志级别在nginx.conf中通过error_log指令控制。建议将日志输出到标准输出/错误流这样Docker可以收集。error_log /dev/stderr info; # 将错误及以上级别日志打到stderr access_log /dev/stdout main; # 访问日志打到stdout然后通过docker logs命令或日志驱动如json-file,journald,syslog来收集容器日志。应用性能监控(APM)可以集成像openresty-systemtap-toolkit这样的工具进行更细粒度的性能剖析或者在Lua代码中关键位置打点记录耗时输出到日志或专门的监控系统。5.3 常见问题与排查清单在实际部署和运行中你几乎一定会遇到下面这些问题。这里提供一个速查清单问题现象可能原因排查步骤与解决方案容器启动失败提示nginx: [emerg] unknown directive lua_package_pathOpenResty未正确安装或ngx_http_lua_module未编译进去。1. 进入容器检查/usr/local/openresty/nginx/sbin/nginx -V查看输出是否包含--with-http_lua_module。2. 检查Dockerfile构建日志确认OpenResty编译是否成功。Lua脚本报错module resty.mysql not foundLua模块路径未正确设置或模块未安装。1. 检查nginx.conf中lua_package_path和lua_package_cpath是否指向正确的目录通常是/usr/local/openresty/lualib。2. 进入容器到/usr/local/openresty/lualib目录下查看是否存在resty/mysql.lua文件。3. 确认在构建阶段是否成功执行了luarocks install lua-resty-mysql。连接MySQL或Redis超时 (connect timeout)网络不通、地址端口错误、服务未启动、防火墙规则限制。1. 在容器内使用ping或nc -zv测试目标主机和端口是否可达。2. 确认MySQL/Redis服务是否正在运行且监听正确端口。3.最重要确认Docker容器网络模式。如果使用默认的bridge需确保端口映射正确或使用--link已废弃或自定义网络--network。推荐使用自定义的Docker网络容器间通过服务名通信。4. 检查Lua代码中的连接超时设置set_timeout是否合理。性能低下请求延迟高1. Lua代码缓存未开启。2. 未使用连接池每次请求新建连接。3. 共享字典竞争激烈。4. 后端数据库/缓存本身慢。1. 确认lua_code_cache on;。2. 检查代码是否在每次操作后都调用了set_keepalive。3. 使用ngx.shared.DICT的get_keys()或通过状态接口观察共享字典使用情况。4. 对数据库和缓存进行慢查询分析。内存使用量持续增长Lua代码存在内存泄漏如全局变量累积、共享字典只增不减、连接未正确关闭。1. 避免在Lua中滥用全局变量使用local变量。2. 为共享字典设置合理的过期策略或清理机制。3. 使用resty.core.shdict模块的free_space方法监控共享内存碎片需OpenResty 1.19.3。4. 确保所有数据库、Redis连接都在finally逻辑或请求结束时被set_keepalive。Redis Cluster操作返回MOVED错误使用了单机版的lua-resty-redis客户端连接集群且未处理重定向。1. 切换到lua-resty-redis-cluster客户端。2. 如果坚持用基础客户端必须自己实现MOVED/ASK错误处理、槽位计算和节点映射更新逻辑。日志中大量worker_connections are not enough并发连接数超过worker_connections设置。1. 增加nginx.conf中events块的worker_connections值。2. 同时增加系统的文件描述符限制ulimit -n和Docker容器的对应限制。最后再分享一个调试小技巧当遇到复杂的Lua逻辑问题时不要只依赖日志。可以在开发环境中临时在Nginx配置里为特定location开启lua_code_cache off;并配合ngx.say()输出中间变量值或者使用resty命令行工具OpenResty自带直接测试你的Lua模块逻辑这比在完整的请求流程中调试要高效得多。
返回列表