Group chat analysis
- app sends group messages to msg_server
- After msg_server receives it, it sets the message creation time with a local timestamp (the client time is unreliable)
- Forward
- Query the validity of the group ID from the database, ignore it if it is illegal
- Whether the member is in the group member or not, ignore it if it is illegal
- Whether there is a session between the user and the group, there is no automatically created a session relationship
- Generate unique message ID in the group
- Write to the database table group_msg (according to groopId%8 points table)
- Return ack
- Return ack (if there are multiple logins, broadcast msg to multiple ends, and each end will reply ack confirmation)
- Query the list of group members from db_proxy_server
- Check the database
- Receiving group members
- Traverse
- Direct send online
- Offline pushes, first check the group push flag settings.
- Check the database
- Traverse the list of group push flags of all offline members
- If it is not set to do not disturb, push directly. ios: apns, android: various push notifications such as Huawei, Xiaomi or third parties such as Aurora, Homing Pigeon, etc.
- Offline users go online
- Pull group messages (different from WeChat, the strategy here is: first check the conversation, click on the group to display the latest 20 messages, and then store the local sqlite database. Then scroll up, and continue to pull.)
This solution of TeamTalkd is more suitable for getting started. Adopted the solution of diffuse reading (only one group message is stored and pulled multiple times), and the specific code is not posted.
msg_server mainly looks at the following 2 files:
- MsgConn.cpp: _HandleClientMsgData, which handles messages from the client.
- DBServConn.cpp: _HandleMsgData, which handles the response from db_proxy_server.
db_proxy_server mainly starts from the message-driven table and finds the implementation of related logic:
- HandlerMap.cpp: Message-driven table implementation, according to CmdID to call related processing functions, start from here.
- business/GroupMessageModel.cpp: group chat message processing, including storage, unread count update, etc.
Group unread count analysis
The implementation of TeamTalk is mainly divided into 2 parts:
- The total unread count of the group im_group_msg (1 group, 1 key)
- Member's unread count im_user_group (1 group with N keys)
The key rules are as follows:
- The total unread count of the group (in fact, the number of group messages): groupID + _im_group_msg, example: 2_im_group_msg
- Unread count of group members (more precisely, the number of group messages when the members have been read): userID + _ + groupID + _im_user_group, example: 7_2_im_user_group
The algorithm principle is as follows (assuming a group has 2 members A and B):
The implementation of TeamTalk is mainly divided into 2 blocks:
the total unread count of the group im_group_msg (1 group, 1 key)
, the unread count of members im_user_group (1 group) N Keys)
The key rules are as follows:
- The total unread count of the group: groupID+_im_group_msg, example: 2_im_group_msg
- Unread count of group members: userID+_+groupID+_im_user_group, example: 7_2_im_user_group
Algorithm principle (assuming a group has 2 members A and B):
- When A sends a message, record total=1, A's offset=1, at this time total-A.offset=0, so the number of unread messages of A is 0. B is online at this time, there is no offset of B in redis, then the unread count of B's group message is the total number, which is 1.
- When B clicks on the conversation, clearing the unread count is actually the process of updating B.offset. The offset is recorded as the total number of current group messages. The next time you log in, total-B.offset will be 0, that is, it will be cleared.
- At this time, A is the same. During the offline period, B sent 4 messages, total=5, total-A.offset=4, and the number of group unread messages during the offline period is calculated.
official:
成员某群未读消息总数 = 群消息总数(im_group_msg) - 群成员已读消息总数(im_user_group)
So why is it designed to use the total number of reads instead of the total number of unreads (for example, if the user is not online, the number of unreads is +1)?
In fact, this can reduce the update frequency of redis. Otherwise, if a group of 200 people sends a message, redis will be updated 199 times. The above design only needs to be updated twice.
Attached example of the structure of redis:
message code:
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;
}
Clear unread count:
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;
}
Get the total number of unread messages of the user:
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");
}
}
About the author
Recommend your own open source IM, written in pure Golang:
CoffeeChat:
https://github.com/xmcy0011/CoffeeChat
opensource im with server(go) and client(flutter+swift)
Referred to well-known projects such as TeamTalk and Guazi IM, including server (go) and client (flutter+swift), single chat and robot (micro, Turing, Sizhi) chat functions have been completed, and group chat functions are currently being developed , Welcome friends Star who are interested in golang and cross-platform development of flutter technology to pay more attention.
————————————————
Copyright statement: This article is the original article of CSDN blogger "Xu Fei", and it follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this for reprinting. statement.
Original link: https://blog.csdn.net/xmcy001122/article/details/109316394