1. 程式人生 > >Gearman實戰第一彈:非同步處理結算單

Gearman實戰第一彈:非同步處理結算單

昨天夢迴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+任務分發,老驥伏櫪,志在千里