log_by_lua中co-socket替代方案触发404错误排查求助
问题排查:OpenResty定时器调用后端端点返回404的原因及解决
核心问题本质
你遇到的404并非URL本身错误,而是定时器异步运行环境与请求上下文环境的差异,导致实际请求的参数、头信息或路由匹配逻辑和直接curl请求不一致。
可能原因及对应排查/解决方法
1. 请求头缺失关键字段
直接curl时你可能手动携带了Host、Content-Type等必要头,但定时器发起的请求默认不包含这些。部分后端服务依赖Host头做虚拟主机路由匹配,缺失会导致404。
- 解决:在定时器的HTTP请求中显式添加对应头,示例:
local http = require "resty.http" local httpc = http.new() local res, err = httpc:request_uri("http://backend-service/unban-refresh-token", { method = "POST", headers = { ["Host"] = "your-production-backend-host", -- 和curl时使用的Host完全一致 ["Content-Type"] = "application/json" }, body = ngx.encode_json({ refresh_token = refresh_token }) })
- 验证:在后端服务日志中查看定时器请求的完整头信息,和curl请求的头做对比,定位缺失项。
2. ngx.ctx变量在定时器中失效
你提到在access_by_log阶段保存了ngx.ctx字段,但定时器是异步触发的,原请求的上下文已经被回收,导致传递的参数(比如旧refresh_token)为空或错误,后端因找不到对应资源返回404。
- 解决:创建定时器时显式传递参数,不要依赖
ngx.ctx,示例:
local function unban_token(premature, refresh_token) if premature then return end -- 打印参数确认有效性 ngx.log(ngx.INFO, "Token to unban: ", refresh_token) -- 后续HTTP请求逻辑 end -- 在log阶段调用定时器时直接传入参数 ngx.timer.at(0, unban_token, ngx.ctx.old_refresh_token)
3. DNS解析异常
虽然Pod内curl正常,但OpenResty的定时器可能使用了错误的DNS配置(比如未正确配置resolver指令),导致域名解析到错误的后端IP,看似URL正确实际访问的是无效服务。
- 排查:
- 在定时器中添加DNS解析日志:
local resolver = require "resty.dns.resolver" local r, err = resolver:new({ nameservers = {"kube-dns.kube-system.svc.cluster.local"}, -- K8S集群DNS地址 timeout = 2000, }) local answers, err = r:query("backend-service") if answers then for _, ans in ipairs(answers) do ngx.log(ngx.INFO, "Resolved backend IP: ", ans.address) end end- 对比curl时解析的IP,确认是否一致。
- 确保OpenResty配置文件中添加了正确的
resolver指令。
4. 后端路由/权限拦截
部分后端框架的路由匹配依赖请求上下文头(如X-Forwarded-*),或端点开启了权限校验,定时器发起的内部请求因缺少这些信息被拦截,部分服务会返回404而非401/403。
- 解决:
- 若依赖上下文头:在定时器请求中模拟前端请求的对应头(如
X-Forwarded-Proto: https)。 - 若有权限拦截:修改后端配置,允许该端点接受无授权的内部请求,或在定时器请求中添加内部认证头(如
X-Internal-Auth: xxx)并配置白名单。
- 若依赖上下文头:在定时器请求中模拟前端请求的对应头(如
快速验证步骤
在定时器的HTTP请求前后,打印完整请求信息到OpenResty日志,和curl请求的后端日志做逐字段对比:
local req_body = ngx.encode_json({ refresh_token = refresh_token }) ngx.log(ngx.INFO, "Timer request URL: http://backend-service/unban-refresh-token") ngx.log(ngx.INFO, "Timer request headers: ", require("cjson").encode({ ["Host"] = "your-production-backend-host", ["Content-Type"] = "application/json" })) ngx.log(ngx.INFO, "Timer request body: ", req_body)
内容的提问来源于stack exchange,提问作者Arman Jasuja
相关产品推荐
相关产品推荐

