Swoole擴充內建的Task進程功能非常強大,可以用來實現各種複雜的商務邏輯。本文主要介紹使用task/finish功能實現程式內的Map-Reduce並發任務處理。一個聊天服務經常會有群聊需求,我的群組和群組內成員,另外群組內成員需要按照積分排序,類似與這樣的功能就可以使用Swoole簡單實現。
傳統多線程方案
建立2個全域變數Map,group_map以group_id為Key,儲存成員set。user_map以uid為Key儲存目前使用者加入的所有group。
多線程環境下實際上不能直接操作這2個Map,必須要加鎖。當添加使用者到一個組或者使用者退出一個組時需要操作這2個map,必須要加鎖。如果操作很頻繁,實際上鎖的碰撞是很嚴重的,這部分操作就會變成串列的。同時只有一個線程可以對map進行操作。鎖的爭搶也會帶來大量線程切換浪費很多CPU資源。
lock.lock();group_map[group_id].append([uid, score]);user_map[uid].append(group_id);group_map.sortByScore();lock.unlock();
基於Swoole的Task功能
基於Swoole的Task功能,可以將任務切片,然後hash投遞到不同的Task進程,完成任務。排序功能可以直接使用PHP提供的 SplHeap 實現,時間複雜度為O(logn),如果要實現查詢功能,如根據UID查詢使用者加入的所有群組,根據GroupId查詢有哪些成員。可以先計算Hash找到對應Task進程,然後通過task/taskwait發送指令,直接讀取進程的變數尋找到資訊。
$serv->set(array("task_worker_num" => 24));$serv->task(array("cmd" => "user", "uid" => $uid, "gid" => $gid, "score" => $score), $gid % $task_worker_num);$serv->task(array("cmd" => "group", "uid" => $uid, "gid" => $gid), $uid % $task_worker_num);class MyMaxHeap extends SplHeap{ public function compare($value1, $value2) { return ($value1['score'] - $value2['score']); }}function onTask($serv, $taskId, $srcWorkerId, $data) { static $userMap = array(); static $groupMap = array(); if ($data['cmd'] == 'group') { if (!isset($groupMap[$data['gid']])) { $groupMap[$data['gid']] = new MyMaxHeap(); } $heap = $groupMap[$data['gid']]; $heap->insert(array("uid" => $data['uid'], "score" => $data['score'])); } elseif ($data['cmd'] == 'user') { $userMap[$data['uid']][] = $data['gid']; }}
由於Task進程只有數組操作,所以是非阻塞的,只需要開啟與CPU核心數相同的進程數量即可。進程間無任何加鎖爭搶,效能非常好。Swoole的Task進程通訊使用UnixSocket,是核心提供的全記憶體通訊方式無任何IO,一寫一讀單進程可達100萬/秒。雖然沒有直接讀變數的速度快,但效能也足夠了。
————–偉大的分割線—————
PHP飯米粒(phpfamily) 由一群靠譜的人建立,願為PHPer帶來一些值得細細品味的精神食糧!
本文由 rango 獨家授權 php飯米粒發布,轉載請註明本來源資訊和以下的二維碼(長按可識別二維碼關注):