它是分佈式的程序調用框架,可完成跨語言的相互調 用,適合在後臺運行工做任務。最初是2005年perl版本,2008年發佈C/C++版本。目前大部分源碼都是(Gearmand服務job Server)C++,各個API實現有各類語言的版本。PHP的Client API與Worker API實現爲C擴展,在PHP官方網站有此擴展的中英文文檔。php
client:請求的發起者,工做任務的需求方(能夠是C、PHP、Java、Perl、Mysql udf等等)mysql
Job Server:請求的調度者,負責將client的請求轉發給相應的worker(gearmand服務進程建立)linux
worker:請求的處理者(能夠是C、PHP、Java、Perl等等)git
從上圖能夠看出,Gearman Client API,Gearman Worker API,Gearman Job Server都是由gearman自己提供,咱們在應用中只須要調用便可。目前client與worker api都很豐富。github
通過的測試,結果以下:sql
系統環境:ubuntu-14.0.4 1個CPU 4核 2G內存 (虛擬機)ubuntu
默認啓動:./gearmand -dapi
client.php服務器
worker.php網絡
啓動一個job server實例:job server IP:127.0.0.1 PORT:4730
啓動一個worker: php worker.php
worker註冊reserve函數,將client的job字符串反轉後返回。
client工做任務的消息爲:just test it(12字節)
同步:4100/s
異步:25700/s
memcached內存準持久化的吞吐能力測試
./gearmand -d -q libmemcached —libmemcached-servers=127.0.0.1:11211
client投遞100000個工做任務:16400/s
啓動兩個job server,他們是獨立的服務進程,有各自的內存隊列。當一個job server進程出現故障,另外一個job server能夠正常調度。(worker api與client api能夠完成job server故障的切換)。在任什麼時候候咱們能夠關閉某個worker,即便那個worker正在處理工做任務(Gearman不會讓正在被執行的job丟失的,因爲worker在工做時與Job server是長鏈接,因此一旦worker發生異常,Job server可以迅速感知並從新派發這個異常worker剛纔正在執行的工做)
job server並不主動分派工做任務,而是由worker從空閒狀態喚醒以後到job server主動抓取工做任務。
鬆耦合的接口和無狀態的job,只須要啓動一個worker,註冊到Job server集羣便可。新加入的worker不會對現有系統有任何的影響。
gearman是分佈式的任務分發框架,worker與job server,client與job server通訊基於tcp的socket鏈接。
gearman內置內存隊列,默認狀況隊列最大容量爲300W,能夠配置最大支持2^32-1,即4 294 967 295。
做爲Gearman的核心,Job server的是用C/C++實現的,因爲只是作簡單的任務派發,所以系統的瓶頸不會出在Job server上。
後臺工做任務Background job——時序圖
由圖可知,client提交完job,job server成功接收後返回JOB_CREATED響應以後,client就斷開與job server之間的連接了。後續不管發生什麼事情,client都是不關心的。一樣,job的執行結果client端也沒辦法經過Gearman消息框架 得到。
通常工做任務Non-background job——時序圖
由圖可知,client端在job執行的整個過程當中,與job server端的連接都是保持着的,這也給job完成後job server返回執行結果給client提供了通路。同時,在job執行過程中,client端還能夠發起job status的查詢。固然,這須要worker端的支持的。
對於隊列持久化的問題,是一個值得考慮的問題。持久化必然影響高性能。gearman支持後臺工做任務的持久化,支持drizzle、mysql、memcached的持久化。對於client提交的background job,Job server除了將其放在內存隊列中進行派發以外,還會將其持久化到外部的持久化隊列中。一旦Job server發生問題重啓,外部持久化隊列中的background job將會被恢復到內存中,參與Job server新的派發當中。這保證了已提交未執行的background job不會因爲Job server發生異常而丟失。而且我測試發現若是開啓了持久化,那麼後臺工做任務會先將工做任務寫到持久化介質,而後在入內存隊列,再執行。非後臺工做任務,因爲client與job server是保持長鏈接的狀態,若是工做任務執行異常,client能夠靈活處理,因此無須持久化。
從典型部署結構看出,兩個Job server之間是沒有鏈接的。也就是Job server間是不共享background job的。若是經過讓兩個Job server指向同一個持久化隊列,可讓兩個Job serer互相備份。但實際上,這樣是行不通的。由於Job server只有在啓動時纔會將持久化隊列中的background job轉入到內存隊列。也就是說,Job server1若是宕機且永遠不啓動,Job server2一直正常運行,那麼Job server1宕機前被提交到Job server1的未被執行的background job將永遠都呆在持久化隊列中,得不到執行。另外若是多個job server實例指向同一個持久化隊列,同時重啓多個job server實例會致使持久隊列中的工做任務被屢次載入,從而致使消息重複處理。
採用memcached作後臺工做任務的準持久化隊列,最好memcached和job server在內網的不一樣機器。兩個機器的兩個服務同時掛掉的可能性比較小,同時也保證了高性能。並且memcached應該爲兩個相互獨立實例,防止其上述的gearman框架中的問題。咱們能夠作一個監控腳本,若是某個job server異常退出,能夠重啓,也最大化的保證了job server的高可用。
目前有一個如今的管理工具,https://github.com/brianlmoon/GearmanManager,可是隻支持php-5.2,不過能夠自行修改支持php-5.4,我建議若是使用PHP做爲worker進程,使用php-5.4以上的版本。該工具的設計方法能夠借鑑,能夠比較好的管理gearman worker。
結合linux crontab,php腳本負責產生job,將任務分發到多臺服務器週期性的併發執行。能夠取代目前咱們比較多的crontab的工做任務。
郵件短信發送
異步log
跨語言相互調用(對於密集型計算的需求,能夠用C實現,PHP直接調用)
其餘耗時腳本
$>wget https://launchpadlibrarian.net/165674261/gearmand-1.1.12.tar.gz |
$>sudo apt-get install libboost1.55-all-dev gperf libevent libevent-dev uuid libmemcached-dev
$>tar zxvf gearmand-1.1.12.tar.gz
$>cd gearmand-1.1.12
$>/configure --prefix=/home/phpboy/Server/gearman
$>make & make install
a)默認啓動
$>./gearman -d |
b)支持memcached準持久化
$>./gearmand -d -q libmemcached --libmemcached-servers=127.0.0.1:11211
$>wget http://pecl.php.net/get/gearman-1.1.2.tgz
$>tar zxvf gearman-1.1.2.tgz#cd gearman-1.1.2
$>phpize
$>./configure --with-php-config=php-config
$>make & make install
能夠用上面個人測試的示例
附Gearmand(job server的啓動參數簡單說明)
-b, –backlog=BACKLOG 鏈接請求隊列的最大值
-d, –daemon Daemon 守護進程化
-f, –file-descriptors=FDS 可打開的文件描述符數量
-h, –help
-l, –log-file=FILE Log 日誌文件
-L, –listen=ADDRESS 開啓監聽的地址
-p, –port=PORT 開啓監聽的端口
-P, –pid-file=FILE File pid file
-r,–protocol=PROTOCOL 使用的協議
-q, –queue-type=QUEUE 持久化隊列類型
-t, –threads=THREADS I/O線程數量
-u, –user=USER 進程的有效用戶名
libdrizzle Options:
--libdrizzle-host=HOST Host of server.
--libdrizzle-port=PORT Port of server.
--libdrizzle-uds=UDS Unix domain socket for server.
--libdrizzle-user=USER User name for authentication.
--libdrizzle-password=PASSWORD Password for authentication.
--libdrizzle-db=DB Database to use.
--libdrizzle-table=TABLE Table to use.
--libdrizzle-mysql Use MySQL protocol.
libmemcached Options:
--libmemcached-servers=SERVER_LIST List of Memcached servers to use.
libsqlite3 Options:
--libsqlite3-db=DB Database file to use.
--libsqlite3-table=TABLE Table to use.
libpq Options:
--libpq-conninfo=STRING PostgreSQL connection information string.
--libpq-table=TABLE Table to use.
http Options:
--http-port=PORT Port to listen on.
Gearman工做在TCP上,默認端口爲4730,client與job server、worker與job server的通訊都基於此tcp的socket鏈接。client是工做任務的發起者,worker是能夠註冊處理函數的工做任務執行者,job server爲工做的調度者。協議包含請求報文與響應報文兩個部分,全部發向job server的數據包(TCP報文段的數據部分)認爲是請求報文,全部從job server發出的數據包(TCP報文段的數據部分)認爲是響應報文。worker或者client與job server間的通訊是基於二進制數據流的,但在管理client也有基於行文本協議的通訊。
請求報文與響應報文是由二進制包封裝。一個二進制包由頭header和可選的數據部分data組成。
報文類別,請求報文或者響應報文,4個字節
「�REQ」 請求報文
「�RES」 響應報文
包類型,高(大)字節序(網絡字節序),4個字節可能的類型有
類型值 名稱 報文類型 發送者
1 CAN_DO REQ Worker
2 CANT_DO REQ Worker
3 RESET_ABILITIES REQ Worker
4 PRE_SLEEP REQ Worker
5 (unused) - -
6 NOOP RES Worker
7 SUBMIT_JOB REQ Client
8 JOB_CREATED RES Client
9 GRAB_JOB REQ Worker
10 NO_JOB RES Worker
11 JOB_ASSIGN RES Worker
12 WORK_STATUS REQ Worker
13 WORK_COMPLETE REQ Worker
14 WORK_FAIL REQ Worker
15 GET_STATUS REQ Client
16 ECHO_REQ REQ Client/Worker
17 ECHO_RES RES Client/Worker
18 SUBMIT_JOB_BG REQ Client
19 ERROR RES Client/Worker
20 STATUS_RES RES Client
21 SUBMIT_JOB_HIGH REQ Client
22 SET_CLIENT_ID REQ Worker
23 CAN_DO_TIMEOUT REQ Worker
24 ALL_YOURS REQ Worker
25 WORK_EXCEPTION REQ Worker
26 OPTION_REQ REQ Client/Worker
27 OPTION_RES RES Client/Worker
28 WORK_DATA REQ Worker
29 WORK_WARNING REQ Worker
30 GRAB_JOB_UNIQ REQ Worker
31 JOB_ASSIGN_UNIQ RES Worker
32 SUBMIT_JOB_HIGH_BG REQ Client
33 SUBMIT_JOB_LOW REQ Client
34 SUBMIT_JOB_LOW_BG REQ Client
35 SUBMIT_JOB_SCHED REQ Client
36 SUBMIT_JOB_EPOCH REQ Client
數據部分,數據部分的各個部分爲null字符分隔。
當job server收到此包類型的請求報文時,就簡單的產生一個包類型爲ECHO_RES,同時將請求報文的數據部分做爲響應報文的數據部分的報文。主要用於測試或者調試
如:
Client -> Job Server 00 52 45 51 0REQ 報文類型 00 00 00 a0 16 (Packet type: ECHO_ERQ) 00 00 00 04 4 (Packet length) 74 65 73 74 test (Workload)
當job server響應ECHO_REQ報文時發送的包類型爲ECHO_RES的響應報文
如:
Job Server -> Client 00 52 45 53 0RES 報文類型 00 00 00 a1 17 (Packet type: ECHO_ERS) 00 00 00 04 4 (Packet length) 74 65 73 74 test (Workload)
client發送的請求報文:(僅能由client發送的請求報文)
SUBMIT_JOB, SUBMIT_JOB_BG,SUBMIT_JOB_HIGH, SUBMIT_JOB_HIGH_BG,SUBMIT_JOB_LOW, SUBMIT_JOB_LOW_BG
當client有一個工做任務須要運行,就會提交相應的請求報文,job server響應包類型爲JOB_CREATED數據部分爲job handle的響應報文。SUBMIT_JOB爲普通的工做任務,client獲得狀態更新及通知任務已經完成的響應;SUBMIT_JOB_BG爲異步的工做任務,client不關心任務的完成狀況;SUBMIT_JOB_HIGH爲高優先級的工做任務,SUBMIT_JOB_HIGH_BG爲高優先級的異步任務;SUBMIT_JOB_LOW爲低優先級的工做任務,SUBMIT_JOB_LOW_BG爲低優先級的異步任務。
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Client -> Job Server 00 52 45 51 0REQ (報文類型) 00 00 00 07 7 (Packet type: SUBMIT_JOB) 00 00 00 0d 13 (Packet length) 72 65 76 65 72 73 65 00 reverse0 (Function) 00 � (Unique ID) 74 65 73 74 test (Workload) |
和SUBMIT_JOB_BG相似,此類型的工做任務不會當即執行,而在設置的某個時間運行。
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Client -> Job Server 00 52 45 51 0REQ (報文類型) 00 00 00 23 35 (Packet type: SUBMIT_JOB_SCHED) 00 00 00 0d 13 (Packet length) 72 65 76 65 72 73 65 00 reverse0 (Function) 00 � (Unique ID) 01 � (minute 0-59) 01 � (hour 0-23) 01 � (day of month 1-31) 01 � (day of month 1-12) 01 � (day of week 0-6) 74 65 73 74 test (Workload) |
和SUBMIT_JOB_SCHED做用同樣,只是將設置的時間定爲了uinx時間戳GET_STATUS獲取某個工做任務執行的狀態信息
設置client與job server鏈接的選項
client獲取的響應報文:
JOB_CREATED響應包類型爲SUBMIT_JOB*的請求報文,數據部分爲job handle
WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE,WORK_FAIL, WORK_EXCEPTION
對於後臺運行的工做任務,任務執行信息能夠經過包類型爲上面的值來查看。
響應包類型爲GET_STATUS的請求報文,一般用來查看一個後臺工做任務是否已經完成,以及完成的百分比。
響應包類型爲OPTION_REQ的請求報文
worker發送的請求報文:
通知job server能夠執行給定的function name的任務,此worker將會放到一個鏈表,當job server收到一個function name的工做任務時,worker爲被喚醒。
和CAN_DO相似,只是針對給定的function_name的任務設置了一個超時時間。
worker通知job server已經不能執行給定的function name的任務
worker通知job server不能執行任何function name的任務
worker通知job server它將進入sleep階段,而以後此worker會被包類型爲NOOP的響應報文喚醒。
worker向job server抓取工做任務,job server將會響應NO_JOB或者JOB_ASSIG
和GRAB_JOB相似,可是job server在有工做任務時將會響應JOB_ASSIGN_UNIQ
worker請求報文的數據部分更新client
worker請求報文表明一個warning,它應該被對待爲一個WARNING
Sworker更新某個job handle的工做狀態,job server應該儲存這些信息,以便響應以後client的GET_STATUS請求
通知job server及全部鏈接的client,數據部分爲返回給client的數據
通知job server及全部鏈接的client,工做任務執行失敗
通知job server及全部鏈接的client,工做任務執行失敗並給出相應的異常
設置worker ID,從而job server的控制檯及報告命令能夠標識各個worker,數據部分爲worker實例的標識
暫未實現
job server喚醒sleep的worker,以即可以開始抓取工做任務
job server響應GRAB_JOB的請求,通知worker沒有等待執行的工做任務
job server響應GRAB_JOB的請求,通知worker有須要執行的工做任務
job server響應GRAB_JOB_UNIQ的請求,和JOB_ASSIGN同樣,只是爲client傳遞了一個惟一標識
基於上述的協議描述一個完整的例子
worker註冊能夠執行的工做任務
Worker -> Job Server
1 2 3 4 5 6 7 |
00 52 45 51 0REQ (Magic) 00 00 00 01 1 (Packet type: CAN_DO) 00 00 00 07 7 (Packet length) 72 65 76 65 72 73 65 reverse (Function) |
worker檢測或者抓取工做任務
1 2 3 4 5 6 7 |
Worker -> Job Server 00 52 45 51 0REQ (Magic) 00 00 00 09 9 (Packet type: GRAB_JOB) 00 00 00 00 0 (Packet length) |
job server響應worker的抓取工做(沒有工做任務)
1 2 3 4 5 |
00 52 45 53 0RES (Magic) 00 00 00 0a 10 (Packet type: NO_JOB) 00 00 00 00 0 (Packet length) |
worker通知job server開始sleep
1 2 3 4 5 |
00 52 45 51 0REQ (Magic) 00 00 00 04 4 (Packet type: PRE_SLEEP) 00 00 00 00 0 (Packet length) |
client提交工做任務
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Client -> Job Server 00 52 45 51 0REQ (Magic) 00 00 00 07 7 (Packet type: SUBMIT_JOB) 00 00 00 0d 13 (Packet length) 72 65 76 65 72 73 65 00 reverse0 (Function) 00 � (Unique ID) 74 65 73 74 test (Workload) |
job server響應client的SUBMIT_JOB請求,返回job handle
1 2 3 4 5 6 7 |
00 52 45 53 0RES (Magic) 00 00 00 08 8 (Packet type: JOB_CREATED) 00 00 00 07 7 (Packet length) 48 3a 6c 61 70 3a 31 H:lap:1 (Job handle) |
job server喚醒worker
1 2 3 4 5 6 7 |
Job Server -> Worker 00 52 45 53 0RES (Magic) 00 00 00 06 6 (Packet type: NOOP) 00 00 00 00 0 (Packet length) |
worker的抓取工做任務
job server分配工做任務給worker
worker完成工做任務通知job server
job server通知client完成了工做任務
Job Server -> Client
每一個client與job server是全雙工通訊,在一個socket能夠完成多個工做任務的投遞,可是收到任務的執行結果的順序可能與投遞的順序不一致。
Worker經過CAN_DO消息,註冊到Job server上。
隨後發起GRAB_JOB,主動要求分派任務。
Job server若是沒有job可分配,就返回NO_JOB。
Worker收到NO_JOB後,進入空閒狀態,並給Job server返回PRE_SLEEP消息,告訴Job server:」若是有工做來的話,用NOOP請求我先。」
Job server收到worker的PRE_SLEEP消息後,明白了發送這條消息的worker已經進入了空閒態。
這時若是有job提交上來,Job server會給worker先發一個NOOP消息。
Worker收到NOOP消息後,發送GRAB_JOB向Job server請求任務。
Job server把工做派發給worker。
Worker幹活,完過後返回WORK_COMPLETE給Job server。