グループチャット分析
- アプリはグループメッセージをmsg_serverに送信します
- msg_serverはそれを受信すると、メッセージの作成時刻をローカルタイムスタンプで設定します(クライアント時刻は信頼できません)。
- フォワード
- データベースからグループIDの有効性を照会し、違法な場合は無視します
- メンバーがグループメンバーであるかどうかにかかわらず、違法である場合は無視してください
- ユーザーとグループの間にセッションがあるかどうかに関係なく、自動的に作成されるセッション関係はありません
- グループ内で一意のメッセージIDを生成します
- データベーステーブルgroup_msgに書き込みます(groopId%8ポイントテーブルによる)
- ackを返す
- ackを返します(複数のログインがある場合は、メッセージを複数の端にブロードキャストし、各端がackの確認に応答します)
- db_proxy_serverからグループメンバーのリストを照会します
- データベースを確認してください
- グループメンバーの受け入れ
- トラバース
- オンラインで直接送信
- オフラインプッシュでは、最初にグループプッシュフラグの設定を確認します。
- データベースを確認してください
- すべてのオフラインメンバーのグループプッシュフラグのリストをトラバースします
- 邪魔にならないように設定されていない場合は、直接押してください。ios:apns、android:Huawei、Xiaomiなどのさまざまなプッシュ通知、またはAurora、HomingPigeonなどのサードパーティ。
- オフラインユーザーはオンラインになります
- グループメッセージをプルします(WeChatとは異なり、ここでの戦略は、最初に会話を確認し、グループをクリックして最新の20メッセージを表示し、次にローカルsqliteデータベースを保存します。次に上にスクロールしてプルを続けます。)
TeamTalkdのこのソリューションは、開始するのにより適しています。拡散読み取りのソリューションを採用し(1つのグループメッセージのみが複数回保存およびプルされる)、特定のコードは投稿されません。
msg_serverは、主に次の2つのファイルを調べます。
- MsgConn.cpp:_HandleClientMsgData。クライアントからのメッセージを処理します。
- DBServConn.cpp:_HandleMsgData。db_proxy_serverからの応答を処理します。
db_proxy_serverは主にメッセージ駆動型テーブルから開始し、関連するロジックの実装を見つけます。
- HandlerMap.cpp:関連する処理関数を呼び出すためのCmdIDによる、メッセージ駆動型テーブルの実装は、ここから開始します。
- business / GroupMessageModel.cpp:ストレージ、未読カウントの更新などを含むグループチャットメッセージの処理。
グループ未読カウント分析
TeamTalkの実装は、主に2つの部分に分かれています。
- グループim_group_msgの未読総数(1グループ、1キー)
- メンバーの未読カウントim_user_group(Nキーの1グループ)
主なルールは次のとおりです。
- グループの未読の総数(実際には、グループメッセージの数):groupID + _im_group_msg、例:2_im_group_msg
- グループメンバーの未読数(より正確には、メンバーが読み取られたときのグループメッセージの数):userID + _ + groupID + _im_user_group、例:7_2_im_user_group
アルゴリズムの原則は次のとおりです(グループに2つのメンバーAとBがあると仮定):
TeamTalkの実装は主に2つのブロックに分割されます:
グループim_group_msg(1グループ、1キー)
の未読総数、メンバーの未読数im_user_group(1グループ)Nキー)
主なルールは次のとおりです。
- グループの未読の総数:groupID + _im_group_msg、例:2_im_group_msg
- グループメンバーの未読数:userID + _ + groupID + _im_user_group、例:7_2_im_user_group
アルゴリズムの原則(グループに2人のメンバーAとBがあると仮定):
- Aがメッセージを送信するとき、レコードtotal = 1、Aのoffset = 1、この時点ではtotal-A.offset = 0であるため、Aの未読メッセージの数は0です。この時点でBはオンラインであり、redisにBのオフセットがない場合、Bのグループメッセージの未読数は合計数であり、1です。
- Bが会話をクリックすると、未読カウントのクリアは実際にはB.offsetを更新するプロセスです。オフセットは、現在のグループメッセージの総数として記録されます。次にログインすると、total-B.offsetは0になります。つまり、クリアされます。
- このとき、Aは同じです。オフライン期間中にBは合計= 5、合計-A.offset = 4の4つのメッセージを送信し、オフライン期間中のグループ内の未読メッセージの数が計算されます。
公式:
成员某群未读消息总数 = 群消息总数(im_group_msg) - 群成员已读消息总数(im_user_group)
では、未読の総数ではなく、読み取りの総数を使用するように設計されているのはなぜですか(たとえば、ユーザーがオンラインでない場合、未読の数は+1になります)。
実際、これによりredisの更新頻度を減らすことができます。そうでない場合、200人のグループがメッセージを送信すると、redisは199回更新されます。上記のデザインは2回更新するだけで済みます。
redisの構造の添付例:
メッセージコード:
bool CGroupMessageModel::incMessageCount(uint64_t nUserId, uint32_t nGroupId) {
bool bRet = false;
CacheManager *pCacheManager = CacheManager::getInstance();
CacheConn *pCacheConn = pCacheManager->GetCacheConn("unread");
if (pCacheConn) {
// 2002_im_group_msg
// |——count: +1
string strGroupKey = int2string(nGroupId) + GROUP_TOTAL_MSG_COUNTER_REDIS_KEY_SUFFIX;
pCacheConn->hincrBy(strGroupKey, GROUP_COUNTER_SUBKEY_COUNTER_FIELD, 1);
// 2002_im_group_msg下所有的key取出来
map<string, string> mapGroupCount;
bool bRet = pCacheConn->hgetAll(strGroupKey, mapGroupCount);
if (bRet) {
// 1_2002_im_user_group
// |——count: 1
string strUserKey =
int2string(nUserId) + "_" + int2string(nGroupId) + GROUP_USER_MSG_COUNTER_REDIS_KEY_SUFFIX;
string strReply = pCacheConn->hmset(strUserKey, mapGroupCount);
if (!strReply.empty()) {
bRet = true;
} else {
ERROR("hmset %s failed !", strUserKey.c_str());
}
} else {
ERROR("hgetAll %s failed!", strGroupKey.c_str());
}
pCacheManager->RelCacheConn(pCacheConn);
} else {
ERROR("no cache connection for unread");
}
return bRet;
}
未読カウントをクリアする:
bool CGroupMessageModel::clearMessageCount(uint64_t nUserId, uint32_t nGroupId) {
bool bRet = false;
CacheManager *pCacheManager = CacheManager::getInstance();
CacheConn *pCacheConn = pCacheManager->GetCacheConn("unread");
if (pCacheConn) {
// 用总数覆盖offset,即total-offset=0
string strGroupKey = int2string(nGroupId) + GROUP_TOTAL_MSG_COUNTER_REDIS_KEY_SUFFIX;
map<string, string> mapGroupCount;
bool bRet = pCacheConn->hgetAll(strGroupKey, mapGroupCount);
pCacheManager->RelCacheConn(pCacheConn);
if (bRet) {
string strUserKey =
int2string(nUserId) + "_" + int2string(nGroupId) + GROUP_USER_MSG_COUNTER_REDIS_KEY_SUFFIX;
string strReply = pCacheConn->hmset(strUserKey, mapGroupCount);
if (strReply.empty()) {
ERROR("hmset %s failed !", strUserKey.c_str());
} else {
bRet = true;
}
} else {
ERROR("hgetAll %s failed !", strGroupKey.c_str());
}
} else {
ERROR("no cache connection for unread");
}
return bRet;
}
ユーザーの未読メッセージの総数を取得します。
void CGroupMessageModel::getUnReadCntAll(uint64_t nUserId, uint32_t &nTotalCnt) {
list<uint32_t> lsGroupId;
CGroupModel::getInstance()->getUserGroupIds(nUserId, lsGroupId, 0);
uint32_t nCount = 0;
CacheManager *pCacheManager = CacheManager::getInstance();
CacheConn *pCacheConn = pCacheManager->GetCacheConn("unread");
if (pCacheConn) {
for (auto it = lsGroupId.begin(); it != lsGroupId.end(); ++it) {
uint32_t nGroupId = *it;
string strGroupKey = int2string(nGroupId) + GROUP_TOTAL_MSG_COUNTER_REDIS_KEY_SUFFIX;
string strGroupCnt = pCacheConn->hget(strGroupKey, GROUP_COUNTER_SUBKEY_COUNTER_FIELD);
if (strGroupCnt.empty()) {
// log("hget %s : count failed !", strGroupKey.c_str());
continue;
}
uint32_t nGroupCnt = (uint32_t) (atoi(strGroupCnt.c_str()));
string strUserKey =
int2string(nUserId) + "_" + int2string(nGroupId) + GROUP_USER_MSG_COUNTER_REDIS_KEY_SUFFIX;
string strUserCnt = pCacheConn->hget(strUserKey, GROUP_COUNTER_SUBKEY_COUNTER_FIELD);
uint32_t nUserCnt = (strUserCnt.empty() ? 0 : ((uint32_t) atoi(strUserCnt.c_str())));
// 这里就是上面说的:total - offset = 未读数量
if (nGroupCnt >= nUserCnt) {
nCount = nGroupCnt - nUserCnt;
}
if (nCount > 0) {
nTotalCnt += nCount;
}
}
pCacheManager->RelCacheConn(pCacheConn);
} else {
ERROR("no cache connection for unread");
}
}
著者について
純粋なGolangで書かれた独自のオープンソースIMをお勧めします。
CoffeeChat:
https://github.com/xmcy0011/CoffeeChat
opensource im with server(go)and client(flutter + swift)
サーバー(go)とクライアント(flutter + swift)、シングルチャットとロボット(micro、Turing、Sizhi)のチャット機能を含む、TeamTalkやGuazi IMなどの有名なプロジェクトが完了し、現在グループチャット機能が実行されています。開発された、より多くの注意を払うためにフラッターテクノロジーのgolangとクロスプラットフォーム開発に興味を持っているWelcomefriendsStar。
————————————————
著作権表示:この記事はCSDNブロガー「XuFei」のオリジナル記事であり、CC 4.0BY-SA著作権表示に準拠しています。元のソースを添付してくださいリンクとこれを再印刷します。ステートメント。
元のリンク:https://blog.csdn.net/xmcy001122/article/details/109316394