开发环境使用nginx + lua;一次使用nginx 的post_action功能,挂在某模块的添加定时器事件的接口上。
首先说下post_action功能,它会在http请求结束时产生一个新请求,产生一个内部跳转。
server {
listen 80;
server_name www.a.com;
location / {
proxy_set_header Host "www.a-upstream.com";
proxy_pass http://127.0.0.1:8000;
post_action @action;
}
location @action {
proxy_set_header Host "www.a-post-action.com";
proxy_pass http://127.0.0.1:8001;
}
}
这样,http://www.a.com/访问结束时,将产生一个内部跳转请求,跳转到@action,访问上游www.a-post-action.com。查看网上资料,这种方式可以用来统计服务器的数据。
假设产生问题的filter模块为A,A的filter函数中,主要完成以下几件事情:
1. 如果没有ctx,则创建ctx;
2. 初始化ctx,ctx中包含一个定时器,定时器回调用来定时统计一个数据;定时器的内存从r->pool中分配;
3. ctx添加cleanup回调;
4.挂载ctx;
在我的测试环境中使用post_action功能,发现访问几次post_action后,产生了coredump信息。
刚开始怀疑r的内存出现了问题,就配置单个worker进程,gdb worker,p了下r的内存是没有任务问题的。这个假设推翻了。
后来有怀疑A模块中定时器的使用方式,查看nginx中其他地方,也是没有问题的。
突然,就给A模块的定时器添加函数和cleanup函数添加了打印信息,分别打印定时器的添加和消费,以及清除事件,发现居然有一个主请求的定时器事情既没有消费,也没有销毁。所以就推断,可能这个定时器使用的内存空间,也就是原来的主请求已经销毁了,内存也非法了。当然后续请求的定时器操作接口遍历时出现了问题。
那么确认下到底什么地方直接退出了定时器的消费或者回收?
进一步添加打印信息,原来是进入cleanup函数中,判断A模块的ctx为空,直接退出了。无法从ctx中获取定时器,并销毁。
进一步通过gdb的watch命令观察A模块的ctx什么时候被清空,发现原来是post_action请求执行前,会首先清空主请求的ctx,才导致了之前的主请求定时器没有销毁。但是主请求释放了,定时器的内存已经非法了。