Gearman實戰第一彈:非同步處理結算單
阿新 • • 發佈:2020-10-12
昨天夢迴jm,醒來之後看著窗外萬里晴空,想大聲喊一句:爺青回!
我想起之前使用gearman的歲月。不知不覺也過了快5年,想總結一篇關於gearman的技術文章算是一種對青春的祭奠,再不寫的話更少有phper用過這個強大的分散式任務分發框架,畢竟這個時代已經屬於swoole。
先講一下Gearman,它是一款用C++編寫的分散式任務分發框架,通過暴露API給使用方來完任務委託,在多臺機器上負載均衡且併發地執行任務。特別是密集型計算,可以使用Gearman去非同步地完成任務排程。
Gearman的結構分成三個角色:
Client: 客戶端,可以是不同的程式語言,如php,java,python等.
Job Server: 任務伺服器,負責分派任務,不負責業務邏輯。
worker: 執行任務的節點,可以是不同的程式語言實現,不一定和clent端的語言一樣,如java,php, python等。
整個流程圖如下圖所示:
![](https://img2020.cnblogs.com/blog/782095/202010/782095-20201011231821192-309901136.png)
下一步開始安裝gearman和相關擴充套件,這次我們還是使用php來作為程式語言。之前我寫過一篇文章講在伺服器上安裝gearman,這裡只講一下在macpro上如何安裝gearman和php擴充套件。
比起linux上面的編譯安裝,mac上安裝很簡單,使用brew命令如下:
```
brew install gearman
# 為了讓上一步安裝的gearman能直接在終端呼叫,需要先建立下面這個目錄
sudo mkdir /usr/local/sbin
# 讓當前使用者成為gearman安裝目錄下的sbin的所有者
sudo chown -R $(whoami) $(brew --prefix)/sbin
# 把link指向剛才安裝的gearman
brew link gearman
# 這一步很關鍵,讓終端可以直接呼叫gearmand命令
ln -s /usr/local/opt/gearman/sbin/gearmand /usr/local/bin
```
先介紹一下背景:
假設我們有一個場景,很多商家通過我們的結算平臺進行天結算,然後結算單需要複雜的計算,然後結算完成後非同步地把計算結果以附件excel的方式傳送給使用者。假設有十萬商家,每天訂單有上萬每戶,那麼這是比較大的資料量,且都是在某個時間段開放結算,有併發的壓力。
這個時候使用者(也就是商家)非常想快速完成結算並看到結果,對於處理過程的等待是非常低容忍度的,我們需要考慮可以利用多個worker非同步去處理這些結算任務。
首先編寫worker端的程式碼:
```
addServer('127.0.0.1', 4730); // 可以註冊多個server,server可以在不同的機器上。
// 非阻塞方式執行
$worker->addOptions(GEARMAN_WORKER_NON_BLOCKING);
$worker->addFunction("calculate", "calculatePayment");
$worker->addFunction('send', "sendEmail");
while($worker->work());
// 計算結算單
function calculatePayment($job) {
$data = json_decode($job->workload());
// 開始複雜的計算 todo
return $data;
}
// 傳送郵件
function sendEmail($job) {
// todo
$data = $job->workload();
send_email_with_attachment($data->email, $data->content);
return true;
}
```
然後是編寫客戶端,命名為client.php,程式碼如下:
```
addServer();
echo "start the calling";
$paymentList = [['order_id' => '110112', 'product_id' => [2323,4455,4455], 'pay_money' => '4423.00'], [['order_id' => '110113', 'product_id' => [223,45,67], 'pay_money' => '1400.00']]];
$data = $client->addTask('calculate', json_encode($paymentList));
// 後臺方式執行,因為畢竟只是發郵件
$client->addTaskBackground("send", $data);
echo "finish\r\n";
```
然後可以先把gearmand跑起來,也就是job server這一塊,命令很簡單:
```
```
但是因為有可能任務執行失敗或者gearmand服務因為各種原因掛掉,所以建議結合mysql做持久化,也就是把執行情況記錄到mysql中,一旦出現問題down掉,重新執行的時候可以根據mysql中的執行記錄從失敗的記錄開始重跑。
為了做持久化,先建立用於持久化的資料庫和表:
```
CREATE DATABASE gearman;
CREATE TABLE `gearman_queue` (
`unique_key` varchar(64) NOT NULL,
`function_name` varchar(255) NOT NULL,
`priority` int(11) NOT NULL,
`data` longblob NOT NULL,
`when_to_run` int(11),
PRIMARY KEY (`unique_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
執行gearmand的時候可以使用到mysql方式作為持久化,命令如下所示:
```
gearmand -q mysql \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=mine \
--mysql-password=xxxxx \
--mysql-db=gearman \
--mysql-table=gearman_queue
```
然後可以把worker執行起來,可以多開幾個終端,例如跑三個worker,命令如下:
```
php /path/to/folder/worker.php
```
然後再執行client去觸發任務:
```
php /path/to/folder/client.php
```
剩下就交給gearman去分發任務了,worker開始併發地高效處理任務啦!
當然我們也要考慮監控和管理job server,可以用shell去監聽,也可以自己編寫一個Gearman manager工具。這是後面可以聊的,到時候另開一篇文章吧。
除此之外,其他我們對於worker部分完全可以考慮使用一些異構語言,如java或者golang,PHP只做客戶端使用,有人已經編寫了grpc去實現了。
今年gearman的作者還在不斷迭代,目前最新版本是1.1.19.1, 我收回之前的話,還是很多人在為這個框架付出,聽說10年前左右雅虎已經大面積使用這個gearman框架來做新聞聚合了,當然jm也有用。
gearman=swoole+任務分發,老驥伏櫪,志在千里