Gearman是最早由LiveJournal內部開發並使用的一個通用並行任務調度架構,允許不同語言直接通過非常簡單的方式進行互操作。前台提交工作任務(Task)和參數,由後台背景工作處理序(Worker)完成實際工作。
例如前台提交使用者需要進行渲染的圖片,由Gearman調度到後台提供渲染服務的背景工作處理序,在完成工作後返回結果給前台進行展示。提交工作和完成工作的代碼只需要通過預先協商好的參數格式進行互動,具體任務的調度、負載平衡、可靠性等,由Gearman伺服器來確保。而針對大規模應用,可以很容易進行多路節點的叢集部署。
在正式對外發布後,Danga Interactive用C重寫了整個伺服器代碼,支援PHP, Perl, Python等常見指令碼用戶端,支援用memcached,
sqlite, postgresql, tokyocabinet等作為任務持久化隊列,基本上來說是便宜量又足。
執行緒模式
在大規模使用的時候,需要針對應用類型進行參數設定,以使Gearman的效能達到最優,這首先應該瞭解Gearman的執行緒模式。
為確保具備對海量任務調度的支援能力,Gearman毫無懸念的選擇libevent作為網路操作支撐庫。因此Gearman的伺服器Gearmand提供了三類線程角色:
連接埠監聽和管理線程,接受新串連請求並將之交給IO線程,1個
IO線程,完成實際的任務處理,包括命令解析,隊列操作等,n個
處理線程,完成內部資料結構的管理,無系統調用儘可能簡單,1個
其中第1, 3種線程對全域處理效能沒有直接影響,雖然處理線程有可能成為瓶頸,但他的工作足夠簡單消耗可忽略不計,因此我們的效能調優主要目標是在IO線程的數量。
對每個IO線程來說,它都會有一個libevent的執行個體;所有Gearman的操作會以非同步任務方式提交到處理線程,並由IO線程擷取完成實際操作,因此IO線程的數量是與可平行處理任務數成正比。Gearmand 提供 -t 參數調整總IO線程數,需要使用 libevent 1.4 以上版本提供多線程支援。
進程控制代碼數
另外一個影響大規模部署的是進程控制代碼數,Gearman會為每一個註冊的Worker分配一個fd(檔案描述符),而這個fd的總數是受使用者限制的,可以使用 ulimit -n 命令查看當前限制
flier@debian:~$ ulimit -n
1024
flier@debian:~$ ulimit -HSn 4096 // 設定進程控制代碼數的最大軟硬限制
4096
也就是說gearman預設配置下,最多允許同時有小於1024個worker註冊上來,fd用完之後的Worker和Client會出現連線逾時或無響應等異常情況。因此,發生類似情況時,我們應首先檢查 /proc/[PID]/fd/ 目錄下的數量,是否已經超過 ulimit -n 的限制,並根據需要進行調整。而全系統的開啟檔案設定,可以參考 /proc/sys/fs/file-max 檔案,並通過 sysctl -w fs.file-max=[NUM] 進行修改。
flier@debian:~$ cat /proc/sys/fs/file-max
24372
flier@debian:~# sysctl -w fs.file-max=100000
100000
更詳細的設定請參考 Linux increase the maximum number of open files or file descriptors。
Gearmand 本身也提供了調整控制代碼計數限制的功能,啟動時則可以通過 –file-descriptors 參數指定,但非特權進程不能設定超過soft limit的數額。
-f, –file-descriptors=FDS Number of file descriptors to allow for the process
(total connections will be slightly less). Default
is max allowed for user.
The soft limit is the value that the kernel enforces for the corresponding resource. The hard limit acts as a
ceiling for the soft limit: an unprivileged process may only set its soft limit to a value in the range from 0
up to the hard limit, and (irreversibly) lower its hard limit. A privileged process (under Linux: one with the
CAP_SYS_RESOURCE capability) may make arbitrary changes to either limit value.
輪詢調度
此外,Gearmand 還提供了一些增強任務調度公平性的參數,例如 0.13 裡面新增的 round-robin 模式,允許將任務公平的調度到多個 Worker,而不是用預設按 Worker 註冊函數的順序進行調度,避免工作過於集中在少數裝置上。
-R, –round-robin Assign work in round-robin order per
workerconnection. The default is to assign work in
the order of functions added by the worker.
Gearmand 內部通過一個 Worker 隊列,在 RR 模式下動態調整 Worker 的調度次序。
if (server_con->thread->server->flags.round_robin) { GEARMAN_LIST_DEL(server_con->worker, server_worker, con_) _server_con_worker_list_append(server_con->worker_list, server_worker); ++server_con->worker_count; if (server_con->worker_list == NULL) { server_con->worker_list= server_worker; } }
受限喚醒
而通過 –worker-wakeup 參數,則可以指定收到任務時,需要喚醒多少個 Worker 進行處理,避免在 Worker 數量非常大時,發送大量不必要的 NOOP 報文,試圖喚醒所有的 Worker。
-w, –worker-wakeup=WORKERS Number of workers to wakeup for each job received.
The default is to wakeup all available workers.
根據 Gearman 協議設計, Worker 如果發現隊列中沒有任務需要處理,是可以通過發送 PRE_SLEEP 命令給伺服器,告知說自己將進入睡眠狀態。在這個狀態下,Worker 不會再去主動抓取任務,只有伺服器發送 NOOP 命令喚醒後,才會恢複正常的任務抓取和處理流程。因此 Gearmand 在收到任務時,會去嘗試喚醒足夠的 Worker
來抓取任務;此時如果 Worker 的總數超過可能的任務數,則有可能產生驚群效應。
/* Queue NOOP for possible sleeping workers. */ if (job->function->worker_list != NULL) { worker= job->function->worker_list; noop_sent= 0; do { if (worker->con->options & GEARMAN_SERVER_CON_SLEEPING && !(worker->con->options & GEARMAN_SERVER_CON_NOOP_SENT)) { ret= gearman_server_io_packet_add(worker->con, false, GEARMAN_MAGIC_RESPONSE, GEARMAN_COMMAND_NOOP, NULL); if (ret != GEARMAN_SUCCESS) return ret; worker->con->options|= GEARMAN_SERVER_CON_NOOP_SENT; noop_sent++; } worker= worker->function_next; } while (worker != job->function->worker_list && (job->server->worker_wakeup == 0 || noop_sent < job->server->worker_wakeup)); job->function->worker_list= worker; }
除此之外,針對應用特點合理使用持久化隊列,在大並發任務量的情況下對效能也會有直接影響。
歸根結底,需要根據自己的應用情境,合理設計一些測試案例和自動化指令碼,通過實際的運行狀態進行參數調整。