Swoole webSocket消息服務系統(tǒng)方案設計詳解
概述
基于Swoole的websocket服務,計劃整合3篇進行技術整理,該服務主要有2個核心業(yè)務,用戶消息服務(消息計數統(tǒng)計)和 客服IM消息系統(tǒng)服務,這篇先說用戶消息服務是怎么設計實現的。
實現方案
用戶消息服務主要有2部分組成,對外使用webSocket長鏈接服務提供給安卓/Ios手機客戶端,web提供服務,對內使用Http服務。
鑒權和緩存周期設置

當服務端攜帶Token來訪問請求webSocket服務,進行用戶中心進行權限驗證,如果權限通過,在本地進行信息緩存,返回給請求端,為了防止緩存雪崩(雪崩就是指緩存同一時間到期),用戶訪問峰值是晚間21-24點這個時間段,峰值大概100w/請求,持續(xù)4個小時左右,但因為用戶中心的緩存時間為7300s,所以這里的過期時間公式:
$uid = $redis->get($token);
$expireTime = 3650 + rand(1, 3000);
$uid = OAuth::getUserInfo($token);
if (!empty($uid) && intval($uid) > 0) {
//存入緩存時間,過期時間小于 7300s
$redis->setEx($token, $expireTime, $uid);
}
if($uid && $uid > 0){
$key = 'token_'.$uid;
$redis->setEx($key, $expireTime, $token);
}
本地服務的緩存怎么存儲,具體看自己的業(yè)務情況,適合自己的就是最好的。
Http服務
Http服務的安全依賴于服務只針對云服務器內網訪問,主站有服務變更時,異步埋點在功能里,比如有系統(tǒng)消息、評論、站內信等一系列操作的時候,會通過http請求用戶消息服務,設置超時時間,允許丟失部分消息。

1.業(yè)務埋點處理
埋點再操作后異步觸發(fā),超時時間2秒,如果失敗再進行一次重試,如果失敗,其實基本就是服務掛了,局域網處理,性能傳輸成本幾乎為0,這個地方相當于消息的生產方。
public function swooleComment($uid, $data)
{
$url = $this->swooleUrl . "/api/comment/message";
$commentUid = empty($data['comment_uid']) ? 0 : $data['comment_uid'];
$msg = [
"uid" => $uid,
"msg" => json_encode(['comment_uid' => $commentUid])
];
$res = Curl::posturl($url, http_build_query($msg), $this->_headerQArr, 2);
if ($res === false) {
// 請求失敗再重試一次
usleep(100000);
$res = Curl::posturl($url, http_build_query($msg), $this->_headerQArr, 2);
}
return $res;
}
2.消息處理
Swoole有一個缺點就是如果沒有建立websocket服務,就不能實時進行通信,所以這個地方我分兩步處理,根據消息類型進行管理和消息的推送,存入redis list結構的隊列中,使用Crontab,執(zhí)行定時腳本處理。
設計方案為快慢2條雙隊列結構,快隊列主要處理當前最新的消息,如果用戶超過1天不上線,放入延遲隊列執(zhí)行,用戶超過超過15天未登錄,消息釋放。
websocket的心跳時間是300s,所以crontab 4min,執(zhí)行一次,延遲隊列6分鐘執(zhí)行一次,我們的redis使用的是鏈接池單節(jié)點特點,整個服務都在依賴,所以這樣設計的方案。
3.數據存儲
數據使用Mysql存儲,Uid進行分表取模,采用分表的初衷是因為當時已經有300w+的用戶,消息多,所以采用分表設計,所有的操作依賴于uid這個變量,所有的操作都采用TaskManager異步操作,以保證最大的性能。
protected function _getTableName(int $uid): string
{
$tableIndex = intval($uid % 128);
return 'user_push_msg_' . $tableIndex;
}
protected function addAsyncMysql( array $pushMsg, int $uid): ?bool
{
$tableName = $this->_getTableName($uid);
if (empty($pushMsg) || empty($tableName) || empty($uid)) return false;
TaskManager::getInstance()->async(function () use ($pushMsg, $tableName) {
DbManager::getInstance()->invoke(function (ClientInterface $client)
use ($pushMsg, $tableName) {
$model = PushMsgModel::invoke($client, $pushMsg);
$model->tableName($tableName)->save();
}, self::MYSQL_CONN_NAME);
});
}
用戶消息數統(tǒng)計
在業(yè)務中有全體用戶,全體作者,簽約作者等分組的情況,成為統(tǒng)計中的重點和難點,一共分分2步解決。
**第一步,**在http消息接收端專門放置一個消息計數器對用戶單條發(fā)送的消息進行計數,只統(tǒng)計針對用戶的消息。
**第二步,**新建一個mysql表,專門用于統(tǒng)計用戶最近查看消息的時間戳,根據用戶最后的查看消息時間來統(tǒng)計群組中的未讀消息數,把兩個結果進行相加,得出用戶未讀消息數和。
表的設計用uid做主鍵,保持用戶的唯一性,使用REPLACE INTO進行更新,REPLACE INTO的好處是如果主鍵uid存在,更新時間,如果不存在則新增數據。
CREATE TABLE `table` ( `uid` int(10) unsigned NOT NULL DEFAULT '0', `unixtime` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`uid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶查看消息最新時間'
以上就是Swoole webSocket消息服務系統(tǒng)方案設計詳解的詳細內容,更多關于Swoole webSocket消息服務的資料請關注腳本之家其它相關文章!
相關文章
ThinkPHP3.2.3框架郵件發(fā)送功能圖文實例詳解
這篇文章主要介紹了ThinkPHP3.2.3框架郵件發(fā)送功能,結合圖文與實例形式詳細分析了基于thinkPHP框架進行郵件發(fā)送的相關原理、配置及操作技巧,需要的朋友可以參考下2019-04-04
PHP實現用戶異地登錄提醒功能的方法【基于thinkPHP框架】
這篇文章主要介紹了PHP實現用戶異地登錄提醒功能的方法,基于thinkPHP框架結合用戶session實現異地登陸的判定功能,非常具有實用價值,需要的朋友可以參考下2018-03-03

