工作中我們有時候會遇到比如需要同時發布數據到多個個服務器上,或者同時處理多個任務。可以使用PHP的curl_multi的方式並發處理請求,但是由於網絡和數據以及各個服務器等等的一些情況導致這種並發處理的響應時間很慢,因為在並發請求的過程中還包括記錄日志,處理數據等邏輯,等待處理結果並返回,所以也不能友好的滿足後台操作的體驗。
現在有另外一種方案,利Gearman來實現並發的需求。通過Client將請求發送到Gearman的Jobs,在每個Work中來再來進行curl_multi和數據處理和日志等一些操作,同時用supervisor 來監控Gearman以及Works的進程,這樣可以實現一個並行的多進程和負載均衡的方案。
Gearman可以做什麼:
異步處理:圖片處理,訂單處理,批量郵件/通知之類的
要求高CPU或內存的處理:大容量的數據處理,MapReduce運算,日志聚集,視頻編碼
分布式和並行的處理
定時處理:增量更新,數據復制
限制速率的FIFO處理
分布式的系統監控任務
Gearman工作原理:
使用Gearman的應用通常有三部分組成:一個Client、一個Worker、一個 任務服務器。 Client的作用是提出一個 Job 任務 交給 Job Server 任務服務器。Job Server 會去尋找一個 合適的 Worker 來完成這項任務。Worker 執行由 Client 發送過來的 Job,並且將結果通過 Job Server 返回給 Client。Gearman 提供了 Client 和 Worker 的 API,利用這些API 應用可以同 Gearman Job Server來進行通信。Gearman 內部 Client 和 Worker 之間的通信都是通過 TCP 連接來進行的。
Gearman可以將工作的負載分擔到不同的機器中。
安裝:
復制代碼 代碼如下:
rpm -ivh http://dl.iuscommunity.org/pub/ius/stable/Redhat/6/x86_64/epel-release-6-5.noarch.rpm
yum install -y gearmand
啟動:
gearmand -d
安裝PHP Gearman擴展
我都是用pcel來安裝的,你也可以下載源碼包來編譯安裝,但是記得要先安裝libgearman和re2c,不然擴展編譯安裝會出錯。
pecl install gearman #不成功並提示版本問題可以試試 pecl install gearman-1.0.3,默認好像是1.1.2
編譯安裝也很簡單
復制代碼 代碼如下:
wget -c http://pecl.php.net/get/gearman-1.1.1.tgz
tar zxvf gearman-1.1.1.tgz
phpize
./configure
make && make install
echo "extension=gearman.so" >> /etc/php.ini
PHP接口函數
Gearman提供很多完善的擴展函數,包括GearmanClient,GearmanJob,GearmanTask,GearmanWorker,具體可以查看PHP官方手冊.
這是官方提供的Example其中的一個,相當與一個並發的分發任務處理的例子
<?php $client = new GearmanClient(); $client->addServer(); // initialize the results of our 3 "query results" here $userInfo = $friends = $posts = null; // This sets up what gearman will callback to as tasks are returned to us. // The $context helps us know which function is being returned so we can // handle it correctly. $client->setCompleteCallback(function(GearmanTask $task, $context) use (&$userInfo, &$friends, &$posts) { switch ($context) { case 'lookup_user': $userInfo = $task->data(); break; case 'baconate': $friends = $task->data(); break; case 'get_latest_posts_by': $posts = $task->data(); break; } }); // Here we queue up multiple tasks to be execute in *as much* parallelism as gearmand can give us $client->addTask('lookup_user', '[email protected]', 'lookup_user'); $client->addTask('baconate', '[email protected]', 'baconate'); $client->addTask('get_latest_posts_by', '[email protected]', 'get_latest_posts_by'); echo "Fetching...\n"; $start = microtime(true); $client->runTasks(); $totaltime = number_format(microtime(true) - $start, 2); echo "Got user info in: $totaltime seconds:\n"; var_dump($userInfo, $friends, $posts);
gearman_work.php
<?php $worker = new GearmanWorker(); $worker->addServer(); $worker->addFunction('lookup_user', function(GearmanJob $job) { // normally you'd so some very safe type checking and query binding to a database here. // ...and we're gonna fake that. sleep(3); return 'The user requested (' . $job->workload() . ') is 7 feet tall and awesome'; }); $worker->addFunction('baconate', function(GearmanJob $job) { sleep(3); return 'The user (' . $job->workload() . ') is 1 degree away from Kevin Bacon'; }); $worker->addFunction('get_latest_posts_by', function(GearmanJob $job) { sleep(3); return 'The user (' . $job->workload() . ') has no posts, sorry!'; }); while ($worker->work());
我在3個終端中都執行了gearman_work.php
ryan@ryan-lamp:~$ ps aux | grep gearman* | grep -v grep gearman 1504 0.0 0.1 60536 1264 ? Ssl 11:06 0:00 /usr/sbin/gearmand --pid-file=/var/run/gearman/gearmand.pid --user=gearman --daemon --log-file=/var/log/gearman-job-server/gearman.log --listen=127.0.0.1 ryan 2992 0.0 0.8 43340 9036 pts/0 S+ 14:05 0:00 php /var/www/gearmand_work.php ryan 3713 0.0 0.8 43340 9036 pts/1 S+ 14:05 0:00 php /var/www/gearmand_work.php ryan 3715 0.0 0.8 43340 9036 pts/2 S+ 14:05 0:00 php /var/www/gearmand_work.php
來查看下執行gearman_work.php的結果shell
復制代碼 代碼如下:
Fetching...
Got user info in: 3.03 seconds:
string(59) "The user requested ([email protected]) is 7 feet tall and awesome"
string(56) "The user ([email protected]) is 1 degree away from Kevin Bacon"
string(43) "The user ([email protected]) has no posts, sorry!"
看到上面的3.03 seconds,說明client請求過去的任務被並行分發執行了。
在實際的生產環境中,為了監測gearmand和work的進程沒有被意外退出,我們可以借助Supervisor這個工具.