需求:
使用webman提供一個http服務(wù),接收大量的請求(1000w+/天),并將請求根據(jù)請求中的參數(shù)task_id存儲到apcu共享內(nèi)存中,由新開的進程定時獲取共享內(nèi)存種的數(shù)據(jù),每次獲取100條,批量存儲到redis中,供其他程序使用。
問題:
開了10個上報進程,每個上報進程每10秒會調(diào)用Cache::search()獲取需要上報的任務(wù),然后加鎖此任務(wù),對該任務(wù)下的數(shù)據(jù)進行上報,上報完成后釋放任務(wù)鎖。
隨著程序啟動時間越長,上報進程會出現(xiàn)busy的情況。此時重啟上報進程會恢復(fù)[idle]狀態(tài),持續(xù)一天時間,相同的業(yè)務(wù)量繼續(xù)出現(xiàn)busy的情況。
分析
我懷疑是隨著apcu的持續(xù)使用,導(dǎo)致Cache::search()模糊獲取key的方法會越來越慢,所以導(dǎo)致這個問題,但是不知道如何下手解決這個問題。
apcuinfo的信息為:
{"num_slots":4099,"ttl":0,"num_hits":11045579162,"num_misses":692074161,"num_inserts":1451177345,"num_entries":15239,"expunges":0,"start_time":1715919517,"mem_size":3907568,"memory_type":"mmap","mem_size_mb":3.7265}
核心代碼為:
public static function push(): void
{
Cache::Search(Cache::WildcardToRegex(Util::TENANT_TASK_PREFIX . '*'), function ($taskKey, $value) {
// 獲取待上報任務(wù)
list(, $tenantId, $taskId) = explode(':', $taskKey, 3);
// 增加原子鎖,保證【是否存在上報的任務(wù)】和【任務(wù)加鎖】的原子性
$isExists = true;
Cache::Atomic('report-lock', function () use ($taskId, &$isExists) {
// 判斷是否已經(jīng)在上報
if (!static::exists($taskId)) {
// 記錄不存在標識
$isExists = false;
// 上報任務(wù)加鎖
static::lock($taskId);
}
});
if ($isExists) {
return;
}
$result = [];
// 每次獲取100條通話記錄
Cache::Search(Cache::WildcardToRegex(static::getCacheKey() . $taskId . '_*'),
function ($key, $value) use ($tenantId, $taskId, &$result) {
$result[$key] = $value;
});
// 刪除已上報的緩存
Cache::Del(...array_keys($result));
// 上報任務(wù)釋放鎖
static::unlock($taskId);
// 上報消息
if (!empty($result)) {
// 分塊 每次上報100條
foreach (array_chunk($result, 100) as $messages) {
static::reportMessage($tenantId, $taskId, $messages);
}
}
});
}
負載均衡node-1
兩周未重啟進程情況為:
負載均衡node-2
一天未重啟進程情況為:
http://wtbis.cn/doc/workerman/debug/status.html
這里有定位busy的文檔。
另外也可以自己記錄下懷疑慢操作代碼的耗時情況。
Cache::search()這個函數(shù)本質(zhì)上是O(N)的復(fù)雜度,會掃表遍歷匹配,不建議在數(shù)據(jù)量非常大的情況下來做頻繁的處理;
在這種業(yè)務(wù)場景下,我更建議開啟進程然后使用Cache的channel進行監(jiān)聽,然后生產(chǎn)端將需要數(shù)據(jù)推送到channel中,由進程監(jiān)聽消費并更新到對應(yīng)的數(shù)據(jù)儲存器中
感謝,找到問題了,業(yè)務(wù)代碼的問題,問題是key太多了,沒有及時的清理,后面我推送完del掉key就沒事了,然后用nignx做了負載到了三個節(jié)點分流,目前推送沒啥問題了,不過后面如果數(shù)據(jù)量進一步擴大,就換成channel監(jiān)聽