使用Nginx post_action配置 & filter模組中新增定時器 導致的coredump
開發環境使用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,才導致了之前的主請求定時器沒有銷燬。但是主請求釋放了,定時器的記憶體已經非法了。