以前曾經介紹過類似的後台任務處理項目Resque。兩者的設計其實非常接近,簡單可以類比為:
Gearman Job Server:對應Resque的Redis部分Gearman Client:對應Resque的Queue操作Gearman Worker:對應Resque的Worker和Job這裡之所以選擇Gearman而不是Resque是因為Gearman提供了比較好用的MySQL UDF,工作量更小。
安裝Gearman及PHP Gearman擴展
以下均以Ubuntu12.04為例。
apt-get install gearman gearman-server libgearman-dev
檢查Gearman的運行狀況:
/etc/init.d/gearman-job-server status * gearmand is running
說明Gearman已經安裝成功。
PHP的Gearman擴展可以通過pecl直接安裝
pecl install gearman echo "extension=gearman.so">/etc/php5/conf.d/gearman.ini service php5-fpm restart
但是實測發現ubuntu默認安裝的gearman版本過低,直接運行pecl install gearman會報錯
configure: error: libgearman version 1.1.0or later required
因此Gearman + PHP擴展建議通過編譯方式安裝,這裡為了簡單說明,選擇安裝舊版本擴展:
pecl install gearman-1.0.3
Gearman + PHP實例 為了更容易理解後文Gearman的運行流程,這裡不妨從一個最簡單的Gearman實例來說明,比如我們要進行一個文件處理的操作,首先編寫一個Gearman Client並命名為client.php:
<?php $client =newGearmanClient(); $client->addServer(); $client->doBackground('writeLog','Log content'); echo '文件已經在後台操作';
運行這個文件,相當於模擬用戶請求一個Web頁面後,將處理結束的信息返回用戶:
php client.php
查看一下Gearman的狀況:
(echo status ; sleep 0.1)| netcat127.0.0.14730
可以看到輸出為
writeLog 100.
說明我們已經在Gearman中建立了一個名為writeLog的任務,並且有1個任務在隊列等待中。
而上面的4列分別代表當前的Gearman的運行狀態:
通過MySQL UDF + Trigger同步數據到Gearman MySQL要實現與外部程序互通的最好方式還是通過MySQL UDF(MySQL user defined functions)來實現。為了讓MySQL能將數據傳入Gearman,這裡使用了lib_mysqludf_json和gearman-mysql-udf的組合。 安裝lib_mysqludf_json 使用lib_mysqludf_json的原因是因為Gearman只接受字符串作為入口參數,可以通過lib_mysqludf_json將MySQL中的數據編碼為JSON字符串
apt-get install libmysqlclient-dev wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip unzip master.zip cd lib_mysqludf_json-master/ rm lib_mysqludf_json.so gcc $(mysql_config --cflags)-shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
可以看到重新編譯生成了 lib_mysqludf_json.so 文件,此時需要查看MySQL的插件安裝路徑:
mysql -u root -pPASSWORD --execute="show variables like '%plugin%';"+---------------+------------------------+|Variable_name|Value|+---------------+------------------------+| plugin_dir |/usr/lib/mysql/plugin/|+---------------+------------------------+
然後將 lib_mysqludf_json.so 文件復制到對應位置:
cp lib_mysqludf_json.so /usr/lib/mysql/plugin/
最後登入MySQL運行語句注冊UDF函數:
CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';
安裝gearman-mysql-udf
方法幾乎一樣:
apt-get install libgearman-dev wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz tar -xzf gearman-mysql-udf-0.6.tar.gz cd gearman-mysql-udf-0.6./configure --with-mysql=/usr/bin/mysql_config --libdir=/usr/lib/mysql/plugin/ make && make install
登入MySQL運行語句注冊UDF函數:
CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so'; CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so';
最後指定Gearman服務器的信息:
SELECT gman_servers_set('127.0.0.1:4730');
通過MySQL觸發器實現數據同步
最終同步哪些數據,同步的條件,還是需要根據實際情況決定,比如我希望將數據表data的數據在每次更新時同步,那麼編寫Trigger如下:
DELIMITER $$ CREATE TRIGGER datatoredis AFTER UPDATE ON data FOR EACH ROW BEGIN SET @ret=gman_do_background('syncToRedis', json_object(NEW.id as`id`, NEW.volume as`volume`));END$$ DELIMITER ;
嘗試在數據庫中更新一條數據查看Gearman是否生效。
Gearman PHP Worker將MySQL數據異步復制到Redis Redis作為時下當熱的NoSQL緩存解決方案無需過多介紹,其安裝及使用也非常簡單:
apt-get install redis-server pecl install redis echo "extension=redis.so">/etc/php5/conf.d/redis.ini
然後編寫一個Gearman Worker:redis_worker.php
#!/usr/bin/env php<? $worker =newGearmanWorker(); $worker->addServer(); $worker->addFunction('syncToRedis','syncToRedis'); $redis =newRedis(); $redis->connect('127.0.0.1',6379);while($worker->work());function syncToRedis($job){global $redis; $workString = $job->workload(); $work = json_decode($workString);if(!isset($work->id)){returnfalse;} $redis->set($work->id, $workString);}
最後需要將Worker在後台運行:
nohup php redis_worker.php &
通過這種方式將MySQL數據復制到Redis,經測試單Worker基本可以瞬時完成。
任務名稱在等待隊列中的任務正在運行的任務正在運行的Worker進程可以使用watch進行實時監控:
watch -n 1"(echo status; sleep 0.1) | nc 127.0.0.1 4730"
然後我們需要編寫一個Gearman Worker命名為worker.php:
<?php $worker =newGearmanWorker(); $worker->addServer(); $worker->addFunction('writeLog','writeLog');while($worker->work());function writeLog($job){ $log = $job->workload();file_put_contents(__DIR__ .'/gearman.log', $log ."\n", FILE_APPEND | LOCK_EX);}
Worker使用一個while死循環實現守護進程,運行
php worker.php
可以看到Gearman狀態變為:
writeLog 001
同時查看同目錄下gearman.log,內容應為從Client傳入的值Log content。