使用gearman作為非同步訊息處理中介軟體是卻沒有想象中的順利。我們多次發現gearmand程序會將php的請求hold住,不做任何響應,即便php在gearmanclient發起連線時設定了連線超時時間,也不會超時。這對於php的工作方式來說,是很危險的。
對於這個問題,我們問了google許多次,自己也在伺服器上跟蹤了許久,終於將問題定位為在gearman的worker程序在通過 addfunction 方法註冊任務時,如果使用了timeout引數,那麼就會復現這個問題,但這個問題的復現是隨機發生的。
首先,gearmand程序正常工作時,通過pstack檢視其工作執行緒狀態如下:
thread 7 (thread 0x40bc2940 (lwp 8627)):
#0 0x00007fec366d1e46 in poll () from /lib64/libc.so.6
#1 0x00007fec386be7a7 in current_epoch_handler () from /proc/8626/exe
#2 0x00007fec37dc54a7 in start_thread () from /lib64/libpthread.so.0
#3 0x00007fec366dac2d in clone () from /lib64/libc.so.6
thread 6 (thread 0x415c3940 (lwp 8628)):
#0 0x00007fec366db018 in epoll_wait () from /lib64/libc.so.6
#1 0x00007fec37767ff0 in epoll_dispatch ()
#2 0x00007fec3775b3e1 in event_base_loop ()
#3 0x00007fec386b7848 in _thread () from /proc/8626/exe
#4 0x00007fec37dc54a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00007fec366dac2d in clone () from /lib64/libc.so.6
thread 5 (thread 0x41fc4940 (lwp 8629)):
#0 0x00007fec37dc9b99 in pthread_cond_wait@@glibc_2.3.2 ()
#1 0x00007fec386b6269 in _proc () from /proc/8626/exe
#2 0x00007fec37dc54a7 in start_thread () from /lib64/libpthread.so.0
#3 0x00007fec366dac2d in clone () from /lib64/libc.so.6
thread 4 (thread 0x429c5940 (lwp 8630)):
#0 0x00007fec366db018 in epoll_wait () from /lib64/libc.so.6
#1 0x00007fec37767ff0 in epoll_dispatch ()
#2 0x00007fec3775b3e1 in event_base_loop ()
#3 0x00007fec386b7848 in _thread () from /proc/8626/exe
#4 0x00007fec37dc54a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00007fec366dac2d in clone () from /lib64/libc.so.6
thread 3 (thread 0x433c6940 (lwp 8631)):
#0 0x00007fec366db018 in epoll_wait () from /lib64/libc.so.6
#1 0x00007fec37767ff0 in epoll_dispatch ()
#2 0x00007fec3775b3e1 in event_base_loop ()
#3 0x00007fec386b7848 in _thread () from /proc/8626/exe
#4 0x00007fec37dc54a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00007fec366dac2d in clone () from /lib64/libc.so.6
thread 2 (thread 0x43dc7940 (lwp 8632)):
#0 0x00007fec366db018 in epoll_wait () from /lib64/libc.so.6
#1 0x00007fec37767ff0 in epoll_dispatch ()
#2 0x00007fec3775b3e1 in event_base_loop ()
#3 0x00007fec386b7848 in _thread () from /proc/8626/exe
#4 0x00007fec37dc54a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00007fec366dac2d in clone () from /lib64/libc.so.6
thread 1 (thread 0x7fec3863e6f0 (lwp 8626)):
#0 0x00007fec366db018 in epoll_wait () from /lib64/libc.so.6
#1 0x00007fec37767ff0 in epoll_dispatch ()
#2 0x00007fec3775b3e1 in event_base_loop ()
#3 0x00007fec386b4d5d in gearmand_run () from /proc/8626/exe
#4 0x00007fec386a3307 in main () from /proc/8626/exe
可以看到,正常工作時,gearmand程序中會有7個工作執行緒。而當工作不正常時,也就是php請求被hold住時,可以看到這裡面的執行緒數是少於7個的。
這個問題在起官方**的issue中也有人提起。
使用的gearmand的版本是1.1.8依然有這個問題。追溯到0.32版本發現無法重現這個問題,而0.33至1.1.8都存在此問題。後來閱讀了原始碼以及檢視了相關changelog發現0.33之後的版本增加了對worker的timeout處理。而從上面官網的一些使用者的反饋來看,也確實和addfunction的timeout引數有關。於是我們先將addfunction時的timeout引數設定為0。問題果然不在重現,那麼這個timeout到底為什麼會導致這個問題呢?這需要進一步分析gearmand的原始碼,分析的重點如下:
首先是執行緒數為什麼會減少?
libgearman-server/gearmand_thread.cc: _thread方法中
static void *_thread(void *data)
(void)gearmand_initialize_thread_logging(buffer);
gearmand_debug("entering thread event loop");
ex s;
int r ;
if ( (r = event_base_loop(thread->base, 0)) == -1)
cout<<"r="《在問題發生的時候,會導致event_base_loop執行完畢,導致退出。檢視了libevent的相關資料,可以看到。libevent 1.4.x是非執行緒安全的,不能跨執行緒執行event_add。而libevent 2.0.x通過執行緒鎖做到了執行緒安全,可以通過執行evthread_use_pthreads跨執行緒執行event_add。而gearmand的**中沒有使用evthread_use_pthreads。而gearmand的官方**中,應該就是對於timeout特性進行處理時發生的問題。我們可以看到如下原始碼中
libgearman-server/connection.cc:gearman_server_con_add_job_timeout 方法中
if (con->timeout_event == null)
timeout_set(con->timeout_event, _server_job_timeout, job);
event_base_set(dcon->thread->base, con->timeout_event);
}
gearman_server_con_add_job_timeout 這個方法,是在main thread中執行的,而下面這條命令
event_base_set(dcon->thread->base, con->timeout_event);操作的是dcon->thread 物件上的event base物件,因此屬於跨執行緒操作了event_base物件。
我們隨後對gearman_server_con_add_job_timeout這個方法中的event_base_set呼叫進行了修改。將dcon->thread->base物件改到了_global_gearmand->base。
非同步訊號安全和執行緒安全
2011 12 24 20 10 54 分類 linux 字型大小 訂閱 問題源自於apue中stevens老先生有關執行緒安全函式的介紹,stevens有曰 如果乙個函式對於多執行緒來說是重入的,則說這個函式是執行緒安全的,但這並不能說明對訊號處理程式來說也是重入的。也就是說訊號安全重入函式要求要...
AsyncTask 非同步處理
1,object,用於指定doinbackground的引數 2,integer,用於指定onprogressupdate的引數 3,uri,用於指定doinbackground的返回型別和onpostexecute的引數型別 public class updatetask extends asyn...
AsyncTask非同步處理
非同步處理的目的 完成任務的同時不阻塞主線程 ui執行緒 涉及handler looper message thread四個物件。實現非同步的流程 主線程啟動thread 子執行緒執行並生成message looper獲取message並傳遞給handler handler逐個獲取message並進...