From d9921248a7b295b0cae5b19f61ee2508365970e2 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 8 Jan 2024 15:39:39 +0800 Subject: [PATCH 01/66] feat: msg local cache --- internal/rpc/msg/server.go | 17 ++-- pkg/common/cachekey/black.go | 15 ++++ pkg/common/cachekey/conversation.go | 48 ++++++++++ pkg/common/cachekey/friend.go | 27 ++++++ pkg/common/cachekey/group.go | 45 ++++++++++ pkg/common/cachekey/msg.go | 23 +++++ pkg/common/cachekey/s3.go | 1 + pkg/common/cachekey/user.go | 21 +++++ pkg/common/db/cache/black.go | 3 +- pkg/common/db/cache/conversation.go | 39 ++++---- pkg/common/db/cache/friend.go | 15 ++-- pkg/common/db/cache/group.go | 33 ++++--- pkg/common/db/cache/msg.go | 20 ++--- pkg/common/db/cache/user.go | 12 +-- pkg/common/localcache/business.go | 69 +++++++++++++++ pkg/common/localcache/cache.go | 66 ++++++++++++++ pkg/common/localcache/local/cache.go | 51 +++++++++++ pkg/common/localcache/local/lru.go | 76 ++++++++++++++++ pkg/common/localcache/local/lru_test.go | 95 ++++++++++++++++++++ pkg/common/localcache/local/target.go | 10 +++ pkg/common/localcache/option.go | 113 ++++++++++++++++++++++++ pkg/common/localcache/tool.go | 9 ++ pkg/rpccache/friend.go | 38 ++++++++ 23 files changed, 778 insertions(+), 68 deletions(-) create mode 100644 pkg/common/cachekey/black.go create mode 100644 pkg/common/cachekey/conversation.go create mode 100644 pkg/common/cachekey/friend.go create mode 100644 pkg/common/cachekey/group.go create mode 100644 pkg/common/cachekey/msg.go create mode 100644 pkg/common/cachekey/s3.go create mode 100644 pkg/common/cachekey/user.go create mode 100644 pkg/common/localcache/business.go create mode 100644 pkg/common/localcache/cache.go create mode 100644 pkg/common/localcache/local/cache.go create mode 100644 pkg/common/localcache/local/lru.go create mode 100644 pkg/common/localcache/local/lru_test.go create mode 100644 pkg/common/localcache/local/target.go create mode 100644 pkg/common/localcache/option.go create mode 100644 pkg/common/localcache/tool.go create mode 100644 pkg/rpccache/friend.go diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 88be287fd..9b1965b00 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -16,6 +16,7 @@ package msg import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/rpccache" "google.golang.org/grpc" @@ -34,12 +35,13 @@ import ( type ( MessageInterceptorChain []MessageInterceptorFunc msgServer struct { - RegisterCenter discoveryregistry.SvcDiscoveryRegistry - MsgDatabase controller.CommonMsgDatabase - Group *rpcclient.GroupRpcClient - User *rpcclient.UserRpcClient - Conversation *rpcclient.ConversationRpcClient - friend *rpcclient.FriendRpcClient + RegisterCenter discoveryregistry.SvcDiscoveryRegistry + MsgDatabase controller.CommonMsgDatabase + Group *rpcclient.GroupRpcClient + User *rpcclient.UserRpcClient + Conversation *rpcclient.ConversationRpcClient + friend *rpccache.FriendLocalCache + //friend *rpcclient.FriendRpcClient GroupLocalCache *localcache.GroupLocalCache ConversationLocalCache *localcache.ConversationLocalCache Handlers MessageInterceptorChain @@ -79,7 +81,6 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e conversationClient := rpcclient.NewConversationRpcClient(client) userRpcClient := rpcclient.NewUserRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) - friendRpcClient := rpcclient.NewFriendRpcClient(client) msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) s := &msgServer{ Conversation: &conversationClient, @@ -89,7 +90,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e RegisterCenter: client, GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient), ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient), - friend: &friendRpcClient, + friend: rpccache.NewFriendLocalCache(rpcclient.NewFriendRpcClient(client)), } s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg)) s.addInterceptorHandler(MessageHasReadEnabled) diff --git a/pkg/common/cachekey/black.go b/pkg/common/cachekey/black.go new file mode 100644 index 000000000..841e10c6a --- /dev/null +++ b/pkg/common/cachekey/black.go @@ -0,0 +1,15 @@ +package cachekey + +const ( + blackIDsKey = "BLACK_IDS:" + isBlackKey = "IS_BLACK:" +) + +func GetBlackIDsKey(ownerUserID string) string { + return blackIDsKey + ownerUserID + +} + +func GetIsBlackIDsKey(possibleBlackUserID, userID string) string { + return isBlackKey + possibleBlackUserID + "-" + userID +} diff --git a/pkg/common/cachekey/conversation.go b/pkg/common/cachekey/conversation.go new file mode 100644 index 000000000..eb5b83f0e --- /dev/null +++ b/pkg/common/cachekey/conversation.go @@ -0,0 +1,48 @@ +package cachekey + +import "time" + +const ( + conversationKey = "CONVERSATION:" + conversationIDsKey = "CONVERSATION_IDS:" + conversationIDsHashKey = "CONVERSATION_IDS_HASH:" + conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:" + recvMsgOptKey = "RECV_MSG_OPT:" + superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:" + superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:" + conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:" + + conversationExpireTime = time.Second * 60 * 60 * 12 +) + +func GetConversationKey(ownerUserID, conversationID string) string { + return conversationKey + ownerUserID + ":" + conversationID +} + +func GetConversationIDsKey(ownerUserID string) string { + return conversationIDsKey + ownerUserID +} + +func GetSuperGroupRecvNotNotifyUserIDsKey(groupID string) string { + return superGroupRecvMsgNotNotifyUserIDsKey + groupID +} + +func GetRecvMsgOptKey(ownerUserID, conversationID string) string { + return recvMsgOptKey + ownerUserID + ":" + conversationID +} + +func GetSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string { + return superGroupRecvMsgNotNotifyUserIDsHashKey + groupID +} + +func GetConversationHasReadSeqKey(ownerUserID, conversationID string) string { + return conversationHasReadSeqKey + ownerUserID + ":" + conversationID +} + +func GetConversationNotReceiveMessageUserIDsKey(conversationID string) string { + return conversationNotReceiveMessageUserIDsKey + conversationID +} + +func GetUserConversationIDsHashKey(ownerUserID string) string { + return conversationIDsHashKey + ownerUserID +} diff --git a/pkg/common/cachekey/friend.go b/pkg/common/cachekey/friend.go new file mode 100644 index 000000000..1f9d4e94f --- /dev/null +++ b/pkg/common/cachekey/friend.go @@ -0,0 +1,27 @@ +package cachekey + +import "time" + +const ( + friendExpireTime = time.Second * 60 * 60 * 12 + friendIDsKey = "FRIEND_IDS:" + twoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:" + friendKey = "FRIEND_INFO:" + isFriendKey = "IS_FRIEND:" +) + +func GetFriendIDsKey(ownerUserID string) string { + return friendIDsKey + ownerUserID +} + +func GetTwoWayFriendsIDsKey(ownerUserID string) string { + return twoWayFriendsIDsKey + ownerUserID +} + +func GetFriendKey(ownerUserID, friendUserID string) string { + return friendKey + ownerUserID + "-" + friendUserID +} + +func GetIsFriendKey(possibleFriendUserID, userID string) string { + return isFriendKey + possibleFriendUserID + "-" + userID +} diff --git a/pkg/common/cachekey/group.go b/pkg/common/cachekey/group.go new file mode 100644 index 000000000..3e5431b83 --- /dev/null +++ b/pkg/common/cachekey/group.go @@ -0,0 +1,45 @@ +package cachekey + +import ( + "strconv" + "time" +) + +const ( + groupExpireTime = time.Second * 60 * 60 * 12 + groupInfoKey = "GROUP_INFO:" + groupMemberIDsKey = "GROUP_MEMBER_IDS:" + groupMembersHashKey = "GROUP_MEMBERS_HASH2:" + groupMemberInfoKey = "GROUP_MEMBER_INFO:" + joinedGroupsKey = "JOIN_GROUPS_KEY:" + groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:" + groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:" +) + +func GetGroupInfoKey(groupID string) string { + return groupInfoKey + groupID +} + +func GetJoinedGroupsKey(userID string) string { + return joinedGroupsKey + userID +} + +func GetGroupMembersHashKey(groupID string) string { + return groupMembersHashKey + groupID +} + +func GetGroupMemberIDsKey(groupID string) string { + return groupMemberIDsKey + groupID +} + +func GetGroupMemberInfoKey(groupID, userID string) string { + return groupMemberInfoKey + groupID + "-" + userID +} + +func GetGroupMemberNumKey(groupID string) string { + return groupMemberNumKey + groupID +} + +func GetGroupRoleLevelMemberIDsKey(groupID string, roleLevel int32) string { + return groupRoleLevelMemberIDsKey + groupID + "-" + strconv.Itoa(int(roleLevel)) +} diff --git a/pkg/common/cachekey/msg.go b/pkg/common/cachekey/msg.go new file mode 100644 index 000000000..b19c4f473 --- /dev/null +++ b/pkg/common/cachekey/msg.go @@ -0,0 +1,23 @@ +package cachekey + +const ( + maxSeq = "MAX_SEQ:" + minSeq = "MIN_SEQ:" + conversationUserMinSeq = "CON_USER_MIN_SEQ:" + hasReadSeq = "HAS_READ_SEQ:" + + //appleDeviceToken = "DEVICE_TOKEN" + getuiToken = "GETUI_TOKEN" + getuiTaskID = "GETUI_TASK_ID" + //signalCache = "SIGNAL_CACHE:" + //signalListCache = "SIGNAL_LIST_CACHE:" + FCM_TOKEN = "FCM_TOKEN:" + + messageCache = "MESSAGE_CACHE:" + messageDelUserList = "MESSAGE_DEL_USER_LIST:" + userDelMessagesList = "USER_DEL_MESSAGES_LIST:" + sendMsgFailedFlag = "SEND_MSG_FAILED_FLAG:" + userBadgeUnreadCountSum = "USER_BADGE_UNREAD_COUNT_SUM:" + exTypeKeyLocker = "EX_LOCK:" + uidPidToken = "UID_PID_TOKEN_STATUS:" +) diff --git a/pkg/common/cachekey/s3.go b/pkg/common/cachekey/s3.go new file mode 100644 index 000000000..cbf8bfad5 --- /dev/null +++ b/pkg/common/cachekey/s3.go @@ -0,0 +1 @@ +package cachekey diff --git a/pkg/common/cachekey/user.go b/pkg/common/cachekey/user.go new file mode 100644 index 000000000..ed11641d5 --- /dev/null +++ b/pkg/common/cachekey/user.go @@ -0,0 +1,21 @@ +package cachekey + +import "time" + +const ( + userExpireTime = time.Second * 60 * 60 * 12 + userInfoKey = "USER_INFO:" + userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:" + olineStatusKey = "ONLINE_STATUS:" + userOlineStatusExpireTime = time.Second * 60 * 60 * 24 + statusMod = 501 + platformID = "_PlatformIDSuffix" +) + +func GetUserInfoKey(userID string) string { + return userInfoKey + userID +} + +func GetUserGlobalRecvMsgOptKey(userID string) string { + return userGlobalRecvMsgOptKey + userID +} diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index d1abe945c..f69b83afe 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -16,6 +16,7 @@ package cache import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "time" "github.com/dtm-labs/rockscache" @@ -71,7 +72,7 @@ func (b *BlackCacheRedis) NewCache() BlackCache { } func (b *BlackCacheRedis) getBlackIDsKey(ownerUserID string) string { - return blackIDsKey + ownerUserID + return cachekey.GetBlackIDsKey(ownerUserID) } func (b *BlackCacheRedis) GetBlackIDs(ctx context.Context, userID string) (blackIDs []string, err error) { diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index a7018bc18..cf638f38f 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -17,6 +17,7 @@ package cache import ( "context" "errors" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "math/big" "strings" "time" @@ -30,14 +31,14 @@ import ( ) const ( - conversationKey = "CONVERSATION:" - conversationIDsKey = "CONVERSATION_IDS:" - conversationIDsHashKey = "CONVERSATION_IDS_HASH:" - conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:" - recvMsgOptKey = "RECV_MSG_OPT:" - superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:" - superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:" - conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:" + //conversationKey = "CONVERSATION:" + //conversationIDsKey = "CONVERSATION_IDS:" + //conversationIDsHashKey = "CONVERSATION_IDS_HASH:" + //conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:" + //recvMsgOptKey = "RECV_MSG_OPT:" + //superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:" + //superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:" + //conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:" conversationExpireTime = time.Second * 60 * 60 * 12 ) @@ -125,31 +126,35 @@ func (c *ConversationRedisCache) NewCache() ConversationCache { } func (c *ConversationRedisCache) getConversationKey(ownerUserID, conversationID string) string { - return conversationKey + ownerUserID + ":" + conversationID + return cachekey.GetConversationKey(ownerUserID, conversationID) } func (c *ConversationRedisCache) getConversationIDsKey(ownerUserID string) string { - return conversationIDsKey + ownerUserID + return cachekey.GetConversationIDsKey(ownerUserID) } func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsKey(groupID string) string { - return superGroupRecvMsgNotNotifyUserIDsKey + groupID + return cachekey.GetSuperGroupRecvNotNotifyUserIDsKey(groupID) } func (c *ConversationRedisCache) getRecvMsgOptKey(ownerUserID, conversationID string) string { - return recvMsgOptKey + ownerUserID + ":" + conversationID + return cachekey.GetRecvMsgOptKey(ownerUserID, conversationID) } func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string { - return superGroupRecvMsgNotNotifyUserIDsHashKey + groupID + return cachekey.GetSuperGroupRecvNotNotifyUserIDsHashKey(groupID) } func (c *ConversationRedisCache) getConversationHasReadSeqKey(ownerUserID, conversationID string) string { - return conversationHasReadSeqKey + ownerUserID + ":" + conversationID + return cachekey.GetConversationHasReadSeqKey(ownerUserID, conversationID) } func (c *ConversationRedisCache) getConversationNotReceiveMessageUserIDsKey(conversationID string) string { - return conversationNotReceiveMessageUserIDsKey + conversationID + return cachekey.GetConversationNotReceiveMessageUserIDsKey(conversationID) +} + +func (c *ConversationRedisCache) getUserConversationIDsHashKey(ownerUserID string) string { + return cachekey.GetUserConversationIDsHashKey(ownerUserID) } func (c *ConversationRedisCache) GetUserConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) { @@ -169,10 +174,6 @@ func (c *ConversationRedisCache) DelConversationIDs(userIDs ...string) Conversat return cache } -func (c *ConversationRedisCache) getUserConversationIDsHashKey(ownerUserID string) string { - return conversationIDsHashKey + ownerUserID -} - func (c *ConversationRedisCache) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) { return getCache( ctx, diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index 1708f7664..4805271a3 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -16,6 +16,7 @@ package cache import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "time" "github.com/dtm-labs/rockscache" @@ -27,10 +28,10 @@ import ( ) const ( - friendExpireTime = time.Second * 60 * 60 * 12 - friendIDsKey = "FRIEND_IDS:" - TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:" - friendKey = "FRIEND_INFO:" + friendExpireTime = time.Second * 60 * 60 * 12 + //friendIDsKey = "FRIEND_IDS:" + //TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:" + //friendKey = "FRIEND_INFO:" ) // FriendCache is an interface for caching friend-related data. @@ -78,17 +79,17 @@ func (f *FriendCacheRedis) NewCache() FriendCache { // getFriendIDsKey returns the key for storing friend IDs in the cache. func (f *FriendCacheRedis) getFriendIDsKey(ownerUserID string) string { - return friendIDsKey + ownerUserID + return cachekey.GetFriendIDsKey(ownerUserID) } // getTwoWayFriendsIDsKey returns the key for storing two-way friend IDs in the cache. func (f *FriendCacheRedis) getTwoWayFriendsIDsKey(ownerUserID string) string { - return TwoWayFriendsIDsKey + ownerUserID + return cachekey.GetTwoWayFriendsIDsKey(ownerUserID) } // getFriendKey returns the key for storing friend info in the cache. func (f *FriendCacheRedis) getFriendKey(ownerUserID, friendUserID string) string { - return friendKey + ownerUserID + "-" + friendUserID + return cachekey.GetFriendKey(ownerUserID, friendUserID) } // GetFriendIDs retrieves friend IDs from the cache or the database if not found. diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index 57fcf1a9b..58c42ccfd 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -17,7 +17,7 @@ package cache import ( "context" "fmt" - "strconv" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "time" "github.com/OpenIMSDK/protocol/constant" @@ -34,15 +34,14 @@ import ( ) const ( - groupExpireTime = time.Second * 60 * 60 * 12 - groupInfoKey = "GROUP_INFO:" - groupMemberIDsKey = "GROUP_MEMBER_IDS:" - groupMembersHashKey = "GROUP_MEMBERS_HASH2:" - groupMemberInfoKey = "GROUP_MEMBER_INFO:" - //groupOwnerInfoKey = "GROUP_OWNER_INFO:". - joinedGroupsKey = "JOIN_GROUPS_KEY:" - groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:" - groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:" + groupExpireTime = time.Second * 60 * 60 * 12 + //groupInfoKey = "GROUP_INFO:" + //groupMemberIDsKey = "GROUP_MEMBER_IDS:" + //groupMembersHashKey = "GROUP_MEMBERS_HASH2:" + //groupMemberInfoKey = "GROUP_MEMBER_INFO:" + //joinedGroupsKey = "JOIN_GROUPS_KEY:" + //groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:" + //groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:" ) type GroupHash interface { @@ -126,31 +125,31 @@ func (g *GroupCacheRedis) NewCache() GroupCache { } func (g *GroupCacheRedis) getGroupInfoKey(groupID string) string { - return groupInfoKey + groupID + return cachekey.GetGroupInfoKey(groupID) } func (g *GroupCacheRedis) getJoinedGroupsKey(userID string) string { - return joinedGroupsKey + userID + return cachekey.GetJoinedGroupsKey(userID) } func (g *GroupCacheRedis) getGroupMembersHashKey(groupID string) string { - return groupMembersHashKey + groupID + return cachekey.GetGroupMembersHashKey(groupID) } func (g *GroupCacheRedis) getGroupMemberIDsKey(groupID string) string { - return groupMemberIDsKey + groupID + return cachekey.GetGroupMemberIDsKey(groupID) } func (g *GroupCacheRedis) getGroupMemberInfoKey(groupID, userID string) string { - return groupMemberInfoKey + groupID + "-" + userID + return cachekey.GetGroupMemberInfoKey(groupID, userID) } func (g *GroupCacheRedis) getGroupMemberNumKey(groupID string) string { - return groupMemberNumKey + groupID + return cachekey.GetGroupMemberNumKey(groupID) } func (g *GroupCacheRedis) getGroupRoleLevelMemberIDsKey(groupID string, roleLevel int32) string { - return groupRoleLevelMemberIDsKey + groupID + "-" + strconv.Itoa(int(roleLevel)) + return cachekey.GetGroupRoleLevelMemberIDsKey(groupID, roleLevel) } func (g *GroupCacheRedis) GetGroupIndex(group *relationtb.GroupModel, keys []string) (int, error) { diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 5cd3cb22c..c1749e719 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -44,12 +44,12 @@ const ( conversationUserMinSeq = "CON_USER_MIN_SEQ:" hasReadSeq = "HAS_READ_SEQ:" - appleDeviceToken = "DEVICE_TOKEN" - getuiToken = "GETUI_TOKEN" - getuiTaskID = "GETUI_TASK_ID" - signalCache = "SIGNAL_CACHE:" - signalListCache = "SIGNAL_LIST_CACHE:" - FCM_TOKEN = "FCM_TOKEN:" + //appleDeviceToken = "DEVICE_TOKEN" + getuiToken = "GETUI_TOKEN" + getuiTaskID = "GETUI_TASK_ID" + //signalCache = "SIGNAL_CACHE:" + //signalListCache = "SIGNAL_LIST_CACHE:" + FCM_TOKEN = "FCM_TOKEN:" messageCache = "MESSAGE_CACHE:" messageDelUserList = "MESSAGE_DEL_USER_LIST:" @@ -148,6 +148,10 @@ func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string return hasReadSeq + userID + ":" + conversationID } +func (c *msgCache) getConversationUserMinSeqKey(conversationID, userID string) string { + return conversationUserMinSeq + conversationID + "u:" + userID +} + func (c *msgCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error { return utils.Wrap1(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err()) } @@ -209,10 +213,6 @@ func (c *msgCache) GetMinSeq(ctx context.Context, conversationID string) (int64, return c.getSeq(ctx, conversationID, c.getMinSeqKey) } -func (c *msgCache) getConversationUserMinSeqKey(conversationID, userID string) string { - return conversationUserMinSeq + conversationID + "u:" + userID -} - func (c *msgCache) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) { return utils.Wrap2(c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64()) } diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index 979bd06e4..14ed7988e 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "errors" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "hash/crc32" "strconv" "time" @@ -36,8 +37,8 @@ import ( ) const ( - userExpireTime = time.Second * 60 * 60 * 12 - userInfoKey = "USER_INFO:" + userExpireTime = time.Second * 60 * 60 * 12 + //userInfoKey = "USER_INFO:" userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:" olineStatusKey = "ONLINE_STATUS:" userOlineStatusExpireTime = time.Second * 60 * 60 * 24 @@ -93,18 +94,17 @@ func (u *UserCacheRedis) NewCache() UserCache { } func (u *UserCacheRedis) getUserInfoKey(userID string) string { - return userInfoKey + userID + return cachekey.GetUserInfoKey(userID) } func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string { - return userGlobalRecvMsgOptKey + userID + return cachekey.GetUserGlobalRecvMsgOptKey(userID) } func (u *UserCacheRedis) GetUserInfo(ctx context.Context, userID string) (userInfo *relationtb.UserModel, err error) { return getCache(ctx, u.rcClient, u.getUserInfoKey(userID), u.expireTime, func(ctx context.Context) (*relationtb.UserModel, error) { return u.userDB.Take(ctx, userID) - }, - ) + }) } func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]*relationtb.UserModel, error) { diff --git a/pkg/common/localcache/business.go b/pkg/common/localcache/business.go new file mode 100644 index 000000000..221eb2664 --- /dev/null +++ b/pkg/common/localcache/business.go @@ -0,0 +1,69 @@ +package localcache + +import ( + "context" + "encoding/json" + "github.com/OpenIMSDK/tools/log" + "github.com/dtm-labs/rockscache" + "github.com/redis/go-redis/v9" +) + +func WithRedisDeleteSubscribe(topic string, cli redis.UniversalClient) Option { + return WithDeleteLocal(func(fn func(key ...string)) { + if fn == nil { + log.ZDebug(context.Background(), "WithRedisDeleteSubscribe fn is nil", "topic", topic) + return + } + msg := cli.Subscribe(context.Background(), topic).Channel() + for m := range msg { + var key []string + if err := json.Unmarshal([]byte(m.Payload), &key); err != nil { + log.ZError(context.Background(), "WithRedisDeleteSubscribe json unmarshal error", err, "topic", topic, "payload", m.Payload) + continue + } + if len(key) == 0 { + continue + } + fn(key...) + } + }) +} + +func WithRedisDeletePublish(topic string, cli redis.UniversalClient) Option { + return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { + data, err := json.Marshal(key) + if err != nil { + log.ZError(ctx, "json marshal error", err, "topic", topic, "key", key) + return + } + if err := cli.Publish(ctx, topic, data).Err(); err != nil { + log.ZError(ctx, "redis publish error", err, "topic", topic, "key", key) + } else { + log.ZDebug(ctx, "redis publish success", "topic", topic, "key", key) + } + }) +} + +func WithRedisDelete(cli redis.UniversalClient) Option { + return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { + for _, s := range key { + if err := cli.Del(ctx, s).Err(); err != nil { + log.ZError(ctx, "redis delete error", err, "key", s) + } else { + log.ZDebug(ctx, "redis delete success", "key", s) + } + } + }) +} + +func WithRocksCacheDelete(cli *rockscache.Client) Option { + return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { + for _, k := range key { + if err := cli.TagAsDeleted2(ctx, k); err != nil { + log.ZError(ctx, "rocksdb delete error", err, "key", k) + } else { + log.ZDebug(ctx, "rocksdb delete success", "key", k) + } + } + }) +} diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go new file mode 100644 index 000000000..f70f9435b --- /dev/null +++ b/pkg/common/localcache/cache.go @@ -0,0 +1,66 @@ +package localcache + +import ( + "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" +) + +type Cache[V any] interface { + Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error)) (V, error) + Del(ctx context.Context, key ...string) +} + +func New[V any](opts ...Option) Cache[V] { + opt := defaultOption() + for _, o := range opts { + o(opt) + } + if opt.enable { + lc := local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target) + c := &cache[V]{ + opt: opt, + local: lc, + } + go func() { + c.opt.delCh(c.del) + }() + return c + } else { + return &cache[V]{ + opt: opt, + } + } +} + +type cache[V any] struct { + opt *option + local local.Cache[V] +} + +func (c *cache[V]) del(key ...string) { + for _, k := range key { + c.local.Del(k) + } +} + +func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error)) (V, error) { + if c.opt.enable { + return c.local.Get(key, func() (V, error) { + return fetch(ctx) + }) + } else { + return fetch(ctx) + } +} + +func (c *cache[V]) Del(ctx context.Context, key ...string) { + if len(key) == 0 { + return + } + for _, fn := range c.opt.delFn { + fn(ctx, key...) + } + if c.opt.enable { + c.del(key...) + } +} diff --git a/pkg/common/localcache/local/cache.go b/pkg/common/localcache/local/cache.go new file mode 100644 index 000000000..a957166fe --- /dev/null +++ b/pkg/common/localcache/local/cache.go @@ -0,0 +1,51 @@ +package local + +import ( + "hash/fnv" + "time" + "unsafe" +) + +type Cache[V any] interface { + Get(key string, fetch func() (V, error)) (V, error) + Del(key string) bool +} + +func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target) Cache[V] { + c := &cache[V]{ + n: uint64(slotNum), + slots: make([]*LRU[string, V], slotNum), + target: target, + } + for i := 0; i < slotNum; i++ { + c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target) + } + return c +} + +type cache[V any] struct { + n uint64 + slots []*LRU[string, V] + target Target +} + +func (c *cache[V]) index(s string) uint64 { + h := fnv.New64a() + _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&s))) + //_, _ = h.Write([]byte(s)) + return h.Sum64() % c.n +} + +func (c *cache[V]) Get(key string, fetch func() (V, error)) (V, error) { + return c.slots[c.index(key)].Get(key, fetch) +} + +func (c *cache[V]) Del(key string) bool { + if c.slots[c.index(key)].Del(key) { + c.target.IncrDelHit() + return true + } else { + c.target.IncrDelNotFound() + return false + } +} diff --git a/pkg/common/localcache/local/lru.go b/pkg/common/localcache/local/lru.go new file mode 100644 index 000000000..0befdfcab --- /dev/null +++ b/pkg/common/localcache/local/lru.go @@ -0,0 +1,76 @@ +package local + +import ( + "github.com/hashicorp/golang-lru/v2/simplelru" + "sync" + "time" +) + +type waitItem[V any] struct { + lock sync.Mutex + expires int64 + active bool + err error + value V +} + +func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target) *LRU[K, V] { + core, err := simplelru.NewLRU[K, *waitItem[V]](size, nil) + if err != nil { + panic(err) + } + return &LRU[K, V]{ + core: core, + successTTL: successTTL, + failedTTL: failedTTL, + target: target, + } +} + +type LRU[K comparable, V any] struct { + lock sync.Mutex + core *simplelru.LRU[K, *waitItem[V]] + successTTL time.Duration + failedTTL time.Duration + target Target +} + +func (x *LRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { + x.lock.Lock() + v, ok := x.core.Get(key) + if ok { + x.lock.Unlock() + v.lock.Lock() + expires, value, err := v.expires, v.value, v.err + if expires != 0 && expires > time.Now().UnixMilli() { + v.lock.Unlock() + x.target.IncrGetHit() + return value, err + } + } else { + v = &waitItem[V]{} + x.core.Add(key, v) + v.lock.Lock() + x.lock.Unlock() + } + defer v.lock.Unlock() + if v.expires > time.Now().UnixMilli() { + return v.value, v.err + } + v.value, v.err = fetch() + if v.err == nil { + v.expires = time.Now().Add(x.successTTL).UnixMilli() + x.target.IncrGetSuccess() + } else { + v.expires = time.Now().Add(x.failedTTL).UnixMilli() + x.target.IncrGetFailed() + } + return v.value, v.err +} + +func (x *LRU[K, V]) Del(key K) bool { + x.lock.Lock() + ok := x.core.Remove(key) + x.lock.Unlock() + return ok +} diff --git a/pkg/common/localcache/local/lru_test.go b/pkg/common/localcache/local/lru_test.go new file mode 100644 index 000000000..adbd52277 --- /dev/null +++ b/pkg/common/localcache/local/lru_test.go @@ -0,0 +1,95 @@ +package local + +import ( + "fmt" + "sync" + "sync/atomic" + "testing" + "time" +) + +type cacheTarget struct { + getHit int64 + getSuccess int64 + getFailed int64 + delHit int64 + delNotFound int64 +} + +func (r *cacheTarget) IncrGetHit() { + atomic.AddInt64(&r.getHit, 1) +} + +func (r *cacheTarget) IncrGetSuccess() { + atomic.AddInt64(&r.getSuccess, 1) +} + +func (r *cacheTarget) IncrGetFailed() { + atomic.AddInt64(&r.getFailed, 1) +} + +func (r *cacheTarget) IncrDelHit() { + atomic.AddInt64(&r.delHit, 1) +} + +func (r *cacheTarget) IncrDelNotFound() { + atomic.AddInt64(&r.delNotFound, 1) +} + +func (r *cacheTarget) String() string { + return fmt.Sprintf("getHit: %d, getSuccess: %d, getFailed: %d, delHit: %d, delNotFound: %d", r.getHit, r.getSuccess, r.getFailed, r.delHit, r.delNotFound) +} + +func TestName(t *testing.T) { + target := &cacheTarget{} + l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target) + //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) + + fn := func(key string, n int, fetch func() (string, error)) { + for i := 0; i < n; i++ { + //v, err := l.Get(key, fetch) + //if err == nil { + // t.Log("key", key, "value", v) + //} else { + // t.Error("key", key, err) + //} + l.Get(key, fetch) + //time.Sleep(time.Second / 100) + } + } + + tmp := make(map[string]struct{}) + + var wg sync.WaitGroup + for i := 0; i < 10000; i++ { + wg.Add(1) + key := fmt.Sprintf("key_%d", i%200) + tmp[key] = struct{}{} + go func() { + defer wg.Done() + //t.Log(key) + fn(key, 10000, func() (string, error) { + //time.Sleep(time.Second * 3) + //t.Log(time.Now(), "key", key, "fetch") + //if rand.Uint32()%5 == 0 { + // return "value_" + key, nil + //} + //return "", errors.New("rand error") + return "value_" + key, nil + }) + }() + + //wg.Add(1) + //go func() { + // defer wg.Done() + // for i := 0; i < 10; i++ { + // l.Del(key) + // time.Sleep(time.Second / 3) + // } + //}() + } + wg.Wait() + t.Log(len(tmp)) + t.Log(target.String()) + +} diff --git a/pkg/common/localcache/local/target.go b/pkg/common/localcache/local/target.go new file mode 100644 index 000000000..6cb134fb0 --- /dev/null +++ b/pkg/common/localcache/local/target.go @@ -0,0 +1,10 @@ +package local + +type Target interface { + IncrGetHit() + IncrGetSuccess() + IncrGetFailed() + + IncrDelHit() + IncrDelNotFound() +} diff --git a/pkg/common/localcache/option.go b/pkg/common/localcache/option.go new file mode 100644 index 000000000..01c9ce57d --- /dev/null +++ b/pkg/common/localcache/option.go @@ -0,0 +1,113 @@ +package localcache + +import ( + "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" + "time" +) + +func defaultOption() *option { + return &option{ + enable: true, + localSlotNum: 500, + localSlotSize: 20000, + localSuccessTTL: time.Minute, + localFailedTTL: time.Second * 5, + delFn: make([]func(ctx context.Context, key ...string), 0, 2), + target: emptyTarget{}, + } +} + +type option struct { + enable bool + localSlotNum int + localSlotSize int + localSuccessTTL time.Duration + localFailedTTL time.Duration + delFn []func(ctx context.Context, key ...string) + delCh func(fn func(key ...string)) + target local.Target +} + +type Option func(o *option) + +func WithDisable() Option { + return func(o *option) { + o.enable = false + } +} + +func WithLocalSlotNum(localSlotNum int) Option { + if localSlotNum < 1 { + panic("localSlotNum should be greater than 0") + } + return func(o *option) { + o.localSlotNum = localSlotNum + } +} + +func WithLocalSlotSize(localSlotSize int) Option { + if localSlotSize < 1 { + panic("localSlotSize should be greater than 0") + } + return func(o *option) { + o.localSlotSize = localSlotSize + } +} + +func WithLocalSuccessTTL(localSuccessTTL time.Duration) Option { + if localSuccessTTL < 0 { + panic("localSuccessTTL should be greater than 0") + } + return func(o *option) { + o.localSuccessTTL = localSuccessTTL + } +} + +func WithLocalFailedTTL(localFailedTTL time.Duration) Option { + if localFailedTTL < 0 { + panic("localFailedTTL should be greater than 0") + } + return func(o *option) { + o.localFailedTTL = localFailedTTL + } +} + +func WithTarget(target local.Target) Option { + if target == nil { + panic("target should not be nil") + } + return func(o *option) { + o.target = target + } +} + +func WithDeleteKeyBefore(fn func(ctx context.Context, key ...string)) Option { + if fn == nil { + panic("fn should not be nil") + } + return func(o *option) { + o.delFn = append(o.delFn, fn) + } +} + +func WithDeleteLocal(fn func(fn func(key ...string))) Option { + if fn == nil { + panic("fn should not be nil") + } + return func(o *option) { + o.delCh = fn + } +} + +type emptyTarget struct{} + +func (e emptyTarget) IncrGetHit() {} + +func (e emptyTarget) IncrGetSuccess() {} + +func (e emptyTarget) IncrGetFailed() {} + +func (e emptyTarget) IncrDelHit() {} + +func (e emptyTarget) IncrDelNotFound() {} diff --git a/pkg/common/localcache/tool.go b/pkg/common/localcache/tool.go new file mode 100644 index 000000000..ea3590823 --- /dev/null +++ b/pkg/common/localcache/tool.go @@ -0,0 +1,9 @@ +package localcache + +func AnyValue[V any](v any, err error) (V, error) { + if err != nil { + var zero V + return zero, err + } + return v.(V), nil +} diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go new file mode 100644 index 000000000..0585b2e9d --- /dev/null +++ b/pkg/rpccache/friend.go @@ -0,0 +1,38 @@ +package rpccache + +import ( + "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" +) + +func NewFriendLocalCache(client rpcclient.FriendRpcClient) *FriendLocalCache { + return &FriendLocalCache{ + local: localcache.New[any](), + client: client, + } +} + +type FriendLocalCache struct { + local localcache.Cache[any] + client rpcclient.FriendRpcClient +} + +func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) { + return localcache.AnyValue[[]string](f.local.Get(ctx, cachekey.GetFriendIDsKey(ownerUserID), func(ctx context.Context) (any, error) { + return f.client.GetFriendIDs(ctx, ownerUserID) + })) +} + +func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (bool, error) { + return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { + return f.client.IsFriend(ctx, possibleFriendUserID, userID) + })) +} + +func (f *FriendLocalCache) IsBlocked(ctx context.Context, possibleBlackUserID, userID string) (bool, error) { + return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { + return f.client.IsFriend(ctx, possibleBlackUserID, userID) + })) +} From 1ae523dcc2d644f244d13fc1732fd2c6187b641f Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 8 Jan 2024 15:41:57 +0800 Subject: [PATCH 02/66] feat: msg local cache --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index f10e123a0..71e25a6c4 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/IBM/sarama v1.41.3 github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/go-redis/redis v6.15.9+incompatible + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/redis/go-redis/v9 v9.2.1 github.com/stathat/consistent v1.0.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 diff --git a/go.sum b/go.sum index 34f5d3ae9..e58e8c626 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= From 3d77c1c8cf53fc999d3adb3ae104eeeaef9aac6d Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 8 Jan 2024 16:47:39 +0800 Subject: [PATCH 03/66] feat: msg local cache --- pkg/common/localcache/cache.go | 25 ++++++++----------- pkg/common/localcache/local/cache.go | 15 ++++++------ pkg/common/localcache/local/callback.go | 5 ++++ pkg/common/localcache/local/lru.go | 10 ++++++-- pkg/common/localcache/local/lru_test.go | 2 +- pkg/common/localcache/option/option.go | 32 +++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 pkg/common/localcache/local/callback.go create mode 100644 pkg/common/localcache/option/option.go diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index f70f9435b..9b8613ecf 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -15,21 +15,12 @@ func New[V any](opts ...Option) Cache[V] { for _, o := range opts { o(opt) } - if opt.enable { - lc := local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target) - c := &cache[V]{ - opt: opt, - local: lc, - } - go func() { - c.opt.delCh(c.del) - }() - return c - } else { - return &cache[V]{ - opt: opt, - } - } + c := &cache[V]{opt: opt} + c.local = local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + go func() { + c.opt.delCh(c.del) + }() + return c } type cache[V any] struct { @@ -37,6 +28,10 @@ type cache[V any] struct { local local.Cache[V] } +func (c *cache[V]) onEvict(key string, value V) { + +} + func (c *cache[V]) del(key ...string) { for _, k := range key { c.local.Del(k) diff --git a/pkg/common/localcache/local/cache.go b/pkg/common/localcache/local/cache.go index a957166fe..06569965e 100644 --- a/pkg/common/localcache/local/cache.go +++ b/pkg/common/localcache/local/cache.go @@ -11,36 +11,35 @@ type Cache[V any] interface { Del(key string) bool } -func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target) Cache[V] { - c := &cache[V]{ +func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[string, V]) Cache[V] { + c := &slot[V]{ n: uint64(slotNum), slots: make([]*LRU[string, V], slotNum), target: target, } for i := 0; i < slotNum; i++ { - c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target) + c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict) } return c } -type cache[V any] struct { +type slot[V any] struct { n uint64 slots []*LRU[string, V] target Target } -func (c *cache[V]) index(s string) uint64 { +func (c *slot[V]) index(s string) uint64 { h := fnv.New64a() _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&s))) - //_, _ = h.Write([]byte(s)) return h.Sum64() % c.n } -func (c *cache[V]) Get(key string, fetch func() (V, error)) (V, error) { +func (c *slot[V]) Get(key string, fetch func() (V, error)) (V, error) { return c.slots[c.index(key)].Get(key, fetch) } -func (c *cache[V]) Del(key string) bool { +func (c *slot[V]) Del(key string) bool { if c.slots[c.index(key)].Del(key) { c.target.IncrDelHit() return true diff --git a/pkg/common/localcache/local/callback.go b/pkg/common/localcache/local/callback.go new file mode 100644 index 000000000..32aef112b --- /dev/null +++ b/pkg/common/localcache/local/callback.go @@ -0,0 +1,5 @@ +package local + +import "github.com/hashicorp/golang-lru/v2/simplelru" + +type EvictCallback[K comparable, V any] simplelru.EvictCallback[K, V] diff --git a/pkg/common/localcache/local/lru.go b/pkg/common/localcache/local/lru.go index 0befdfcab..45dc3b651 100644 --- a/pkg/common/localcache/local/lru.go +++ b/pkg/common/localcache/local/lru.go @@ -14,8 +14,14 @@ type waitItem[V any] struct { value V } -func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target) *LRU[K, V] { - core, err := simplelru.NewLRU[K, *waitItem[V]](size, nil) +func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *LRU[K, V] { + var cb simplelru.EvictCallback[K, *waitItem[V]] + if onEvict != nil { + cb = func(key K, value *waitItem[V]) { + onEvict(key, value.value) + } + } + core, err := simplelru.NewLRU[K, *waitItem[V]](size, cb) if err != nil { panic(err) } diff --git a/pkg/common/localcache/local/lru_test.go b/pkg/common/localcache/local/lru_test.go index adbd52277..a6e7553ee 100644 --- a/pkg/common/localcache/local/lru_test.go +++ b/pkg/common/localcache/local/lru_test.go @@ -42,7 +42,7 @@ func (r *cacheTarget) String() string { func TestName(t *testing.T) { target := &cacheTarget{} - l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target) + l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) fn := func(key string, n int, fetch func() (string, error)) { diff --git a/pkg/common/localcache/option/option.go b/pkg/common/localcache/option/option.go new file mode 100644 index 000000000..adaaa3a45 --- /dev/null +++ b/pkg/common/localcache/option/option.go @@ -0,0 +1,32 @@ +package option + +var ( + t = true + f = false +) + +type Option struct { + enable *bool + key []string +} + +func (o *Option) Enable() *Option { + o.enable = &t + return o +} + +func (o *Option) Disable() *Option { + o.enable = &f + return o +} + +func (o *Option) DelKey(key ...string) *Option { + if len(key) > 0 { + if o.key == nil { + o.key = key + } else { + o.key = append(o.key, key...) + } + } + return o +} From 938622b1fed13d9a9d86bd9856b6c2377009638d Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 8 Jan 2024 20:36:41 +0800 Subject: [PATCH 04/66] feat: msg local cache --- internal/msggateway/callback.go | 6 +- pkg/common/localcache/cache.go | 24 ++++-- pkg/common/localcache/link/link.go | 109 ++++++++++++++++++++++++ pkg/common/localcache/link/link_test.go | 24 ++++++ pkg/common/localcache/option/option.go | 29 ++++--- pkg/rpccache/friend.go | 7 +- 6 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 pkg/common/localcache/link/link.go create mode 100644 pkg/common/localcache/link/link_test.go diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go index 7d5381754..d9507c85e 100644 --- a/internal/msggateway/callback.go +++ b/internal/msggateway/callback.go @@ -105,7 +105,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err // func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID // string) cbApi.CommonCallbackResp { // callbackResp := cbApi.CommonCallbackResp{OperationID: operationID} -// if !config.Config.Callback.CallbackUserOnline.Enable { +// if !config.Config.Callback.CallbackUserOnline.WithEnable { // return callbackResp // } // callbackUserOnlineReq := cbApi.CallbackUserOnlineReq{ @@ -134,7 +134,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err //} //func callbackUserOffline(operationID, userID string, platformID int, connID string) cbApi.CommonCallbackResp { // callbackResp := cbApi.CommonCallbackResp{OperationID: operationID} -// if !config.Config.Callback.CallbackUserOffline.Enable { +// if !config.Config.Callback.CallbackUserOffline.WithEnable { // return callbackResp // } // callbackOfflineReq := cbApi.CallbackUserOfflineReq{ @@ -161,7 +161,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err //} //func callbackUserKickOff(operationID string, userID string, platformID int) cbApi.CommonCallbackResp { // callbackResp := cbApi.CommonCallbackResp{OperationID: operationID} -// if !config.Config.Callback.CallbackUserKickOff.Enable { +// if !config.Config.Callback.CallbackUserKickOff.WithEnable { // return callbackResp // } // callbackUserKickOffReq := cbApi.CallbackUserKickOffReq{ diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index 9b8613ecf..64df1fe02 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -2,11 +2,13 @@ package localcache import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/link" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" + opt "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" ) type Cache[V any] interface { - Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error)) (V, error) + Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) Del(ctx context.Context, key ...string) } @@ -15,7 +17,7 @@ func New[V any](opts ...Option) Cache[V] { for _, o := range opts { o(opt) } - c := &cache[V]{opt: opt} + c := &cache[V]{opt: opt, link: link.New(opt.localSlotNum)} c.local = local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) go func() { c.opt.delCh(c.del) @@ -25,11 +27,16 @@ func New[V any](opts ...Option) Cache[V] { type cache[V any] struct { opt *option + link link.Link local local.Cache[V] } func (c *cache[V]) onEvict(key string, value V) { - + for k := range c.link.Del(key) { + if key != k { + c.local.Del(k) + } + } } func (c *cache[V]) del(key ...string) { @@ -38,8 +45,15 @@ func (c *cache[V]) del(key ...string) { } } -func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error)) (V, error) { - if c.opt.enable { +func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) { + enable := c.opt.enable + if len(opts) > 0 && opts[0].Enable != nil { + enable = *opts[0].Enable + } + if enable { + if len(opts) > 0 && len(opts[0].Link) > 0 { + c.link.Link(key, opts[0].Link...) + } return c.local.Get(key, func() (V, error) { return fetch(ctx) }) diff --git a/pkg/common/localcache/link/link.go b/pkg/common/localcache/link/link.go new file mode 100644 index 000000000..4f238907b --- /dev/null +++ b/pkg/common/localcache/link/link.go @@ -0,0 +1,109 @@ +package link + +import ( + "hash/fnv" + "sync" + "unsafe" +) + +type Link interface { + Link(key string, link ...string) + Del(key string) map[string]struct{} +} + +func newLinkKey() *linkKey { + return &linkKey{ + data: make(map[string]map[string]struct{}), + } +} + +type linkKey struct { + lock sync.Mutex + data map[string]map[string]struct{} +} + +func (x *linkKey) link(key string, link ...string) { + x.lock.Lock() + defer x.lock.Unlock() + v, ok := x.data[key] + if !ok { + v = make(map[string]struct{}) + x.data[key] = v + } + for _, k := range link { + v[k] = struct{}{} + } +} + +func (x *linkKey) del(key string) map[string]struct{} { + x.lock.Lock() + defer x.lock.Unlock() + ks, ok := x.data[key] + if !ok { + return nil + } + delete(x.data, key) + return ks +} + +func New(n int) Link { + if n <= 0 { + panic("must be greater than 0") + } + slots := make([]*linkKey, n) + for i := 0; i < len(slots); i++ { + slots[i] = newLinkKey() + } + return &slot{ + n: uint64(n), + slots: slots, + } +} + +type slot struct { + n uint64 + slots []*linkKey +} + +func (x *slot) index(s string) uint64 { + h := fnv.New64a() + _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&s))) + return h.Sum64() % x.n +} + +func (x *slot) Link(key string, link ...string) { + if len(link) == 0 { + return + } + mk := key + lks := make([]string, len(link)) + for i, k := range link { + lks[i] = k + } + x.slots[x.index(mk)].link(mk, lks...) + for _, lk := range lks { + x.slots[x.index(lk)].link(lk, mk) + } +} + +func (x *slot) Del(key string) map[string]struct{} { + return x.delKey(key) +} + +func (x *slot) delKey(k string) map[string]struct{} { + del := make(map[string]struct{}) + stack := []string{k} + for len(stack) > 0 { + curr := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if _, ok := del[curr]; ok { + continue + } + del[curr] = struct{}{} + childKeys := x.slots[x.index(curr)].del(curr) + for ck := range childKeys { + stack = append(stack, ck) + } + } + return del +} diff --git a/pkg/common/localcache/link/link_test.go b/pkg/common/localcache/link/link_test.go new file mode 100644 index 000000000..994c26ec7 --- /dev/null +++ b/pkg/common/localcache/link/link_test.go @@ -0,0 +1,24 @@ +package link + +import ( + "testing" +) + +func TestName(t *testing.T) { + + v := New(1) + + //v.Link("a:1", "b:1", "c:1", "d:1") + v.Link("a:1", "b:1", "c:1") + v.Link("z:1", "b:1") + + //v.DelKey("a:1") + v.Del("z:1") + + t.Log(v.slots[0].data) + + for k, v := range v.slots[0].data { + t.Log(k, v) + } + +} diff --git a/pkg/common/localcache/option/option.go b/pkg/common/localcache/option/option.go index adaaa3a45..798c93ba5 100644 --- a/pkg/common/localcache/option/option.go +++ b/pkg/common/localcache/option/option.go @@ -1,31 +1,32 @@ package option -var ( - t = true - f = false -) +func NewOption() *Option { + return &Option{} +} type Option struct { - enable *bool - key []string + Enable *bool + Link []string } -func (o *Option) Enable() *Option { - o.enable = &t +func (o *Option) WithEnable() *Option { + t := true + o.Enable = &t return o } -func (o *Option) Disable() *Option { - o.enable = &f +func (o *Option) WithDisable() *Option { + f := false + o.Enable = &f return o } -func (o *Option) DelKey(key ...string) *Option { +func (o *Option) WithLink(key ...string) *Option { if len(key) > 0 { - if o.key == nil { - o.key = key + if len(o.Link) == 0 { + o.Link = key } else { - o.key = append(o.key, key...) + o.Link = append(o.Link, key...) } } return o diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 0585b2e9d..68c6a736a 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -4,6 +4,7 @@ import ( "context" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) @@ -28,11 +29,11 @@ func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (bool, error) { return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { return f.client.IsFriend(ctx, possibleFriendUserID, userID) - })) + }, option.NewOption().WithLink(cachekey.GetFriendIDsKey(possibleFriendUserID), cachekey.GetFriendIDsKey(userID)))) } func (f *FriendLocalCache) IsBlocked(ctx context.Context, possibleBlackUserID, userID string) (bool, error) { return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { - return f.client.IsFriend(ctx, possibleBlackUserID, userID) - })) + return f.client.IsBlocked(ctx, possibleBlackUserID, userID) + }, option.NewOption().WithLink(cachekey.GetBlackIDsKey(possibleBlackUserID), cachekey.GetBlackIDsKey(userID)))) } From 0248fbb47da10fda4f66b8ca5d98d1e9becb75d0 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 8 Jan 2024 20:38:23 +0800 Subject: [PATCH 05/66] feat: msg local cache --- pkg/common/localcache/link/link_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/common/localcache/link/link_test.go b/pkg/common/localcache/link/link_test.go index 994c26ec7..ed684e693 100644 --- a/pkg/common/localcache/link/link_test.go +++ b/pkg/common/localcache/link/link_test.go @@ -15,10 +15,6 @@ func TestName(t *testing.T) { //v.DelKey("a:1") v.Del("z:1") - t.Log(v.slots[0].data) - - for k, v := range v.slots[0].data { - t.Log(k, v) - } + t.Log(v) } From bbb5ef5cccbff2f4ff2c6f64538b1cda02509a16 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 9 Jan 2024 17:01:50 +0800 Subject: [PATCH 06/66] feat: msg local cache --- deployments/templates/openim.yaml | 75 +++++++++++++++++-------------- internal/rpc/msg/server.go | 2 +- pkg/common/config/config.go | 12 +++++ pkg/common/db/cache/black.go | 7 ++- pkg/common/db/cache/friend.go | 6 ++- pkg/common/db/cache/meta_cache.go | 46 +++++++++---------- pkg/common/localcache/business.go | 1 + pkg/common/localcache/cache.go | 4 +- pkg/rpccache/friend.go | 6 ++- 9 files changed, 93 insertions(+), 66 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 6880e4c4e..7f33fa015 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -31,7 +31,7 @@ envs: # Zookeeper password zookeeper: schema: ${ZOOKEEPER_SCHEMA} - address: [ ${ZOOKEEPER_ADDRESS}:${ZOOKEEPER_PORT} ] + address: [ ${ ZOOKEEPER_ADDRESS }:${ ZOOKEEPER_PORT } ] username: ${ZOOKEEPER_USERNAME} password: ${ZOOKEEPER_PASSWORD} @@ -44,14 +44,14 @@ zookeeper: mongo: uri: ${MONGO_URI} -# List of MongoDB server addresses. -# Used for constructing the MongoDB URI if 'uri' above is empty. -# For a standalone setup, specify the address of the single server. -# For a sharded cluster, specify the addresses of the Mongos servers. -# Example: [ '172.28.0.1:37017', '172.28.0.2:37017' ] -# Default MongoDB database name -# Maximum connection pool size - address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ] + # List of MongoDB server addresses. + # Used for constructing the MongoDB URI if 'uri' above is empty. + # For a standalone setup, specify the address of the single server. + # For a sharded cluster, specify the addresses of the Mongos servers. + # Example: [ '172.28.0.1:37017', '172.28.0.2:37017' ] + # Default MongoDB database name + # Maximum connection pool size + address: [ ${ MONGO_ADDRESS }:${ MONGO_PORT } ] database: ${MONGO_DATABASE} username: ${MONGO_OPENIM_USERNAME} password: ${MONGO_OPENIM_PASSWORD} @@ -62,7 +62,7 @@ mongo: # # Username is required only for Redis version 6.0+ redis: - address: [ ${REDIS_ADDRESS}:${REDIS_PORT} ] + address: [ ${ REDIS_ADDRESS }:${ REDIS_PORT } ] username: ${REDIS_USERNAME} password: ${REDIS_PASSWORD} @@ -76,7 +76,7 @@ redis: kafka: username: ${KAFKA_USERNAME} password: ${KAFKA_PASSWORD} - addr: [ ${KAFKA_ADDRESS}:${KAFKA_PORT} ] + addr: [ ${ KAFKA_ADDRESS }:${ KAFKA_PORT } ] latestMsgToRedis: topic: "${KAFKA_LATESTMSG_REDIS_TOPIC}" offlineMsgToMongo: @@ -104,7 +104,7 @@ rpc: # API service port # Default listen IP is 0.0.0.0 api: - openImApiPort: [ ${API_OPENIM_PORT} ] + openImApiPort: [ ${ API_OPENIM_PORT } ] listenIP: ${API_LISTEN_IP} ###################### Object configuration information ###################### @@ -160,14 +160,14 @@ object: # For launching multiple programs, just fill in multiple ports separated by commas # For example, [10110, 10111] rpcPort: - openImUserPort: [ ${OPENIM_USER_PORT} ] - openImFriendPort: [ ${OPENIM_FRIEND_PORT} ] - openImMessagePort: [ ${OPENIM_MESSAGE_PORT} ] - openImGroupPort: [ ${OPENIM_GROUP_PORT} ] - openImAuthPort: [ ${OPENIM_AUTH_PORT} ] - openImPushPort: [ ${OPENIM_PUSH_PORT} ] - openImConversationPort: [ ${OPENIM_CONVERSATION_PORT} ] - openImThirdPort: [ ${OPENIM_THIRD_PORT} ] + openImUserPort: [ ${ OPENIM_USER_PORT } ] + openImFriendPort: [ ${ OPENIM_FRIEND_PORT } ] + openImMessagePort: [ ${ OPENIM_MESSAGE_PORT } ] + openImGroupPort: [ ${ OPENIM_GROUP_PORT } ] + openImAuthPort: [ ${ OPENIM_AUTH_PORT } ] + openImPushPort: [ ${ OPENIM_PUSH_PORT } ] + openImConversationPort: [ ${ OPENIM_CONVERSATION_PORT } ] + openImThirdPort: [ ${ OPENIM_THIRD_PORT } ] ###################### RPC Register Name Configuration ###################### # RPC service names for registration, it's not recommended to modify these @@ -209,9 +209,9 @@ log: # Maximum length of websocket request package # Websocket connection handshake timeout longConnSvr: - openImWsPort: [ ${OPENIM_WS_PORT} ] + openImWsPort: [ ${ OPENIM_WS_PORT } ] websocketMaxConnNum: ${WEBSOCKET_MAX_CONN_NUM} - openImMessageGatewayPort: [ ${OPENIM_MESSAGE_GATEWAY_PORT} ] + openImMessageGatewayPort: [ ${ OPENIM_MESSAGE_GATEWAY_PORT } ] websocketMaxMsgLen: ${WEBSOCKET_MAX_MSG_LEN} websocketTimeout: ${WEBSOCKET_TIMEOUT} @@ -507,15 +507,22 @@ callback: prometheus: enable: ${PROMETHEUS_ENABLE} grafanaUrl: ${GRAFANA_URL} - apiPrometheusPort: [${API_PROM_PORT}] - userPrometheusPort: [ ${USER_PROM_PORT} ] - friendPrometheusPort: [ ${FRIEND_PROM_PORT} ] - messagePrometheusPort: [ ${MESSAGE_PROM_PORT} ] - messageGatewayPrometheusPort: [ ${MSG_GATEWAY_PROM_PORT} ] - groupPrometheusPort: [ ${GROUP_PROM_PORT} ] - authPrometheusPort: [ ${AUTH_PROM_PORT} ] - pushPrometheusPort: [ ${PUSH_PROM_PORT} ] - conversationPrometheusPort: [ ${CONVERSATION_PROM_PORT} ] - rtcPrometheusPort: [ ${RTC_PROM_PORT} ] - thirdPrometheusPort: [ ${THIRD_PROM_PORT} ] - messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports + apiPrometheusPort: [ ${ API_PROM_PORT } ] + userPrometheusPort: [ ${ USER_PROM_PORT } ] + friendPrometheusPort: [ ${ FRIEND_PROM_PORT } ] + messagePrometheusPort: [ ${ MESSAGE_PROM_PORT } ] + messageGatewayPrometheusPort: [ ${ MSG_GATEWAY_PROM_PORT } ] + groupPrometheusPort: [ ${ GROUP_PROM_PORT } ] + authPrometheusPort: [ ${ AUTH_PROM_PORT } ] + pushPrometheusPort: [ ${ PUSH_PROM_PORT } ] + conversationPrometheusPort: [ ${ CONVERSATION_PROM_PORT } ] + rtcPrometheusPort: [ ${ RTC_PROM_PORT } ] + thirdPrometheusPort: [ ${ THIRD_PROM_PORT } ] + messageTransferPrometheusPort: [ ${ MSG_TRANSFER_PROM_PORT } ] # List of ports + +localCache: + friend: + topic: "friend" + slotNum: 500 + slotSize: 20000 + diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 9b1965b00..0600031cd 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -90,7 +90,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e RegisterCenter: client, GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient), ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient), - friend: rpccache.NewFriendLocalCache(rpcclient.NewFriendRpcClient(client)), + friend: rpccache.NewFriendLocalCache(rpcclient.NewFriendRpcClient(client), rdb), } s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg)) s.addInterceptorHandler(MessageHasReadEnabled) diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 88e87e709..0b31220c9 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -258,6 +258,8 @@ type configStruct struct { FriendVerify *bool `yaml:"friendVerify"` } `yaml:"messageVerify"` + LocalCache localCache `yaml:"localCache"` + IOSPush struct { PushSound string `yaml:"pushSound"` BadgeCount bool `yaml:"badgeCount"` @@ -370,6 +372,16 @@ type notification struct { ConversationSetPrivate NotificationConf `yaml:"conversationSetPrivate"` } +type LocalCache struct { + Topic string `yaml:"topic"` + SlotNum int `yaml:"slotNum"` + SlotSize int `yaml:"slotSize"` +} + +type localCache struct { + Friend LocalCache `yaml:"friend"` +} + func (c *configStruct) GetServiceNames() []string { return []string{ c.RpcRegisterName.OpenImUserName, diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index f69b83afe..e98f7de7b 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -17,6 +17,7 @@ package cache import ( "context" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" "github.com/dtm-labs/rockscache" @@ -53,11 +54,13 @@ func NewBlackCacheRedis( options rockscache.Options, ) BlackCache { rcClient := rockscache.NewClient(rdb, options) - + mc := NewMetaCacheRedis(rcClient) + mc.SetTopic(config.Config.LocalCache.Friend.Topic) + mc.SetRawRedisClient(rdb) return &BlackCacheRedis{ expireTime: blackExpireTime, rcClient: rcClient, - metaCache: NewMetaCacheRedis(rcClient), + metaCache: mc, blackDB: blackDB, } } diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index 4805271a3..160c6df95 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -17,6 +17,7 @@ package cache import ( "context" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" "github.com/dtm-labs/rockscache" @@ -59,8 +60,11 @@ type FriendCacheRedis struct { func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendModelInterface, options rockscache.Options) FriendCache { rcClient := rockscache.NewClient(rdb, options) + mc := NewMetaCacheRedis(rcClient) + mc.SetTopic(config.Config.LocalCache.Friend.Topic) + mc.SetRawRedisClient(rdb) return &FriendCacheRedis{ - metaCache: NewMetaCacheRedis(rcClient), + metaCache: mc, friendDB: friendDB, expireTime: friendExpireTime, rcClient: rcClient, diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 4bc2a046a..863f6d243 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "errors" + "github.com/redis/go-redis/v9" "time" "github.com/OpenIMSDK/tools/mw/specialerror" @@ -44,6 +45,8 @@ type metaCache interface { AddKeys(keys ...string) ClearKeys() GetPreDelKeys() []string + SetTopic(topic string) + SetRawRedisClient(cli redis.UniversalClient) } func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache { @@ -51,10 +54,20 @@ func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache { } type metaCacheRedis struct { + topic string rcClient *rockscache.Client keys []string maxRetryTimes int retryInterval time.Duration + redisClient redis.UniversalClient +} + +func (m *metaCacheRedis) SetTopic(topic string) { + m.topic = topic +} + +func (m *metaCacheRedis) SetRawRedisClient(cli redis.UniversalClient) { + m.redisClient = cli } func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error { @@ -72,31 +85,18 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error { } break } - - //retryTimes := 0 - //for { - // m.rcClient.TagAsDeleted() - // if err := m.rcClient.TagAsDeletedBatch2(ctx, []string{key}); err != nil { - // if retryTimes >= m.maxRetryTimes { - // err = errs.ErrInternalServer.Wrap( - // fmt.Sprintf( - // "delete cache error: %v, keys: %v, retry times %d, please check redis server", - // err, - // key, - // retryTimes, - // ), - // ) - // log.ZWarn(ctx, "delete cache failed, please handle keys", err, "keys", key) - // return err - // } - // retryTimes++ - // } else { - // break - // } - //} + } + if m.topic != "" && m.redisClient != nil { + data, err := json.Marshal(m.keys) + if err != nil { + log.ZError(ctx, "keys json marshal failed", err, "topic", m.topic, "keys", m.keys) + } else { + if err := m.redisClient.Publish(ctx, m.topic, string(data)).Err(); err != nil { + log.ZError(ctx, "redis publish cache delete error", err, "topic", m.topic, "keys", m.keys) + } + } } } - return nil } diff --git a/pkg/common/localcache/business.go b/pkg/common/localcache/business.go index 221eb2664..f011719df 100644 --- a/pkg/common/localcache/business.go +++ b/pkg/common/localcache/business.go @@ -16,6 +16,7 @@ func WithRedisDeleteSubscribe(topic string, cli redis.UniversalClient) Option { } msg := cli.Subscribe(context.Background(), topic).Channel() for m := range msg { + log.ZDebug(context.Background(), "WithRedisDeleteSubscribe delete", "topic", m.Channel, "payload", m.Payload) var key []string if err := json.Unmarshal([]byte(m.Payload), &key); err != nil { log.ZError(context.Background(), "WithRedisDeleteSubscribe json unmarshal error", err, "topic", topic, "payload", m.Payload) diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index 64df1fe02..77e77ce5f 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -33,9 +33,7 @@ type cache[V any] struct { func (c *cache[V]) onEvict(key string, value V) { for k := range c.link.Del(key) { - if key != k { - c.local.Del(k) - } + c.local.Del(k) } } diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 68c6a736a..737f674ab 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -3,14 +3,16 @@ package rpccache import ( "context" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" ) -func NewFriendLocalCache(client rpcclient.FriendRpcClient) *FriendLocalCache { +func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalClient) *FriendLocalCache { return &FriendLocalCache{ - local: localcache.New[any](), + local: localcache.New[any](localcache.WithRedisDeleteSubscribe(config.Config.LocalCache.Friend.Topic, cli)), client: client, } } From 86aebfc0bdc7f039afb56650f3c11ca18f6a16e9 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 9 Jan 2024 17:36:24 +0800 Subject: [PATCH 07/66] fix: mongo --- pkg/common/db/unrelation/mongo.go | 4 ++-- tools/component/component.go | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index b8184d767..583789b73 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -103,9 +103,9 @@ func buildMongoURI() string { maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize) } - uriFormat := "mongodb://%s/%s?maxPoolSize=%s" + uriFormat := "mongodb://%s/%s?maxPoolSize=%s&authSource=admin" if username != "" && password != "" { - uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s" + uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s&authSource=admin" return fmt.Sprintf(uriFormat, username, password, address, database, maxPoolSize) } return fmt.Sprintf(uriFormat, address, database, maxPoolSize) diff --git a/tools/component/component.go b/tools/component/component.go index 62f2c60ac..e75c41365 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -33,10 +33,10 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7" - "github.com/redis/go-redis/v9" - "gopkg.in/yaml.v3" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/redis/go-redis/v9" + "gopkg.in/yaml.v3" ) const ( @@ -145,7 +145,7 @@ func getEnv(key, fallback string) string { func checkMongo() (string, error) { // Use environment variables or fallback to config uri := getEnv("MONGO_URI", buildMongoURI()) - + fmt.Println("mongo uri", uri) client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri)) str := "ths addr is:" + strings.Join(config.Config.Mongo.Address, ",") if err != nil { @@ -171,10 +171,10 @@ func buildMongoURI() string { mongodbHosts := strings.Join(config.Config.Mongo.Address, ",") if username != "" && password != "" { - return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d", + return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d&authSource=admin", username, password, mongodbHosts, database, maxPoolSize) } - return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d", + return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d&authSource=admin", mongodbHosts, database, maxPoolSize) } @@ -184,7 +184,6 @@ func checkMinio() (string, error) { if config.Config.Object.Enable != "minio" { return "", nil } - // Prioritize environment variables endpoint := getEnv("MINIO_ENDPOINT", config.Config.Object.Minio.Endpoint) accessKeyID := getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID) From f0075265d5d9abe4c1ec51978ed538693530bc91 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 10:18:05 +0800 Subject: [PATCH 08/66] fix: mongo --- pkg/common/db/unrelation/mongo.go | 4 ++-- tools/component/component.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 583789b73..b8184d767 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -103,9 +103,9 @@ func buildMongoURI() string { maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize) } - uriFormat := "mongodb://%s/%s?maxPoolSize=%s&authSource=admin" + uriFormat := "mongodb://%s/%s?maxPoolSize=%s" if username != "" && password != "" { - uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s&authSource=admin" + uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s" return fmt.Sprintf(uriFormat, username, password, address, database, maxPoolSize) } return fmt.Sprintf(uriFormat, address, database, maxPoolSize) diff --git a/tools/component/component.go b/tools/component/component.go index e75c41365..b704ce845 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -171,10 +171,10 @@ func buildMongoURI() string { mongodbHosts := strings.Join(config.Config.Mongo.Address, ",") if username != "" && password != "" { - return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d&authSource=admin", + return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d", username, password, mongodbHosts, database, maxPoolSize) } - return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d&authSource=admin", + return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d", mongodbHosts, database, maxPoolSize) } From 22c00c411e4fdcc118cb6bf2288a407f4ccd0645 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 10:32:18 +0800 Subject: [PATCH 09/66] fix: mongo --- deployments/templates/openim.yaml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 7f33fa015..99b83a27d 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -518,11 +518,4 @@ prometheus: conversationPrometheusPort: [ ${ CONVERSATION_PROM_PORT } ] rtcPrometheusPort: [ ${ RTC_PROM_PORT } ] thirdPrometheusPort: [ ${ THIRD_PROM_PORT } ] - messageTransferPrometheusPort: [ ${ MSG_TRANSFER_PROM_PORT } ] # List of ports - -localCache: - friend: - topic: "friend" - slotNum: 500 - slotSize: 20000 - + messageTransferPrometheusPort: [ ${ MSG_TRANSFER_PROM_PORT } ] # List of ports \ No newline at end of file From 8360c72300a6dd9ca93bceabb1ba7f546bae4137 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 11:12:52 +0800 Subject: [PATCH 10/66] openim.yaml --- deployments/templates/openim.yaml | 68 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 664bedad5..4c84373a5 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -31,7 +31,7 @@ envs: # Zookeeper password zookeeper: schema: ${ZOOKEEPER_SCHEMA} - address: [ ${ ZOOKEEPER_ADDRESS }:${ ZOOKEEPER_PORT } ] + address: [ ${ZOOKEEPER_ADDRESS}:${ZOOKEEPER_PORT} ] username: ${ZOOKEEPER_USERNAME} password: ${ZOOKEEPER_PASSWORD} @@ -44,14 +44,14 @@ zookeeper: mongo: uri: ${MONGO_URI} - # List of MongoDB server addresses. - # Used for constructing the MongoDB URI if 'uri' above is empty. - # For a standalone setup, specify the address of the single server. - # For a sharded cluster, specify the addresses of the Mongos servers. - # Example: [ '172.28.0.1:37017', '172.28.0.2:37017' ] - # Default MongoDB database name - # Maximum connection pool size - address: [ ${ MONGO_ADDRESS }:${ MONGO_PORT } ] +# List of MongoDB server addresses. +# Used for constructing the MongoDB URI if 'uri' above is empty. +# For a standalone setup, specify the address of the single server. +# For a sharded cluster, specify the addresses of the Mongos servers. +# Example: [ '172.28.0.1:37017', '172.28.0.2:37017' ] +# Default MongoDB database name +# Maximum connection pool size + address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ] database: ${MONGO_DATABASE} username: ${MONGO_OPENIM_USERNAME} password: ${MONGO_OPENIM_PASSWORD} @@ -62,7 +62,7 @@ mongo: # # Username is required only for Redis version 6.0+ redis: - address: [ ${ REDIS_ADDRESS }:${ REDIS_PORT } ] + address: [ ${REDIS_ADDRESS}:${REDIS_PORT} ] username: ${REDIS_USERNAME} password: ${REDIS_PASSWORD} @@ -76,7 +76,7 @@ redis: kafka: username: ${KAFKA_USERNAME} password: ${KAFKA_PASSWORD} - addr: [ ${ KAFKA_ADDRESS }:${ KAFKA_PORT } ] + addr: [ ${KAFKA_ADDRESS}:${KAFKA_PORT} ] latestMsgToRedis: topic: "${KAFKA_LATESTMSG_REDIS_TOPIC}" offlineMsgToMongo: @@ -104,7 +104,7 @@ rpc: # API service port # Default listen IP is 0.0.0.0 api: - openImApiPort: [ ${ API_OPENIM_PORT } ] + openImApiPort: [ ${API_OPENIM_PORT} ] listenIP: ${API_LISTEN_IP} ###################### Object configuration information ###################### @@ -160,14 +160,14 @@ object: # For launching multiple programs, just fill in multiple ports separated by commas # For example, [10110, 10111] rpcPort: - openImUserPort: [ ${ OPENIM_USER_PORT } ] - openImFriendPort: [ ${ OPENIM_FRIEND_PORT } ] - openImMessagePort: [ ${ OPENIM_MESSAGE_PORT } ] - openImGroupPort: [ ${ OPENIM_GROUP_PORT } ] - openImAuthPort: [ ${ OPENIM_AUTH_PORT } ] - openImPushPort: [ ${ OPENIM_PUSH_PORT } ] - openImConversationPort: [ ${ OPENIM_CONVERSATION_PORT } ] - openImThirdPort: [ ${ OPENIM_THIRD_PORT } ] + openImUserPort: [ ${OPENIM_USER_PORT} ] + openImFriendPort: [ ${OPENIM_FRIEND_PORT} ] + openImMessagePort: [ ${OPENIM_MESSAGE_PORT} ] + openImGroupPort: [ ${OPENIM_GROUP_PORT} ] + openImAuthPort: [ ${OPENIM_AUTH_PORT} ] + openImPushPort: [ ${OPENIM_PUSH_PORT} ] + openImConversationPort: [ ${OPENIM_CONVERSATION_PORT} ] + openImThirdPort: [ ${OPENIM_THIRD_PORT} ] ###################### RPC Register Name Configuration ###################### # RPC service names for registration, it's not recommended to modify these @@ -209,9 +209,9 @@ log: # Maximum length of websocket request package # Websocket connection handshake timeout longConnSvr: - openImWsPort: [ ${ OPENIM_WS_PORT } ] + openImWsPort: [ ${OPENIM_WS_PORT} ] websocketMaxConnNum: ${WEBSOCKET_MAX_CONN_NUM} - openImMessageGatewayPort: [ ${ OPENIM_MESSAGE_GATEWAY_PORT } ] + openImMessageGatewayPort: [ ${OPENIM_MESSAGE_GATEWAY_PORT} ] websocketMaxMsgLen: ${WEBSOCKET_MAX_MSG_LEN} websocketTimeout: ${WEBSOCKET_TIMEOUT} @@ -515,15 +515,15 @@ callback: prometheus: enable: ${PROMETHEUS_ENABLE} grafanaUrl: ${GRAFANA_URL} - apiPrometheusPort: [ ${ API_PROM_PORT } ] - userPrometheusPort: [ ${ USER_PROM_PORT } ] - friendPrometheusPort: [ ${ FRIEND_PROM_PORT } ] - messagePrometheusPort: [ ${ MESSAGE_PROM_PORT } ] - messageGatewayPrometheusPort: [ ${ MSG_GATEWAY_PROM_PORT } ] - groupPrometheusPort: [ ${ GROUP_PROM_PORT } ] - authPrometheusPort: [ ${ AUTH_PROM_PORT } ] - pushPrometheusPort: [ ${ PUSH_PROM_PORT } ] - conversationPrometheusPort: [ ${ CONVERSATION_PROM_PORT } ] - rtcPrometheusPort: [ ${ RTC_PROM_PORT } ] - thirdPrometheusPort: [ ${ THIRD_PROM_PORT } ] - messageTransferPrometheusPort: [ ${ MSG_TRANSFER_PROM_PORT } ] # List of ports \ No newline at end of file + apiPrometheusPort: [${API_PROM_PORT}] + userPrometheusPort: [ ${USER_PROM_PORT} ] + friendPrometheusPort: [ ${FRIEND_PROM_PORT} ] + messagePrometheusPort: [ ${MESSAGE_PROM_PORT} ] + messageGatewayPrometheusPort: [ ${MSG_GATEWAY_PROM_PORT} ] + groupPrometheusPort: [ ${GROUP_PROM_PORT} ] + authPrometheusPort: [ ${AUTH_PROM_PORT} ] + pushPrometheusPort: [ ${PUSH_PROM_PORT} ] + conversationPrometheusPort: [ ${CONVERSATION_PROM_PORT} ] + rtcPrometheusPort: [ ${RTC_PROM_PORT} ] + thirdPrometheusPort: [ ${THIRD_PROM_PORT} ] + messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports From b0ec48d35dd1e60f1720f62556eb2ea42327b6af Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 11:41:57 +0800 Subject: [PATCH 11/66] localcache --- pkg/common/db/cache/meta_cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 863f6d243..29901a12f 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -75,7 +75,7 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error { m.keys = utils.Distinct(m.keys) } if len(m.keys) > 0 { - log.ZDebug(ctx, "delete cache", "keys", m.keys) + log.ZDebug(ctx, "delete cache", "topic", m.topic, "keys", m.keys) for _, key := range m.keys { for i := 0; i < m.maxRetryTimes; i++ { if err := m.rcClient.TagAsDeleted(key); err != nil { From f0e5c587ad91b4277163cf8f63c4f40ecb79389d Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 11:46:27 +0800 Subject: [PATCH 12/66] localcache --- pkg/common/db/cache/black.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index e98f7de7b..e844cec0f 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -16,6 +16,7 @@ package cache import ( "context" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" @@ -55,6 +56,7 @@ func NewBlackCacheRedis( ) BlackCache { rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) + log.ZDebug(context.Background(), "local cache init", config.Config.LocalCache) mc.SetTopic(config.Config.LocalCache.Friend.Topic) mc.SetRawRedisClient(rdb) return &BlackCacheRedis{ From 03e15766abed5eab56673548433e117a1afdd231 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 11:57:46 +0800 Subject: [PATCH 13/66] localcache --- pkg/common/config/parse.go | 7 +++++++ pkg/common/config/parse_test.go | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index 1410a5a4a..6513079ab 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -110,3 +110,10 @@ func InitConfig(configFolderPath string) error { return initConfig(&Config.Notification, NotificationFileName, configFolderPath) } + +// todo test +func init() { + Config.LocalCache.Friend.Topic = "friend" + Config.LocalCache.Friend.SlotNum = 500 + Config.LocalCache.Friend.SlotSize = 20000 +} diff --git a/pkg/common/config/parse_test.go b/pkg/common/config/parse_test.go index 38171ec08..30cb270fe 100644 --- a/pkg/common/config/parse_test.go +++ b/pkg/common/config/parse_test.go @@ -16,6 +16,8 @@ package config import ( _ "embed" + "fmt" + "gopkg.in/yaml.v3" "reflect" "testing" @@ -115,3 +117,13 @@ func TestInitConfig(t *testing.T) { }) } } + +func TestName(t *testing.T) { + Config.LocalCache.Friend.Topic = "friend" + Config.LocalCache.Friend.SlotNum = 500 + Config.LocalCache.Friend.SlotSize = 20000 + + data, _ := yaml.Marshal(&Config) + + fmt.Println(string(data)) +} From c16cd0bf7cc3d9e5a18b7ad7cb89a8032e33d9e3 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 12:01:55 +0800 Subject: [PATCH 14/66] localcache --- internal/rpc/friend/friend.go | 6 ++++++ internal/rpc/msg/server.go | 7 +++++++ pkg/common/config/parse.go | 7 ------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 84702f548..02e123bac 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -16,6 +16,7 @@ package friend import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/tools/tx" @@ -54,6 +55,11 @@ type friendServer struct { } func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { + + config.Config.LocalCache.Friend.Topic = "friend" + config.Config.LocalCache.Friend.SlotNum = 500 + config.Config.LocalCache.Friend.SlotSize = 20000 + // Initialize MongoDB mongo, err := unrelation.NewMongo() if err != nil { diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 0600031cd..1c919372f 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -16,6 +16,7 @@ package msg import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpccache" "google.golang.org/grpc" @@ -65,6 +66,11 @@ func (m *msgServer) execInterceptorHandler(ctx context.Context, req *msg.SendMsg } func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + + config.Config.LocalCache.Friend.Topic = "friend" + config.Config.LocalCache.Friend.SlotNum = 500 + config.Config.LocalCache.Friend.SlotSize = 20000 + rdb, err := cache.NewRedis() if err != nil { return err @@ -82,6 +88,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e userRpcClient := rpcclient.NewUserRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) + s := &msgServer{ Conversation: &conversationClient, User: &userRpcClient, diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index 6513079ab..1410a5a4a 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -110,10 +110,3 @@ func InitConfig(configFolderPath string) error { return initConfig(&Config.Notification, NotificationFileName, configFolderPath) } - -// todo test -func init() { - Config.LocalCache.Friend.Topic = "friend" - Config.LocalCache.Friend.SlotNum = 500 - Config.LocalCache.Friend.SlotSize = 20000 -} From 1ea5fe04f17fd44cd59c8888af44621fac64794d Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 12:08:15 +0800 Subject: [PATCH 15/66] localcache --- pkg/common/db/cache/black.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index e844cec0f..2bb0e7f25 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -56,7 +56,8 @@ func NewBlackCacheRedis( ) BlackCache { rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) - log.ZDebug(context.Background(), "local cache init", config.Config.LocalCache) + f := config.Config.LocalCache.Friend + log.ZDebug(context.Background(), "local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize) mc.SetTopic(config.Config.LocalCache.Friend.Topic) mc.SetRawRedisClient(rdb) return &BlackCacheRedis{ From 28292be37d1d6a77db26f6a388cf17eedd296bce Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 12:09:25 +0800 Subject: [PATCH 16/66] localcache --- pkg/common/db/cache/black.go | 4 ++-- pkg/common/db/cache/friend.go | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index 2bb0e7f25..1eb4a6938 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -57,8 +57,8 @@ func NewBlackCacheRedis( rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) f := config.Config.LocalCache.Friend - log.ZDebug(context.Background(), "local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize) - mc.SetTopic(config.Config.LocalCache.Friend.Topic) + log.ZDebug(context.Background(), "black local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize) + mc.SetTopic(f.Topic) mc.SetRawRedisClient(rdb) return &BlackCacheRedis{ expireTime: blackExpireTime, diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index e973885de..d39f22271 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -16,6 +16,7 @@ package cache import ( "context" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" @@ -63,7 +64,9 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendMo options rockscache.Options) FriendCache { rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) - mc.SetTopic(config.Config.LocalCache.Friend.Topic) + f := config.Config.LocalCache.Friend + log.ZDebug(context.Background(), "friend local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize) + mc.SetTopic(f.Topic) mc.SetRawRedisClient(rdb) return &FriendCacheRedis{ metaCache: mc, From 7a67beb7a1cd38eb4da313d459901ffc9291d155 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 14:38:04 +0800 Subject: [PATCH 17/66] localcache --- pkg/common/db/mgo/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index b82966371..d2beafd49 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -83,7 +83,7 @@ func (u *UserMgo) PageFindUser(ctx context.Context, level int64, pagination pagi } func (u *UserMgo) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) { - return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"user_id": 1})) + return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1})) } func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err error) { From da2c0d11f5ed69fdb8cce981eed88465562bddc6 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 15:06:12 +0800 Subject: [PATCH 18/66] localcache --- pkg/common/db/cache/black.go | 2 +- pkg/common/db/cache/friend.go | 2 +- pkg/common/db/cache/meta_cache.go | 17 +++++++++++++++++ pkg/common/db/mgo/user.go | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index 1eb4a6938..ca116fdc7 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -73,7 +73,7 @@ func (b *BlackCacheRedis) NewCache() BlackCache { expireTime: b.expireTime, rcClient: b.rcClient, blackDB: b.blackDB, - metaCache: NewMetaCacheRedis(b.rcClient, b.metaCache.GetPreDelKeys()...), + metaCache: b.metaCache.Copy(), } } diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index d39f22271..6d287d2f5 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -80,7 +80,7 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendMo func (f *FriendCacheRedis) NewCache() FriendCache { return &FriendCacheRedis{ rcClient: f.rcClient, - metaCache: NewMetaCacheRedis(f.rcClient, f.metaCache.GetPreDelKeys()...), + metaCache: f.metaCache.Copy(), friendDB: f.friendDB, expireTime: f.expireTime, } diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 29901a12f..dbe2344f0 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -47,6 +47,7 @@ type metaCache interface { GetPreDelKeys() []string SetTopic(topic string) SetRawRedisClient(cli redis.UniversalClient) + Copy() metaCache } func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache { @@ -62,6 +63,22 @@ type metaCacheRedis struct { redisClient redis.UniversalClient } +func (m *metaCacheRedis) Copy() metaCache { + var keys []string + if len(m.keys) > 0 { + keys = make([]string, 0, len(m.keys)*2) + keys = append(keys, m.keys...) + } + return &metaCacheRedis{ + topic: m.topic, + rcClient: m.rcClient, + keys: keys, + maxRetryTimes: m.maxRetryTimes, + retryInterval: m.retryInterval, + redisClient: redisClient, + } +} + func (m *metaCacheRedis) SetTopic(topic string) { m.topic = topic } diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index d2beafd49..b07f82928 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -91,7 +91,7 @@ func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err err } func (u *UserMgo) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) { - return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"global_recv_msg_opt": 1})) + return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"_id": 0, "global_recv_msg_opt": 1})) } func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { From ee9c9eb97c5c44097cfadf1e9dba40da2296aef5 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 15:16:30 +0800 Subject: [PATCH 19/66] localcache --- pkg/rpccache/friend.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 737f674ab..f61405ec7 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -2,6 +2,7 @@ package rpccache import ( "context" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" @@ -23,19 +24,25 @@ type FriendLocalCache struct { } func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) { + log.ZDebug(ctx, "FriendLocalCache GetFriendIDs req", "ownerUserID", ownerUserID) return localcache.AnyValue[[]string](f.local.Get(ctx, cachekey.GetFriendIDsKey(ownerUserID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "FriendLocalCache GetFriendIDs call rpc", "ownerUserID", ownerUserID) return f.client.GetFriendIDs(ctx, ownerUserID) })) } func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (bool, error) { + log.ZDebug(ctx, "FriendLocalCache IsFriend req", "possibleFriendUserID", possibleFriendUserID, "userID", userID) return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "FriendLocalCache IsFriend rpc", "possibleFriendUserID", possibleFriendUserID, "userID", userID) return f.client.IsFriend(ctx, possibleFriendUserID, userID) }, option.NewOption().WithLink(cachekey.GetFriendIDsKey(possibleFriendUserID), cachekey.GetFriendIDsKey(userID)))) } func (f *FriendLocalCache) IsBlocked(ctx context.Context, possibleBlackUserID, userID string) (bool, error) { + log.ZDebug(ctx, "FriendLocalCache IsBlocked req", "possibleBlackUserID", possibleBlackUserID, "userID", userID) return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "FriendLocalCache IsBlocked rpc", "possibleBlackUserID", possibleBlackUserID, "userID", userID) return f.client.IsBlocked(ctx, possibleBlackUserID, userID) }, option.NewOption().WithLink(cachekey.GetBlackIDsKey(possibleBlackUserID), cachekey.GetBlackIDsKey(userID)))) } From a84ad774f72ed79140ba951ff3cbb3bc188f84c8 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 15:27:24 +0800 Subject: [PATCH 20/66] local cache --- internal/rpc/msg/verify.go | 2 +- pkg/rpccache/friend.go | 33 +++++++++++++++++++++++++++------ pkg/rpcclient/friend.go | 2 +- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 2837cb944..e316ef88d 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -58,7 +58,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe data.MsgData.ContentType >= constant.NotificationBegin { return nil } - black, err := m.friend.IsBlocked(ctx, data.MsgData.SendID, data.MsgData.RecvID) + black, err := m.friend.IsBlack(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { return err } diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index f61405ec7..f519f6f14 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -23,26 +23,47 @@ type FriendLocalCache struct { client rpcclient.FriendRpcClient } -func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) { +func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) (val []string, err error) { log.ZDebug(ctx, "FriendLocalCache GetFriendIDs req", "ownerUserID", ownerUserID) + defer func() { + if err == nil { + log.ZDebug(ctx, "FriendLocalCache GetFriendIDs return", "value", val) + } else { + log.ZError(ctx, "FriendLocalCache GetFriendIDs return", err) + } + }() return localcache.AnyValue[[]string](f.local.Get(ctx, cachekey.GetFriendIDsKey(ownerUserID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "FriendLocalCache GetFriendIDs call rpc", "ownerUserID", ownerUserID) return f.client.GetFriendIDs(ctx, ownerUserID) })) } -func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (bool, error) { +func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (val bool, err error) { log.ZDebug(ctx, "FriendLocalCache IsFriend req", "possibleFriendUserID", possibleFriendUserID, "userID", userID) + defer func() { + if err == nil { + log.ZDebug(ctx, "FriendLocalCache IsFriend return", "value", val) + } else { + log.ZError(ctx, "FriendLocalCache IsFriend return", err) + } + }() return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "FriendLocalCache IsFriend rpc", "possibleFriendUserID", possibleFriendUserID, "userID", userID) return f.client.IsFriend(ctx, possibleFriendUserID, userID) }, option.NewOption().WithLink(cachekey.GetFriendIDsKey(possibleFriendUserID), cachekey.GetFriendIDsKey(userID)))) } -func (f *FriendLocalCache) IsBlocked(ctx context.Context, possibleBlackUserID, userID string) (bool, error) { - log.ZDebug(ctx, "FriendLocalCache IsBlocked req", "possibleBlackUserID", possibleBlackUserID, "userID", userID) +func (f *FriendLocalCache) IsBlack(ctx context.Context, possibleBlackUserID, userID string) (val bool, err error) { + log.ZDebug(ctx, "FriendLocalCache IsBlack req", "possibleBlackUserID", possibleBlackUserID, "userID", userID) + defer func() { + if err == nil { + log.ZDebug(ctx, "FriendLocalCache IsBlack return", "value", val) + } else { + log.ZError(ctx, "FriendLocalCache IsBlack return", err) + } + }() return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { - log.ZDebug(ctx, "FriendLocalCache IsBlocked rpc", "possibleBlackUserID", possibleBlackUserID, "userID", userID) - return f.client.IsBlocked(ctx, possibleBlackUserID, userID) + log.ZDebug(ctx, "FriendLocalCache IsBlack rpc", "possibleBlackUserID", possibleBlackUserID, "userID", userID) + return f.client.IsBlack(ctx, possibleBlackUserID, userID) }, option.NewOption().WithLink(cachekey.GetBlackIDsKey(possibleBlackUserID), cachekey.GetBlackIDsKey(userID)))) } diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index b84db40d4..7158ed569 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -80,7 +80,7 @@ func (f *FriendRpcClient) GetFriendIDs(ctx context.Context, ownerUserID string) return resp.FriendIDs, nil } -func (b *FriendRpcClient) IsBlocked(ctx context.Context, possibleBlackUserID, userID string) (bool, error) { +func (b *FriendRpcClient) IsBlack(ctx context.Context, possibleBlackUserID, userID string) (bool, error) { r, err := b.Client.IsBlack(ctx, &friend.IsBlackReq{UserID1: possibleBlackUserID, UserID2: userID}) if err != nil { return false, err From 2d21ab3aebdedf0c2c313403405bb8590bfcb365 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 16:03:49 +0800 Subject: [PATCH 21/66] local cache --- pkg/common/localcache/cache.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index 77e77ce5f..dedcf6aa9 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -32,15 +32,23 @@ type cache[V any] struct { } func (c *cache[V]) onEvict(key string, value V) { - for k := range c.link.Del(key) { + lks := c.link.Del(key) + for k := range lks { c.local.Del(k) } } func (c *cache[V]) del(key ...string) { for _, k := range key { + lks := c.link.Del(k) c.local.Del(k) + //fmt.Println(">>>", lks) + for k := range lks { + c.local.Del(k) + //fmt.Println("+++", k) + } } + //fmt.Println() } func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) { From 8ff8861603717ff53a5063fb359337f8d508c59a Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 16:13:55 +0800 Subject: [PATCH 22/66] local cache --- pkg/common/localcache/cache.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index dedcf6aa9..228538fa6 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -34,7 +34,9 @@ type cache[V any] struct { func (c *cache[V]) onEvict(key string, value V) { lks := c.link.Del(key) for k := range lks { - c.local.Del(k) + if key != k { // prevent deadlock + c.local.Del(k) + } } } @@ -42,13 +44,10 @@ func (c *cache[V]) del(key ...string) { for _, k := range key { lks := c.link.Del(k) c.local.Del(k) - //fmt.Println(">>>", lks) for k := range lks { c.local.Del(k) - //fmt.Println("+++", k) } } - //fmt.Println() } func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) { From 006e4a1e93a862eade5c5cdab3a7dd795c3d00d6 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 10 Jan 2024 16:24:46 +0800 Subject: [PATCH 23/66] local cache --- deployments/templates/openim.yaml | 6 ++++++ internal/rpc/friend/friend.go | 7 ------- internal/rpc/msg/server.go | 6 ------ pkg/common/cachekey/black.go | 2 +- pkg/rpccache/friend.go | 5 +++-- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 4c84373a5..69f578f81 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -527,3 +527,9 @@ prometheus: rtcPrometheusPort: [ ${RTC_PROM_PORT} ] thirdPrometheusPort: [ ${THIRD_PROM_PORT} ] messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports + +localCache: + friend: + topic: friend + slotNum: 500 + slotSize: 20000 \ No newline at end of file diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 02e123bac..9c76c6009 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -16,8 +16,6 @@ package friend import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/protocol/sdkws" @@ -55,11 +53,6 @@ type friendServer struct { } func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { - - config.Config.LocalCache.Friend.Topic = "friend" - config.Config.LocalCache.Friend.SlotNum = 500 - config.Config.LocalCache.Friend.SlotSize = 20000 - // Initialize MongoDB mongo, err := unrelation.NewMongo() if err != nil { diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 1c919372f..f5b1dd823 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -16,7 +16,6 @@ package msg import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpccache" "google.golang.org/grpc" @@ -66,11 +65,6 @@ func (m *msgServer) execInterceptorHandler(ctx context.Context, req *msg.SendMsg } func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - - config.Config.LocalCache.Friend.Topic = "friend" - config.Config.LocalCache.Friend.SlotNum = 500 - config.Config.LocalCache.Friend.SlotSize = 20000 - rdb, err := cache.NewRedis() if err != nil { return err diff --git a/pkg/common/cachekey/black.go b/pkg/common/cachekey/black.go index 841e10c6a..136972cb7 100644 --- a/pkg/common/cachekey/black.go +++ b/pkg/common/cachekey/black.go @@ -11,5 +11,5 @@ func GetBlackIDsKey(ownerUserID string) string { } func GetIsBlackIDsKey(possibleBlackUserID, userID string) string { - return isBlackKey + possibleBlackUserID + "-" + userID + return isBlackKey + userID + "-" + possibleBlackUserID } diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index f519f6f14..14adf1e0a 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -50,9 +50,10 @@ func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, u return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "FriendLocalCache IsFriend rpc", "possibleFriendUserID", possibleFriendUserID, "userID", userID) return f.client.IsFriend(ctx, possibleFriendUserID, userID) - }, option.NewOption().WithLink(cachekey.GetFriendIDsKey(possibleFriendUserID), cachekey.GetFriendIDsKey(userID)))) + }, option.NewOption().WithLink(cachekey.GetFriendIDsKey(possibleFriendUserID)))) } +// IsBlack possibleBlackUserID selfUserID func (f *FriendLocalCache) IsBlack(ctx context.Context, possibleBlackUserID, userID string) (val bool, err error) { log.ZDebug(ctx, "FriendLocalCache IsBlack req", "possibleBlackUserID", possibleBlackUserID, "userID", userID) defer func() { @@ -65,5 +66,5 @@ func (f *FriendLocalCache) IsBlack(ctx context.Context, possibleBlackUserID, use return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "FriendLocalCache IsBlack rpc", "possibleBlackUserID", possibleBlackUserID, "userID", userID) return f.client.IsBlack(ctx, possibleBlackUserID, userID) - }, option.NewOption().WithLink(cachekey.GetBlackIDsKey(possibleBlackUserID), cachekey.GetBlackIDsKey(userID)))) + }, option.NewOption().WithLink(cachekey.GetBlackIDsKey(userID)))) } From 4c3c4555a35ec8e31ffbf3e96a5dba3bceec09ee Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Thu, 11 Jan 2024 11:18:52 +0800 Subject: [PATCH 24/66] fix: GroupApplicationAcceptedNotification --- pkg/rpcclient/notification/group.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index cbae7c49b..f2413143d 100755 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -413,7 +413,7 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { + for _, userID := range append(userIDs, req.FromUserID) { err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) @@ -441,7 +441,7 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { + for _, userID := range append(userIDs, req.FromUserID) { err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) From a00d77a7053c3f9999952337490f0493b4865c49 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Thu, 11 Jan 2024 16:31:24 +0800 Subject: [PATCH 25/66] fix: GroupApplicationAcceptedNotification --- pkg/rpcclient/notification/group.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index f2413143d..8c3719b2c 100755 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -409,11 +409,16 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte if err != nil { return err } - tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg, ReceiverAs: 1} + tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } for _, userID := range append(userIDs, req.FromUserID) { + if userID == req.FromUserID { + tips.ReceiverAs = 0 + } else { + tips.ReceiverAs = 1 + } err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) @@ -442,6 +447,11 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte return err } for _, userID := range append(userIDs, req.FromUserID) { + if userID == req.FromUserID { + tips.ReceiverAs = 0 + } else { + tips.ReceiverAs = 1 + } err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) From 48ff03f854e48fd22819c818586c6385d899a3c1 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Fri, 12 Jan 2024 11:01:28 +0800 Subject: [PATCH 26/66] fix: NotificationUserInfoUpdate --- internal/rpc/group/group.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index f9b73ad2b..abc271651 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -109,14 +109,11 @@ type groupServer struct { } func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) { - defer log.ZDebug(ctx, "return") + defer log.ZDebug(ctx, "NotificationUserInfoUpdate return") members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID) if err != nil { return nil, err } - if err := s.PopulateGroupMember(ctx, members...); err != nil { - return nil, err - } groupIDs := make([]string, 0, len(members)) for _, member := range members { if member.Nickname != "" && member.FaceURL != "" { From 1f1ab6537519700c99cd8a06eb4178f7b3bd4bca Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:27:54 +0800 Subject: [PATCH 27/66] feat: cache add single-flight and timing-wheel. --- pkg/common/localcache/cache.go | 42 ++++++-- pkg/common/localcache/{local => }/callback.go | 2 +- pkg/common/localcache/local/cache.go | 50 ---------- pkg/common/localcache/local/lru_test.go | 95 ------------------- pkg/common/localcache/local/target.go | 10 -- pkg/common/localcache/{local => }/lru.go | 9 +- pkg/common/localcache/lru_test.go | 55 +++++++++++ pkg/common/localcache/option.go | 21 +--- pkg/common/localcache/singleflight.go | 43 +++++++++ pkg/common/localcache/target.go | 59 ++++++++++++ pkg/common/localcache/timingwheel.go | 71 ++++++++++++++ pkg/common/redispubsub/redispubliser.go | 16 ++++ pkg/common/redispubsub/redissubscriber.go | 27 ++++++ 13 files changed, 316 insertions(+), 184 deletions(-) rename pkg/common/localcache/{local => }/callback.go (86%) delete mode 100644 pkg/common/localcache/local/cache.go delete mode 100644 pkg/common/localcache/local/lru_test.go delete mode 100644 pkg/common/localcache/local/target.go rename pkg/common/localcache/{local => }/lru.go (91%) create mode 100644 pkg/common/localcache/lru_test.go create mode 100644 pkg/common/localcache/singleflight.go create mode 100644 pkg/common/localcache/target.go create mode 100644 pkg/common/localcache/timingwheel.go create mode 100644 pkg/common/redispubsub/redispubliser.go create mode 100644 pkg/common/redispubsub/redissubscriber.go diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index 228538fa6..f81a1a214 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -3,10 +3,14 @@ package localcache import ( "context" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/link" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" opt "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" + "hash/fnv" + "time" + "unsafe" ) +const TimingWheelSize = 500 + type Cache[V any] interface { Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) Del(ctx context.Context, key ...string) @@ -17,8 +21,15 @@ func New[V any](opts ...Option) Cache[V] { for _, o := range opts { o(opt) } - c := &cache[V]{opt: opt, link: link.New(opt.localSlotNum)} - c.local = local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + c := &cache[V]{ + opt: opt, + link: link.New(opt.localSlotNum), + n: uint64(opt.localSlotNum), + } + c.timingWheel = NewTimeWheel[string, V](TimingWheelSize, time.Second, c.exec) + for i := 0; i < opt.localSlotNum; i++ { + c.slots[i] = NewLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + } go func() { c.opt.delCh(c.del) }() @@ -26,16 +37,24 @@ func New[V any](opts ...Option) Cache[V] { } type cache[V any] struct { - opt *option - link link.Link - local local.Cache[V] + n uint64 + slots []*LRU[string, V] + opt *option + link link.Link + timingWheel *TimeWheel[string, V] +} + +func (c *cache[V]) index(key string) uint64 { + h := fnv.New64a() + _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&key))) + return h.Sum64() % c.n } func (c *cache[V]) onEvict(key string, value V) { lks := c.link.Del(key) for k := range lks { if key != k { // prevent deadlock - c.local.Del(k) + c.slots[c.index(k)].Del(k) } } } @@ -43,9 +62,9 @@ func (c *cache[V]) onEvict(key string, value V) { func (c *cache[V]) del(key ...string) { for _, k := range key { lks := c.link.Del(k) - c.local.Del(k) + c.slots[c.index(k)].Del(k) for k := range lks { - c.local.Del(k) + c.slots[c.index(k)].Del(k) } } } @@ -59,7 +78,7 @@ func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.C if len(opts) > 0 && len(opts[0].Link) > 0 { c.link.Link(key, opts[0].Link...) } - return c.local.Get(key, func() (V, error) { + return c.slots[c.index(key)].Get(key, func() (V, error) { return fetch(ctx) }) } else { @@ -78,3 +97,6 @@ func (c *cache[V]) Del(ctx context.Context, key ...string) { c.del(key...) } } +func (c *cache[V]) exec(key string, value V) { + +} diff --git a/pkg/common/localcache/local/callback.go b/pkg/common/localcache/callback.go similarity index 86% rename from pkg/common/localcache/local/callback.go rename to pkg/common/localcache/callback.go index 32aef112b..4bd37a2c2 100644 --- a/pkg/common/localcache/local/callback.go +++ b/pkg/common/localcache/callback.go @@ -1,4 +1,4 @@ -package local +package localcache import "github.com/hashicorp/golang-lru/v2/simplelru" diff --git a/pkg/common/localcache/local/cache.go b/pkg/common/localcache/local/cache.go deleted file mode 100644 index 06569965e..000000000 --- a/pkg/common/localcache/local/cache.go +++ /dev/null @@ -1,50 +0,0 @@ -package local - -import ( - "hash/fnv" - "time" - "unsafe" -) - -type Cache[V any] interface { - Get(key string, fetch func() (V, error)) (V, error) - Del(key string) bool -} - -func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[string, V]) Cache[V] { - c := &slot[V]{ - n: uint64(slotNum), - slots: make([]*LRU[string, V], slotNum), - target: target, - } - for i := 0; i < slotNum; i++ { - c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict) - } - return c -} - -type slot[V any] struct { - n uint64 - slots []*LRU[string, V] - target Target -} - -func (c *slot[V]) index(s string) uint64 { - h := fnv.New64a() - _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&s))) - return h.Sum64() % c.n -} - -func (c *slot[V]) Get(key string, fetch func() (V, error)) (V, error) { - return c.slots[c.index(key)].Get(key, fetch) -} - -func (c *slot[V]) Del(key string) bool { - if c.slots[c.index(key)].Del(key) { - c.target.IncrDelHit() - return true - } else { - c.target.IncrDelNotFound() - return false - } -} diff --git a/pkg/common/localcache/local/lru_test.go b/pkg/common/localcache/local/lru_test.go deleted file mode 100644 index a6e7553ee..000000000 --- a/pkg/common/localcache/local/lru_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package local - -import ( - "fmt" - "sync" - "sync/atomic" - "testing" - "time" -) - -type cacheTarget struct { - getHit int64 - getSuccess int64 - getFailed int64 - delHit int64 - delNotFound int64 -} - -func (r *cacheTarget) IncrGetHit() { - atomic.AddInt64(&r.getHit, 1) -} - -func (r *cacheTarget) IncrGetSuccess() { - atomic.AddInt64(&r.getSuccess, 1) -} - -func (r *cacheTarget) IncrGetFailed() { - atomic.AddInt64(&r.getFailed, 1) -} - -func (r *cacheTarget) IncrDelHit() { - atomic.AddInt64(&r.delHit, 1) -} - -func (r *cacheTarget) IncrDelNotFound() { - atomic.AddInt64(&r.delNotFound, 1) -} - -func (r *cacheTarget) String() string { - return fmt.Sprintf("getHit: %d, getSuccess: %d, getFailed: %d, delHit: %d, delNotFound: %d", r.getHit, r.getSuccess, r.getFailed, r.delHit, r.delNotFound) -} - -func TestName(t *testing.T) { - target := &cacheTarget{} - l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) - //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) - - fn := func(key string, n int, fetch func() (string, error)) { - for i := 0; i < n; i++ { - //v, err := l.Get(key, fetch) - //if err == nil { - // t.Log("key", key, "value", v) - //} else { - // t.Error("key", key, err) - //} - l.Get(key, fetch) - //time.Sleep(time.Second / 100) - } - } - - tmp := make(map[string]struct{}) - - var wg sync.WaitGroup - for i := 0; i < 10000; i++ { - wg.Add(1) - key := fmt.Sprintf("key_%d", i%200) - tmp[key] = struct{}{} - go func() { - defer wg.Done() - //t.Log(key) - fn(key, 10000, func() (string, error) { - //time.Sleep(time.Second * 3) - //t.Log(time.Now(), "key", key, "fetch") - //if rand.Uint32()%5 == 0 { - // return "value_" + key, nil - //} - //return "", errors.New("rand error") - return "value_" + key, nil - }) - }() - - //wg.Add(1) - //go func() { - // defer wg.Done() - // for i := 0; i < 10; i++ { - // l.Del(key) - // time.Sleep(time.Second / 3) - // } - //}() - } - wg.Wait() - t.Log(len(tmp)) - t.Log(target.String()) - -} diff --git a/pkg/common/localcache/local/target.go b/pkg/common/localcache/local/target.go deleted file mode 100644 index 6cb134fb0..000000000 --- a/pkg/common/localcache/local/target.go +++ /dev/null @@ -1,10 +0,0 @@ -package local - -type Target interface { - IncrGetHit() - IncrGetSuccess() - IncrGetFailed() - - IncrDelHit() - IncrDelNotFound() -} diff --git a/pkg/common/localcache/local/lru.go b/pkg/common/localcache/lru.go similarity index 91% rename from pkg/common/localcache/local/lru.go rename to pkg/common/localcache/lru.go index 45dc3b651..4fd1704d2 100644 --- a/pkg/common/localcache/local/lru.go +++ b/pkg/common/localcache/lru.go @@ -1,4 +1,4 @@ -package local +package localcache import ( "github.com/hashicorp/golang-lru/v2/simplelru" @@ -30,6 +30,7 @@ func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, successTTL: successTTL, failedTTL: failedTTL, target: target, + s: NewSingleFlight[K, V](), } } @@ -39,6 +40,7 @@ type LRU[K comparable, V any] struct { successTTL time.Duration failedTTL time.Duration target Target + s *SingleFlight[K, V] } func (x *LRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { @@ -78,5 +80,10 @@ func (x *LRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() + if ok { + x.target.IncrDelHit() + } else { + x.target.IncrDelNotFound() + } return ok } diff --git a/pkg/common/localcache/lru_test.go b/pkg/common/localcache/lru_test.go new file mode 100644 index 000000000..fff925eaa --- /dev/null +++ b/pkg/common/localcache/lru_test.go @@ -0,0 +1,55 @@ +package localcache + +//func TestName(t *testing.T) { +// target := &cacheTarget{} +// l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) +// //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) +// +// fn := func(key string, n int, fetch func() (string, error)) { +// for i := 0; i < n; i++ { +// //v, err := l.Get(key, fetch) +// //if err == nil { +// // t.Log("key", key, "value", v) +// //} else { +// // t.Error("key", key, err) +// //} +// l.Get(key, fetch) +// //time.Sleep(time.Second / 100) +// } +// } +// +// tmp := make(map[string]struct{}) +// +// var wg sync.WaitGroup +// for i := 0; i < 10000; i++ { +// wg.Add(1) +// key := fmt.Sprintf("key_%d", i%200) +// tmp[key] = struct{}{} +// go func() { +// defer wg.Done() +// //t.Log(key) +// fn(key, 10000, func() (string, error) { +// //time.Sleep(time.Second * 3) +// //t.Log(time.Now(), "key", key, "fetch") +// //if rand.Uint32()%5 == 0 { +// // return "value_" + key, nil +// //} +// //return "", errors.New("rand error") +// return "value_" + key, nil +// }) +// }() +// +// //wg.Add(1) +// //go func() { +// // defer wg.Done() +// // for i := 0; i < 10; i++ { +// // l.Del(key) +// // time.Sleep(time.Second / 3) +// // } +// //}() +// } +// wg.Wait() +// t.Log(len(tmp)) +// t.Log(target.String()) +// +//} diff --git a/pkg/common/localcache/option.go b/pkg/common/localcache/option.go index 01c9ce57d..6f0b973d4 100644 --- a/pkg/common/localcache/option.go +++ b/pkg/common/localcache/option.go @@ -2,15 +2,14 @@ package localcache import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" "time" ) func defaultOption() *option { return &option{ enable: true, - localSlotNum: 500, - localSlotSize: 20000, + localSlotNum: 500, //LRU的slot数量 + localSlotSize: 20000, //每个LRU的大小 localSuccessTTL: time.Minute, localFailedTTL: time.Second * 5, delFn: make([]func(ctx context.Context, key ...string), 0, 2), @@ -26,7 +25,7 @@ type option struct { localFailedTTL time.Duration delFn []func(ctx context.Context, key ...string) delCh func(fn func(key ...string)) - target local.Target + target Target } type Option func(o *option) @@ -73,7 +72,7 @@ func WithLocalFailedTTL(localFailedTTL time.Duration) Option { } } -func WithTarget(target local.Target) Option { +func WithTarget(target Target) Option { if target == nil { panic("target should not be nil") } @@ -99,15 +98,3 @@ func WithDeleteLocal(fn func(fn func(key ...string))) Option { o.delCh = fn } } - -type emptyTarget struct{} - -func (e emptyTarget) IncrGetHit() {} - -func (e emptyTarget) IncrGetSuccess() {} - -func (e emptyTarget) IncrGetFailed() {} - -func (e emptyTarget) IncrDelHit() {} - -func (e emptyTarget) IncrDelNotFound() {} diff --git a/pkg/common/localcache/singleflight.go b/pkg/common/localcache/singleflight.go new file mode 100644 index 000000000..5dcd70669 --- /dev/null +++ b/pkg/common/localcache/singleflight.go @@ -0,0 +1,43 @@ +package localcache + +import "sync" + +type call[K comparable, V any] struct { + wg sync.WaitGroup + val V + err error +} + +type SingleFlight[K comparable, V any] struct { + mu sync.Mutex + m map[K]*call[K, V] +} + +func NewSingleFlight[K comparable, V any]() *SingleFlight[K, V] { + return &SingleFlight[K, V]{m: make(map[K]*call[K, V])} +} + +func (r *SingleFlight[K, V]) Do(key K, fn func() (V, error)) (V, error) { + r.mu.Lock() + if r.m == nil { + r.m = make(map[K]*call[K, V]) + } + if c, ok := r.m[key]; ok { + r.mu.Unlock() + c.wg.Wait() + return c.val, c.err + } + c := new(call[K, V]) + c.wg.Add(1) + r.m[key] = c + r.mu.Unlock() + + c.val, c.err = fn() + c.wg.Done() + + r.mu.Lock() + delete(r.m, key) + r.mu.Unlock() + + return c.val, c.err +} diff --git a/pkg/common/localcache/target.go b/pkg/common/localcache/target.go new file mode 100644 index 000000000..2edf51ddb --- /dev/null +++ b/pkg/common/localcache/target.go @@ -0,0 +1,59 @@ +package localcache + +import ( + "fmt" + "sync/atomic" +) + +type Target interface { + IncrGetHit() + IncrGetSuccess() + IncrGetFailed() + + IncrDelHit() + IncrDelNotFound() +} + +type cacheTarget struct { + getHit int64 + getSuccess int64 + getFailed int64 + delHit int64 + delNotFound int64 +} + +func (r *cacheTarget) IncrGetHit() { + atomic.AddInt64(&r.getHit, 1) +} + +func (r *cacheTarget) IncrGetSuccess() { + atomic.AddInt64(&r.getSuccess, 1) +} + +func (r *cacheTarget) IncrGetFailed() { + atomic.AddInt64(&r.getFailed, 1) +} + +func (r *cacheTarget) IncrDelHit() { + atomic.AddInt64(&r.delHit, 1) +} + +func (r *cacheTarget) IncrDelNotFound() { + atomic.AddInt64(&r.delNotFound, 1) +} + +func (r *cacheTarget) String() string { + return fmt.Sprintf("getHit: %d, getSuccess: %d, getFailed: %d, delHit: %d, delNotFound: %d", r.getHit, r.getSuccess, r.getFailed, r.delHit, r.delNotFound) +} + +type emptyTarget struct{} + +func (e emptyTarget) IncrGetHit() {} + +func (e emptyTarget) IncrGetSuccess() {} + +func (e emptyTarget) IncrGetFailed() {} + +func (e emptyTarget) IncrDelHit() {} + +func (e emptyTarget) IncrDelNotFound() {} diff --git a/pkg/common/localcache/timingwheel.go b/pkg/common/localcache/timingwheel.go new file mode 100644 index 000000000..ca1bac033 --- /dev/null +++ b/pkg/common/localcache/timingwheel.go @@ -0,0 +1,71 @@ +package localcache + +import ( + "sync" + "time" +) + +type Execute[K comparable, V any] func(K, V) + +type Task[K comparable, V any] struct { + key K + value V +} + +type TimeWheel[K comparable, V any] struct { + ticker *time.Ticker + slots [][]Task[K, V] + currentPos int + size int + slotMutex sync.Mutex + execute Execute[K, V] +} + +func NewTimeWheel[K comparable, V any](size int, tickDuration time.Duration, execute Execute[K, V]) *TimeWheel[K, V] { + return &TimeWheel[K, V]{ + ticker: time.NewTicker(tickDuration), + slots: make([][]Task[K, V], size), + currentPos: 0, + size: size, + execute: execute, + } +} + +func (tw *TimeWheel[K, V]) Start() { + for range tw.ticker.C { + tw.tick() + } +} + +func (tw *TimeWheel[K, V]) Stop() { + tw.ticker.Stop() +} + +func (tw *TimeWheel[K, V]) tick() { + tw.slotMutex.Lock() + defer tw.slotMutex.Unlock() + + tasks := tw.slots[tw.currentPos] + tw.slots[tw.currentPos] = nil + if len(tasks) > 0 { + go func(tasks []Task[K, V]) { + for _, task := range tasks { + tw.execute(task.key, task.value) + } + }(tasks) + } + + tw.currentPos = (tw.currentPos + 1) % tw.size +} + +func (tw *TimeWheel[K, V]) AddTask(delay int, task Task[K, V]) { + if delay < 0 || delay >= tw.size { + return + } + + tw.slotMutex.Lock() + defer tw.slotMutex.Unlock() + + pos := (tw.currentPos + delay) % tw.size + tw.slots[pos] = append(tw.slots[pos], task) +} diff --git a/pkg/common/redispubsub/redispubliser.go b/pkg/common/redispubsub/redispubliser.go new file mode 100644 index 000000000..822b25bf9 --- /dev/null +++ b/pkg/common/redispubsub/redispubliser.go @@ -0,0 +1,16 @@ +package redispubsub + +import "github.com/redis/go-redis/v9" + +type Publisher struct { + client redis.UniversalClient + channel string +} + +func NewPublisher(client redis.UniversalClient, channel string) *Publisher { + return &Publisher{client: client, channel: channel} +} + +func (p *Publisher) Publish(message string) error { + return p.client.Publish(ctx, p.channel, message).Err() +} diff --git a/pkg/common/redispubsub/redissubscriber.go b/pkg/common/redispubsub/redissubscriber.go new file mode 100644 index 000000000..69cfd8a69 --- /dev/null +++ b/pkg/common/redispubsub/redissubscriber.go @@ -0,0 +1,27 @@ +package redispubsub + +import ( + "context" + "github.com/redis/go-redis/v9" +) + +var ctx = context.Background() + +type Subscriber struct { + client redis.UniversalClient + channel string +} + +func NewSubscriber(client redis.UniversalClient, channel string) *Subscriber { + return &Subscriber{client: client, channel: channel} +} + +func (s *Subscriber) OnMessage(callback func(string)) error { + messageChannel := s.client.Subscribe(ctx, s.channel).Channel() + go func() { + for msg := range messageChannel { + callback(msg.Payload) + } + }() + return nil +} From 45064ae5cab5224a36858455b1f42a087c1a05bd Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Fri, 12 Jan 2024 15:41:05 +0800 Subject: [PATCH 28/66] feat: local cache --- deployments/templates/openim.yaml | 12 ++- internal/push/push_rpc_server.go | 6 +- internal/push/push_to_client.go | 8 +- internal/rpc/msg/server.go | 22 +++-- pkg/common/cachekey/black.go | 8 +- pkg/common/cachekey/conversation.go | 36 ++++---- pkg/common/cachekey/friend.go | 19 ++--- pkg/common/cachekey/group.go | 28 +++---- pkg/common/cachekey/msg.go | 23 ------ pkg/common/cachekey/s3.go | 1 - pkg/common/cachekey/user.go | 21 ----- pkg/common/config/config.go | 8 +- pkg/common/db/cache/black.go | 7 +- pkg/common/db/cache/config.go | 62 ++++++++++++++ pkg/common/db/cache/conversation.go | 9 +- pkg/common/db/cache/friend.go | 2 +- pkg/common/db/cache/group.go | 9 +- pkg/common/db/cache/meta_cache.go | 8 +- pkg/common/db/localcache/conversation.go | 87 -------------------- pkg/common/db/localcache/doc.go | 15 ---- pkg/common/db/localcache/group.go | 78 ------------------ pkg/common/db/localcache/meta_local_cache.go | 15 ---- pkg/common/localcache/cache.go | 49 ++++++----- pkg/common/localcache/option.go | 22 ++--- pkg/common/localcache/option/option.go | 15 +--- pkg/rpccache/conversation.go | 28 +++++++ pkg/rpccache/friend.go | 28 +++---- pkg/rpccache/group.go | 28 +++++++ 28 files changed, 268 insertions(+), 386 deletions(-) delete mode 100644 pkg/common/cachekey/msg.go delete mode 100644 pkg/common/cachekey/s3.go delete mode 100644 pkg/common/cachekey/user.go create mode 100644 pkg/common/db/cache/config.go delete mode 100644 pkg/common/db/localcache/conversation.go delete mode 100644 pkg/common/db/localcache/doc.go delete mode 100644 pkg/common/db/localcache/group.go delete mode 100644 pkg/common/db/localcache/meta_local_cache.go create mode 100644 pkg/rpccache/conversation.go create mode 100644 pkg/rpccache/group.go diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 69f578f81..3a416a806 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -530,6 +530,16 @@ prometheus: localCache: friend: - topic: friend + topic: delete_cache_friend + slotNum: 500 + slotSize: 20000 + + group: + topic: delete_cache_group + slotNum: 500 + slotSize: 20000 + + conversation: + topic: delete_cache_conversation slotNum: 500 slotSize: 20000 \ No newline at end of file diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 9e66f8f73..8a9da0577 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -17,6 +17,7 @@ package push import ( "context" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/rpccache" "sync" "google.golang.org/grpc" @@ -28,7 +29,6 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) @@ -51,8 +51,8 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e client, offlinePusher, database, - localcache.NewGroupLocalCache(&groupRpcClient), - localcache.NewConversationLocalCache(&conversationRpcClient), + rpccache.NewGroupLocalCache(rpcclient.NewGroupRpcClient(client), rdb), + rpccache.NewConversationLocalCache(rpcclient.NewConversationRpcClient(client), rdb), &conversationRpcClient, &groupRpcClient, &msgRpcClient, diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 7cee7b99d..2cf1a34b5 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "errors" + "github.com/openimsdk/open-im-server/v3/pkg/rpccache" "google.golang.org/grpc" "sync" @@ -40,7 +41,6 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" @@ -50,8 +50,8 @@ type Pusher struct { database controller.PushDatabase discov discoveryregistry.SvcDiscoveryRegistry offlinePusher offlinepush.OfflinePusher - groupLocalCache *localcache.GroupLocalCache - conversationLocalCache *localcache.ConversationLocalCache + groupLocalCache *rpccache.GroupLocalCache + conversationLocalCache *rpccache.ConversationLocalCache msgRpcClient *rpcclient.MessageRpcClient conversationRpcClient *rpcclient.ConversationRpcClient groupRpcClient *rpcclient.GroupRpcClient @@ -60,7 +60,7 @@ type Pusher struct { var errNoOfflinePusher = errors.New("no offlinePusher is configured") func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, - groupLocalCache *localcache.GroupLocalCache, conversationLocalCache *localcache.ConversationLocalCache, + groupLocalCache *rpccache.GroupLocalCache, conversationLocalCache *rpccache.ConversationLocalCache, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient, ) *Pusher { return &Pusher{ diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index f5b1dd823..08786b43b 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -27,7 +27,6 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) @@ -35,15 +34,14 @@ import ( type ( MessageInterceptorChain []MessageInterceptorFunc msgServer struct { - RegisterCenter discoveryregistry.SvcDiscoveryRegistry - MsgDatabase controller.CommonMsgDatabase - Group *rpcclient.GroupRpcClient - User *rpcclient.UserRpcClient - Conversation *rpcclient.ConversationRpcClient - friend *rpccache.FriendLocalCache - //friend *rpcclient.FriendRpcClient - GroupLocalCache *localcache.GroupLocalCache - ConversationLocalCache *localcache.ConversationLocalCache + RegisterCenter discoveryregistry.SvcDiscoveryRegistry + MsgDatabase controller.CommonMsgDatabase + Group *rpcclient.GroupRpcClient + User *rpcclient.UserRpcClient + Conversation *rpcclient.ConversationRpcClient + friend *rpccache.FriendLocalCache + GroupLocalCache *rpccache.GroupLocalCache + ConversationLocalCache *rpccache.ConversationLocalCache Handlers MessageInterceptorChain notificationSender *rpcclient.NotificationSender } @@ -89,8 +87,8 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e Group: &groupRpcClient, MsgDatabase: msgDatabase, RegisterCenter: client, - GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient), - ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient), + GroupLocalCache: rpccache.NewGroupLocalCache(rpcclient.NewGroupRpcClient(client), rdb), + ConversationLocalCache: rpccache.NewConversationLocalCache(rpcclient.NewConversationRpcClient(client), rdb), friend: rpccache.NewFriendLocalCache(rpcclient.NewFriendRpcClient(client), rdb), } s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg)) diff --git a/pkg/common/cachekey/black.go b/pkg/common/cachekey/black.go index 136972cb7..527ad14dc 100644 --- a/pkg/common/cachekey/black.go +++ b/pkg/common/cachekey/black.go @@ -1,15 +1,15 @@ package cachekey const ( - blackIDsKey = "BLACK_IDS:" - isBlackKey = "IS_BLACK:" + BlackIDsKey = "BLACK_IDS:" + IsBlackKey = "IS_BLACK:" // local cache ) func GetBlackIDsKey(ownerUserID string) string { - return blackIDsKey + ownerUserID + return BlackIDsKey + ownerUserID } func GetIsBlackIDsKey(possibleBlackUserID, userID string) string { - return isBlackKey + userID + "-" + possibleBlackUserID + return IsBlackKey + userID + "-" + possibleBlackUserID } diff --git a/pkg/common/cachekey/conversation.go b/pkg/common/cachekey/conversation.go index eb5b83f0e..665ca11c6 100644 --- a/pkg/common/cachekey/conversation.go +++ b/pkg/common/cachekey/conversation.go @@ -1,48 +1,44 @@ package cachekey -import "time" - const ( - conversationKey = "CONVERSATION:" - conversationIDsKey = "CONVERSATION_IDS:" - conversationIDsHashKey = "CONVERSATION_IDS_HASH:" - conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:" - recvMsgOptKey = "RECV_MSG_OPT:" - superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:" - superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:" - conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:" - - conversationExpireTime = time.Second * 60 * 60 * 12 + ConversationKey = "CONVERSATION:" + ConversationIDsKey = "CONVERSATION_IDS:" + ConversationIDsHashKey = "CONVERSATION_IDS_HASH:" + ConversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:" + RecvMsgOptKey = "RECV_MSG_OPT:" + SuperGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:" + SuperGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:" + ConversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:" ) func GetConversationKey(ownerUserID, conversationID string) string { - return conversationKey + ownerUserID + ":" + conversationID + return ConversationKey + ownerUserID + ":" + conversationID } func GetConversationIDsKey(ownerUserID string) string { - return conversationIDsKey + ownerUserID + return ConversationIDsKey + ownerUserID } func GetSuperGroupRecvNotNotifyUserIDsKey(groupID string) string { - return superGroupRecvMsgNotNotifyUserIDsKey + groupID + return SuperGroupRecvMsgNotNotifyUserIDsKey + groupID } func GetRecvMsgOptKey(ownerUserID, conversationID string) string { - return recvMsgOptKey + ownerUserID + ":" + conversationID + return RecvMsgOptKey + ownerUserID + ":" + conversationID } func GetSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string { - return superGroupRecvMsgNotNotifyUserIDsHashKey + groupID + return SuperGroupRecvMsgNotNotifyUserIDsHashKey + groupID } func GetConversationHasReadSeqKey(ownerUserID, conversationID string) string { - return conversationHasReadSeqKey + ownerUserID + ":" + conversationID + return ConversationHasReadSeqKey + ownerUserID + ":" + conversationID } func GetConversationNotReceiveMessageUserIDsKey(conversationID string) string { - return conversationNotReceiveMessageUserIDsKey + conversationID + return ConversationNotReceiveMessageUserIDsKey + conversationID } func GetUserConversationIDsHashKey(ownerUserID string) string { - return conversationIDsHashKey + ownerUserID + return ConversationIDsHashKey + ownerUserID } diff --git a/pkg/common/cachekey/friend.go b/pkg/common/cachekey/friend.go index 1f9d4e94f..f37c9da37 100644 --- a/pkg/common/cachekey/friend.go +++ b/pkg/common/cachekey/friend.go @@ -1,27 +1,24 @@ package cachekey -import "time" - const ( - friendExpireTime = time.Second * 60 * 60 * 12 - friendIDsKey = "FRIEND_IDS:" - twoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:" - friendKey = "FRIEND_INFO:" - isFriendKey = "IS_FRIEND:" + FriendIDsKey = "FRIEND_IDS:" + TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:" + FriendKey = "FRIEND_INFO:" + IsFriendKey = "IS_FRIEND:" // local cache key ) func GetFriendIDsKey(ownerUserID string) string { - return friendIDsKey + ownerUserID + return FriendIDsKey + ownerUserID } func GetTwoWayFriendsIDsKey(ownerUserID string) string { - return twoWayFriendsIDsKey + ownerUserID + return TwoWayFriendsIDsKey + ownerUserID } func GetFriendKey(ownerUserID, friendUserID string) string { - return friendKey + ownerUserID + "-" + friendUserID + return FriendKey + ownerUserID + "-" + friendUserID } func GetIsFriendKey(possibleFriendUserID, userID string) string { - return isFriendKey + possibleFriendUserID + "-" + userID + return IsFriendKey + possibleFriendUserID + "-" + userID } diff --git a/pkg/common/cachekey/group.go b/pkg/common/cachekey/group.go index 3e5431b83..1dcf0ffce 100644 --- a/pkg/common/cachekey/group.go +++ b/pkg/common/cachekey/group.go @@ -7,39 +7,39 @@ import ( const ( groupExpireTime = time.Second * 60 * 60 * 12 - groupInfoKey = "GROUP_INFO:" - groupMemberIDsKey = "GROUP_MEMBER_IDS:" - groupMembersHashKey = "GROUP_MEMBERS_HASH2:" - groupMemberInfoKey = "GROUP_MEMBER_INFO:" - joinedGroupsKey = "JOIN_GROUPS_KEY:" - groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:" - groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:" + GroupInfoKey = "GROUP_INFO:" + GroupMemberIDsKey = "GROUP_MEMBER_IDS:" + GroupMembersHashKey = "GROUP_MEMBERS_HASH2:" + GroupMemberInfoKey = "GROUP_MEMBER_INFO:" + JoinedGroupsKey = "JOIN_GROUPS_KEY:" + GroupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:" + GroupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:" ) func GetGroupInfoKey(groupID string) string { - return groupInfoKey + groupID + return GroupInfoKey + groupID } func GetJoinedGroupsKey(userID string) string { - return joinedGroupsKey + userID + return JoinedGroupsKey + userID } func GetGroupMembersHashKey(groupID string) string { - return groupMembersHashKey + groupID + return GroupMembersHashKey + groupID } func GetGroupMemberIDsKey(groupID string) string { - return groupMemberIDsKey + groupID + return GroupMemberIDsKey + groupID } func GetGroupMemberInfoKey(groupID, userID string) string { - return groupMemberInfoKey + groupID + "-" + userID + return GroupMemberInfoKey + groupID + "-" + userID } func GetGroupMemberNumKey(groupID string) string { - return groupMemberNumKey + groupID + return GroupMemberNumKey + groupID } func GetGroupRoleLevelMemberIDsKey(groupID string, roleLevel int32) string { - return groupRoleLevelMemberIDsKey + groupID + "-" + strconv.Itoa(int(roleLevel)) + return GroupRoleLevelMemberIDsKey + groupID + "-" + strconv.Itoa(int(roleLevel)) } diff --git a/pkg/common/cachekey/msg.go b/pkg/common/cachekey/msg.go deleted file mode 100644 index b19c4f473..000000000 --- a/pkg/common/cachekey/msg.go +++ /dev/null @@ -1,23 +0,0 @@ -package cachekey - -const ( - maxSeq = "MAX_SEQ:" - minSeq = "MIN_SEQ:" - conversationUserMinSeq = "CON_USER_MIN_SEQ:" - hasReadSeq = "HAS_READ_SEQ:" - - //appleDeviceToken = "DEVICE_TOKEN" - getuiToken = "GETUI_TOKEN" - getuiTaskID = "GETUI_TASK_ID" - //signalCache = "SIGNAL_CACHE:" - //signalListCache = "SIGNAL_LIST_CACHE:" - FCM_TOKEN = "FCM_TOKEN:" - - messageCache = "MESSAGE_CACHE:" - messageDelUserList = "MESSAGE_DEL_USER_LIST:" - userDelMessagesList = "USER_DEL_MESSAGES_LIST:" - sendMsgFailedFlag = "SEND_MSG_FAILED_FLAG:" - userBadgeUnreadCountSum = "USER_BADGE_UNREAD_COUNT_SUM:" - exTypeKeyLocker = "EX_LOCK:" - uidPidToken = "UID_PID_TOKEN_STATUS:" -) diff --git a/pkg/common/cachekey/s3.go b/pkg/common/cachekey/s3.go deleted file mode 100644 index cbf8bfad5..000000000 --- a/pkg/common/cachekey/s3.go +++ /dev/null @@ -1 +0,0 @@ -package cachekey diff --git a/pkg/common/cachekey/user.go b/pkg/common/cachekey/user.go deleted file mode 100644 index ed11641d5..000000000 --- a/pkg/common/cachekey/user.go +++ /dev/null @@ -1,21 +0,0 @@ -package cachekey - -import "time" - -const ( - userExpireTime = time.Second * 60 * 60 * 12 - userInfoKey = "USER_INFO:" - userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:" - olineStatusKey = "ONLINE_STATUS:" - userOlineStatusExpireTime = time.Second * 60 * 60 * 24 - statusMod = 501 - platformID = "_PlatformIDSuffix" -) - -func GetUserInfoKey(userID string) string { - return userInfoKey + userID -} - -func GetUserGlobalRecvMsgOptKey(userID string) string { - return userGlobalRecvMsgOptKey + userID -} diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index a8d865ef5..b06209c16 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -378,8 +378,14 @@ type LocalCache struct { SlotSize int `yaml:"slotSize"` } +func (l LocalCache) Enable() bool { + return l.Topic != "" && l.SlotNum > 0 && l.SlotSize > 0 +} + type localCache struct { - Friend LocalCache `yaml:"friend"` + Friend LocalCache `yaml:"friend"` + Group LocalCache `yaml:"group"` + Conversation LocalCache `yaml:"conversation"` } func (c *configStruct) GetServiceNames() []string { diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index ca116fdc7..716c9eef0 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -16,7 +16,6 @@ package cache import ( "context" - "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" @@ -56,9 +55,7 @@ func NewBlackCacheRedis( ) BlackCache { rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) - f := config.Config.LocalCache.Friend - log.ZDebug(context.Background(), "black local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize) - mc.SetTopic(f.Topic) + mc.SetTopic(config.Config.LocalCache.Friend.Topic) mc.SetRawRedisClient(rdb) return &BlackCacheRedis{ expireTime: blackExpireTime, @@ -73,7 +70,7 @@ func (b *BlackCacheRedis) NewCache() BlackCache { expireTime: b.expireTime, rcClient: b.rcClient, blackDB: b.blackDB, - metaCache: b.metaCache.Copy(), + metaCache: b.Copy(), } } diff --git a/pkg/common/db/cache/config.go b/pkg/common/db/cache/config.go new file mode 100644 index 000000000..dc5c55143 --- /dev/null +++ b/pkg/common/db/cache/config.go @@ -0,0 +1,62 @@ +package cache + +import ( + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "strings" + "sync" +) + +var ( + once sync.Once + subscribe map[string][]string +) + +func getPublishKey(topic string, key []string) []string { + if topic == "" || len(key) == 0 { + return nil + } + once.Do(func() { + list := []struct { + Local config.LocalCache + Keys []string + }{ + { + Local: config.Config.LocalCache.Group, + Keys: []string{cachekey.GroupMemberIDsKey}, + }, + { + Local: config.Config.LocalCache.Friend, + Keys: []string{cachekey.FriendIDsKey, cachekey.BlackIDsKey}, + }, + { + Local: config.Config.LocalCache.Conversation, + Keys: []string{cachekey.ConversationIDsKey}, + }, + } + subscribe = make(map[string][]string) + for _, v := range list { + if v.Local.Enable() { + subscribe[v.Local.Topic] = v.Keys + } + } + }) + prefix, ok := subscribe[topic] + if !ok { + return nil + } + res := make([]string, 0, len(key)) + for _, k := range key { + var exist bool + for _, p := range prefix { + if strings.HasPrefix(k, p) { + exist = true + break + } + } + if exist { + res = append(res, k) + } + } + return res +} diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index cf638f38f..9c0391e67 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -18,6 +18,7 @@ import ( "context" "errors" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "math/big" "strings" "time" @@ -85,10 +86,12 @@ type ConversationCache interface { func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationtb.ConversationModelInterface) ConversationCache { rcClient := rockscache.NewClient(rdb, opts) - + mc := NewMetaCacheRedis(rcClient) + mc.SetTopic(config.Config.LocalCache.Conversation.Topic) + mc.SetRawRedisClient(rdb) return &ConversationRedisCache{ rcClient: rcClient, - metaCache: NewMetaCacheRedis(rcClient), + metaCache: mc, conversationDB: db, expireTime: conversationExpireTime, } @@ -119,7 +122,7 @@ type ConversationRedisCache struct { func (c *ConversationRedisCache) NewCache() ConversationCache { return &ConversationRedisCache{ rcClient: c.rcClient, - metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), + metaCache: c.Copy(), conversationDB: c.conversationDB, expireTime: c.expireTime, } diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index 6d287d2f5..432d08572 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -80,7 +80,7 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendMo func (f *FriendCacheRedis) NewCache() FriendCache { return &FriendCacheRedis{ rcClient: f.rcClient, - metaCache: f.metaCache.Copy(), + metaCache: f.Copy(), friendDB: f.friendDB, expireTime: f.expireTime, } diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index 58c42ccfd..783f2e515 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" "github.com/OpenIMSDK/protocol/constant" @@ -104,12 +105,14 @@ func NewGroupCacheRedis( opts rockscache.Options, ) GroupCache { rcClient := rockscache.NewClient(rdb, opts) - + mc := NewMetaCacheRedis(rcClient) + mc.SetTopic(config.Config.LocalCache.Group.Topic) + mc.SetRawRedisClient(rdb) return &GroupCacheRedis{ rcClient: rcClient, expireTime: groupExpireTime, groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB, groupHash: hashCode, - metaCache: NewMetaCacheRedis(rcClient), + metaCache: mc, } } @@ -120,7 +123,7 @@ func (g *GroupCacheRedis) NewCache() GroupCache { groupDB: g.groupDB, groupMemberDB: g.groupMemberDB, groupRequestDB: g.groupRequestDB, - metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...), + metaCache: g.Copy(), } } diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index dbe2344f0..86ade0b68 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -103,13 +103,13 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error { break } } - if m.topic != "" && m.redisClient != nil { - data, err := json.Marshal(m.keys) + if pk := getPublishKey(m.topic, m.keys); len(pk) > 0 { + data, err := json.Marshal(pk) if err != nil { - log.ZError(ctx, "keys json marshal failed", err, "topic", m.topic, "keys", m.keys) + log.ZError(ctx, "keys json marshal failed", err, "topic", m.topic, "keys", pk) } else { if err := m.redisClient.Publish(ctx, m.topic, string(data)).Err(); err != nil { - log.ZError(ctx, "redis publish cache delete error", err, "topic", m.topic, "keys", m.keys) + log.ZError(ctx, "redis publish cache delete error", err, "topic", m.topic, "keys", pk) } } } diff --git a/pkg/common/db/localcache/conversation.go b/pkg/common/db/localcache/conversation.go deleted file mode 100644 index c40bcdbce..000000000 --- a/pkg/common/db/localcache/conversation.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package localcache - -import ( - "context" - "sync" - - "github.com/OpenIMSDK/protocol/conversation" - - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" -) - -type ConversationLocalCache struct { - lock sync.Mutex - superGroupRecvMsgNotNotifyUserIDs map[string]Hash - conversationIDs map[string]Hash - client *rpcclient.ConversationRpcClient -} - -type Hash struct { - hash uint64 - ids []string -} - -func NewConversationLocalCache(client *rpcclient.ConversationRpcClient) *ConversationLocalCache { - return &ConversationLocalCache{ - superGroupRecvMsgNotNotifyUserIDs: make(map[string]Hash), - conversationIDs: make(map[string]Hash), - client: client, - } -} - -func (g *ConversationLocalCache) GetRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) { - resp, err := g.client.Client.GetRecvMsgNotNotifyUserIDs(ctx, &conversation.GetRecvMsgNotNotifyUserIDsReq{ - GroupID: groupID, - }) - if err != nil { - return nil, err - } - return resp.UserIDs, nil -} - -func (g *ConversationLocalCache) GetConversationIDs(ctx context.Context, userID string) ([]string, error) { - resp, err := g.client.Client.GetUserConversationIDsHash(ctx, &conversation.GetUserConversationIDsHashReq{ - OwnerUserID: userID, - }) - if err != nil { - return nil, err - } - - g.lock.Lock() - hash, ok := g.conversationIDs[userID] - g.lock.Unlock() - - if !ok || hash.hash != resp.Hash { - conversationIDsResp, err := g.client.Client.GetConversationIDs(ctx, &conversation.GetConversationIDsReq{ - UserID: userID, - }) - if err != nil { - return nil, err - } - - g.lock.Lock() - defer g.lock.Unlock() - g.conversationIDs[userID] = Hash{ - hash: resp.Hash, - ids: conversationIDsResp.ConversationIDs, - } - - return conversationIDsResp.ConversationIDs, nil - } - - return hash.ids, nil -} diff --git a/pkg/common/db/localcache/doc.go b/pkg/common/db/localcache/doc.go deleted file mode 100644 index d349373ee..000000000 --- a/pkg/common/db/localcache/doc.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package localcache // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" diff --git a/pkg/common/db/localcache/group.go b/pkg/common/db/localcache/group.go deleted file mode 100644 index 4958d91ee..000000000 --- a/pkg/common/db/localcache/group.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package localcache - -import ( - "context" - "sync" - - "github.com/OpenIMSDK/protocol/group" - "github.com/OpenIMSDK/tools/errs" - - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" -) - -type GroupLocalCache struct { - lock sync.Mutex - cache map[string]GroupMemberIDsHash - client *rpcclient.GroupRpcClient -} - -type GroupMemberIDsHash struct { - memberListHash uint64 - userIDs []string -} - -func NewGroupLocalCache(client *rpcclient.GroupRpcClient) *GroupLocalCache { - return &GroupLocalCache{ - cache: make(map[string]GroupMemberIDsHash, 0), - client: client, - } -} - -func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { - resp, err := g.client.Client.GetGroupAbstractInfo(ctx, &group.GetGroupAbstractInfoReq{ - GroupIDs: []string{groupID}, - }) - if err != nil { - return nil, err - } - if len(resp.GroupAbstractInfos) < 1 { - return nil, errs.ErrGroupIDNotFound - } - - g.lock.Lock() - localHashInfo, ok := g.cache[groupID] - if ok && localHashInfo.memberListHash == resp.GroupAbstractInfos[0].GroupMemberListHash { - g.lock.Unlock() - return localHashInfo.userIDs, nil - } - g.lock.Unlock() - - groupMembersResp, err := g.client.Client.GetGroupMemberUserIDs(ctx, &group.GetGroupMemberUserIDsReq{ - GroupID: groupID, - }) - if err != nil { - return nil, err - } - - g.lock.Lock() - defer g.lock.Unlock() - g.cache[groupID] = GroupMemberIDsHash{ - memberListHash: resp.GroupAbstractInfos[0].GroupMemberListHash, - userIDs: groupMembersResp.UserIDs, - } - return g.cache[groupID].userIDs, nil -} diff --git a/pkg/common/db/localcache/meta_local_cache.go b/pkg/common/db/localcache/meta_local_cache.go deleted file mode 100644 index ed9389c27..000000000 --- a/pkg/common/db/localcache/meta_local_cache.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package localcache diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index 228538fa6..56cf14ff8 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -4,11 +4,11 @@ import ( "context" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/link" "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" - opt "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" + lopt "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" ) type Cache[V any] interface { - Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) + Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*lopt.Option) (V, error) Del(ctx context.Context, key ...string) } @@ -17,12 +17,17 @@ func New[V any](opts ...Option) Cache[V] { for _, o := range opts { o(opt) } - c := &cache[V]{opt: opt, link: link.New(opt.localSlotNum)} - c.local = local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) - go func() { - c.opt.delCh(c.del) - }() - return c + c := cache[V]{opt: opt} + if opt.localSlotNum > 0 && opt.localSlotSize > 0 { + c.local = local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + go func() { + c.opt.delCh(c.del) + }() + if opt.linkSlotNum > 0 { + c.link = link.New(opt.linkSlotNum) + } + } + return &c } type cache[V any] struct { @@ -32,10 +37,12 @@ type cache[V any] struct { } func (c *cache[V]) onEvict(key string, value V) { - lks := c.link.Del(key) - for k := range lks { - if key != k { // prevent deadlock - c.local.Del(k) + if c.link != nil { + lks := c.link.Del(key) + for k := range lks { + if key != k { // prevent deadlock + c.local.Del(k) + } } } } @@ -50,16 +57,14 @@ func (c *cache[V]) del(key ...string) { } } -func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*opt.Option) (V, error) { - enable := c.opt.enable - if len(opts) > 0 && opts[0].Enable != nil { - enable = *opts[0].Enable - } - if enable { - if len(opts) > 0 && len(opts[0].Link) > 0 { - c.link.Link(key, opts[0].Link...) - } +func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*lopt.Option) (V, error) { + if c.local != nil { return c.local.Get(key, func() (V, error) { + if c.link != nil { + for _, o := range opts { + c.link.Link(key, o.Link...) + } + } return fetch(ctx) }) } else { @@ -74,7 +79,7 @@ func (c *cache[V]) Del(ctx context.Context, key ...string) { for _, fn := range c.opt.delFn { fn(ctx, key...) } - if c.opt.enable { + if c.local != nil { c.del(key...) } } diff --git a/pkg/common/localcache/option.go b/pkg/common/localcache/option.go index 01c9ce57d..f23a04e68 100644 --- a/pkg/common/localcache/option.go +++ b/pkg/common/localcache/option.go @@ -8,9 +8,9 @@ import ( func defaultOption() *option { return &option{ - enable: true, localSlotNum: 500, localSlotSize: 20000, + linkSlotNum: 500, localSuccessTTL: time.Minute, localFailedTTL: time.Second * 5, delFn: make([]func(ctx context.Context, key ...string), 0, 2), @@ -19,9 +19,9 @@ func defaultOption() *option { } type option struct { - enable bool localSlotNum int localSlotSize int + linkSlotNum int localSuccessTTL time.Duration localFailedTTL time.Duration delFn []func(ctx context.Context, key ...string) @@ -31,25 +31,27 @@ type option struct { type Option func(o *option) -func WithDisable() Option { +func WithLocalDisable() Option { + return WithLinkSlotNum(0) +} + +func WithLinkDisable() Option { + return WithLinkSlotNum(0) +} + +func WithLinkSlotNum(linkSlotNum int) Option { return func(o *option) { - o.enable = false + o.linkSlotNum = linkSlotNum } } func WithLocalSlotNum(localSlotNum int) Option { - if localSlotNum < 1 { - panic("localSlotNum should be greater than 0") - } return func(o *option) { o.localSlotNum = localSlotNum } } func WithLocalSlotSize(localSlotSize int) Option { - if localSlotSize < 1 { - panic("localSlotSize should be greater than 0") - } return func(o *option) { o.localSlotSize = localSlotSize } diff --git a/pkg/common/localcache/option/option.go b/pkg/common/localcache/option/option.go index 798c93ba5..8547b3339 100644 --- a/pkg/common/localcache/option/option.go +++ b/pkg/common/localcache/option/option.go @@ -5,20 +5,7 @@ func NewOption() *Option { } type Option struct { - Enable *bool - Link []string -} - -func (o *Option) WithEnable() *Option { - t := true - o.Enable = &t - return o -} - -func (o *Option) WithDisable() *Option { - f := false - o.Enable = &f - return o + Link []string } func (o *Option) WithLink(key ...string) *Option { diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go new file mode 100644 index 000000000..181b8fd8a --- /dev/null +++ b/pkg/rpccache/conversation.go @@ -0,0 +1,28 @@ +package rpccache + +import ( + "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" +) + +func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis.UniversalClient) *ConversationLocalCache { + return &ConversationLocalCache{ + local: localcache.New[any](localcache.WithRedisDeleteSubscribe(config.Config.LocalCache.Conversation.Topic, cli)), + client: client, + } +} + +type ConversationLocalCache struct { + local localcache.Cache[any] + client rpcclient.ConversationRpcClient +} + +func (c *ConversationLocalCache) GetConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) { + return localcache.AnyValue[[]string](c.local.Get(ctx, cachekey.GetConversationIDsKey(ownerUserID), func(ctx context.Context) (any, error) { + return c.client.GetConversationIDs(ctx, ownerUserID) + })) +} diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 14adf1e0a..82d0a03a5 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -23,20 +23,20 @@ type FriendLocalCache struct { client rpcclient.FriendRpcClient } -func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) (val []string, err error) { - log.ZDebug(ctx, "FriendLocalCache GetFriendIDs req", "ownerUserID", ownerUserID) - defer func() { - if err == nil { - log.ZDebug(ctx, "FriendLocalCache GetFriendIDs return", "value", val) - } else { - log.ZError(ctx, "FriendLocalCache GetFriendIDs return", err) - } - }() - return localcache.AnyValue[[]string](f.local.Get(ctx, cachekey.GetFriendIDsKey(ownerUserID), func(ctx context.Context) (any, error) { - log.ZDebug(ctx, "FriendLocalCache GetFriendIDs call rpc", "ownerUserID", ownerUserID) - return f.client.GetFriendIDs(ctx, ownerUserID) - })) -} +//func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) (val []string, err error) { +// log.ZDebug(ctx, "FriendLocalCache GetFriendIDs req", "ownerUserID", ownerUserID) +// defer func() { +// if err == nil { +// log.ZDebug(ctx, "FriendLocalCache GetFriendIDs return", "value", val) +// } else { +// log.ZError(ctx, "FriendLocalCache GetFriendIDs return", err) +// } +// }() +// return localcache.AnyValue[[]string](f.local.Get(ctx, cachekey.GetFriendIDsKey(ownerUserID), func(ctx context.Context) (any, error) { +// log.ZDebug(ctx, "FriendLocalCache GetFriendIDs call rpc", "ownerUserID", ownerUserID) +// return f.client.GetFriendIDs(ctx, ownerUserID) +// })) +//} func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (val bool, err error) { log.ZDebug(ctx, "FriendLocalCache IsFriend req", "possibleFriendUserID", possibleFriendUserID, "userID", userID) diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go new file mode 100644 index 000000000..0c3bc6b93 --- /dev/null +++ b/pkg/rpccache/group.go @@ -0,0 +1,28 @@ +package rpccache + +import ( + "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" +) + +func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClient) *GroupLocalCache { + return &GroupLocalCache{ + local: localcache.New[any](localcache.WithRedisDeleteSubscribe(config.Config.LocalCache.Group.Topic, cli)), + client: client, + } +} + +type GroupLocalCache struct { + local localcache.Cache[any] + client rpcclient.GroupRpcClient +} + +func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { + return localcache.AnyValue[[]string](g.local.Get(ctx, cachekey.GetGroupMemberIDsKey(groupID), func(ctx context.Context) (any, error) { + return g.client.GetGroupMemberIDs(ctx, groupID) + })) +} From a81bc3fc23991208d66bb1e3b7365d9dc0015f63 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Fri, 12 Jan 2024 17:51:01 +0800 Subject: [PATCH 29/66] feat: local cache --- go.mod | 5 +- pkg/common/localcache/business.go | 70 -------------- pkg/common/localcache/lru_test.go | 55 ----------- pkg/common/localcache/singleflight.go | 43 --------- pkg/common/localcache/target.go | 59 ------------ pkg/common/localcache/timingwheel.go | 71 -------------- pkg/localcache/business.go | 71 ++++++++++++++ pkg/{common => }/localcache/cache.go | 6 +- pkg/localcache/go.mod | 5 + pkg/{common => }/localcache/link/link.go | 0 pkg/{common => }/localcache/link/link_test.go | 0 pkg/localcache/local/cache.go | 50 ++++++++++ .../local}/callback.go | 2 +- .../localcache => localcache/local}/lru.go | 9 +- pkg/localcache/local/lru_test.go | 95 +++++++++++++++++++ pkg/localcache/local/target.go | 10 ++ pkg/{common => }/localcache/option.go | 2 +- pkg/{common => }/localcache/option/option.go | 0 pkg/{common => }/localcache/tool.go | 0 pkg/rpccache/conversation.go | 2 +- pkg/rpccache/friend.go | 4 +- pkg/rpccache/group.go | 2 +- 22 files changed, 245 insertions(+), 316 deletions(-) delete mode 100644 pkg/common/localcache/business.go delete mode 100644 pkg/common/localcache/lru_test.go delete mode 100644 pkg/common/localcache/singleflight.go delete mode 100644 pkg/common/localcache/target.go delete mode 100644 pkg/common/localcache/timingwheel.go create mode 100644 pkg/localcache/business.go rename pkg/{common => }/localcache/cache.go (88%) create mode 100644 pkg/localcache/go.mod rename pkg/{common => }/localcache/link/link.go (100%) rename pkg/{common => }/localcache/link/link_test.go (100%) create mode 100644 pkg/localcache/local/cache.go rename pkg/{common/localcache => localcache/local}/callback.go (86%) rename pkg/{common/localcache => localcache/local}/lru.go (91%) create mode 100644 pkg/localcache/local/lru_test.go create mode 100644 pkg/localcache/local/target.go rename pkg/{common => }/localcache/option.go (97%) rename pkg/{common => }/localcache/option/option.go (100%) rename pkg/{common => }/localcache/tool.go (100%) diff --git a/go.mod b/go.mod index 11a87d882..1086560bc 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/minio/minio-go/v7 v7.0.63 github.com/mitchellh/mapstructure v1.5.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/openimsdk/localcache v0.0.1 github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.17.0 github.com/robfig/cron/v3 v3.0.1 @@ -37,7 +38,6 @@ require ( github.com/IBM/sarama v1.41.3 github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/go-redis/redis v6.15.9+incompatible - github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/redis/go-redis/v9 v9.2.1 github.com/stathat/consistent v1.0.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 @@ -82,6 +82,7 @@ require ( github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect @@ -157,3 +158,5 @@ require ( golang.org/x/crypto v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + +replace github.com/openimsdk/localcache => ./pkg/localcache diff --git a/pkg/common/localcache/business.go b/pkg/common/localcache/business.go deleted file mode 100644 index f011719df..000000000 --- a/pkg/common/localcache/business.go +++ /dev/null @@ -1,70 +0,0 @@ -package localcache - -import ( - "context" - "encoding/json" - "github.com/OpenIMSDK/tools/log" - "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" -) - -func WithRedisDeleteSubscribe(topic string, cli redis.UniversalClient) Option { - return WithDeleteLocal(func(fn func(key ...string)) { - if fn == nil { - log.ZDebug(context.Background(), "WithRedisDeleteSubscribe fn is nil", "topic", topic) - return - } - msg := cli.Subscribe(context.Background(), topic).Channel() - for m := range msg { - log.ZDebug(context.Background(), "WithRedisDeleteSubscribe delete", "topic", m.Channel, "payload", m.Payload) - var key []string - if err := json.Unmarshal([]byte(m.Payload), &key); err != nil { - log.ZError(context.Background(), "WithRedisDeleteSubscribe json unmarshal error", err, "topic", topic, "payload", m.Payload) - continue - } - if len(key) == 0 { - continue - } - fn(key...) - } - }) -} - -func WithRedisDeletePublish(topic string, cli redis.UniversalClient) Option { - return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { - data, err := json.Marshal(key) - if err != nil { - log.ZError(ctx, "json marshal error", err, "topic", topic, "key", key) - return - } - if err := cli.Publish(ctx, topic, data).Err(); err != nil { - log.ZError(ctx, "redis publish error", err, "topic", topic, "key", key) - } else { - log.ZDebug(ctx, "redis publish success", "topic", topic, "key", key) - } - }) -} - -func WithRedisDelete(cli redis.UniversalClient) Option { - return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { - for _, s := range key { - if err := cli.Del(ctx, s).Err(); err != nil { - log.ZError(ctx, "redis delete error", err, "key", s) - } else { - log.ZDebug(ctx, "redis delete success", "key", s) - } - } - }) -} - -func WithRocksCacheDelete(cli *rockscache.Client) Option { - return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { - for _, k := range key { - if err := cli.TagAsDeleted2(ctx, k); err != nil { - log.ZError(ctx, "rocksdb delete error", err, "key", k) - } else { - log.ZDebug(ctx, "rocksdb delete success", "key", k) - } - } - }) -} diff --git a/pkg/common/localcache/lru_test.go b/pkg/common/localcache/lru_test.go deleted file mode 100644 index fff925eaa..000000000 --- a/pkg/common/localcache/lru_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package localcache - -//func TestName(t *testing.T) { -// target := &cacheTarget{} -// l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) -// //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) -// -// fn := func(key string, n int, fetch func() (string, error)) { -// for i := 0; i < n; i++ { -// //v, err := l.Get(key, fetch) -// //if err == nil { -// // t.Log("key", key, "value", v) -// //} else { -// // t.Error("key", key, err) -// //} -// l.Get(key, fetch) -// //time.Sleep(time.Second / 100) -// } -// } -// -// tmp := make(map[string]struct{}) -// -// var wg sync.WaitGroup -// for i := 0; i < 10000; i++ { -// wg.Add(1) -// key := fmt.Sprintf("key_%d", i%200) -// tmp[key] = struct{}{} -// go func() { -// defer wg.Done() -// //t.Log(key) -// fn(key, 10000, func() (string, error) { -// //time.Sleep(time.Second * 3) -// //t.Log(time.Now(), "key", key, "fetch") -// //if rand.Uint32()%5 == 0 { -// // return "value_" + key, nil -// //} -// //return "", errors.New("rand error") -// return "value_" + key, nil -// }) -// }() -// -// //wg.Add(1) -// //go func() { -// // defer wg.Done() -// // for i := 0; i < 10; i++ { -// // l.Del(key) -// // time.Sleep(time.Second / 3) -// // } -// //}() -// } -// wg.Wait() -// t.Log(len(tmp)) -// t.Log(target.String()) -// -//} diff --git a/pkg/common/localcache/singleflight.go b/pkg/common/localcache/singleflight.go deleted file mode 100644 index 5dcd70669..000000000 --- a/pkg/common/localcache/singleflight.go +++ /dev/null @@ -1,43 +0,0 @@ -package localcache - -import "sync" - -type call[K comparable, V any] struct { - wg sync.WaitGroup - val V - err error -} - -type SingleFlight[K comparable, V any] struct { - mu sync.Mutex - m map[K]*call[K, V] -} - -func NewSingleFlight[K comparable, V any]() *SingleFlight[K, V] { - return &SingleFlight[K, V]{m: make(map[K]*call[K, V])} -} - -func (r *SingleFlight[K, V]) Do(key K, fn func() (V, error)) (V, error) { - r.mu.Lock() - if r.m == nil { - r.m = make(map[K]*call[K, V]) - } - if c, ok := r.m[key]; ok { - r.mu.Unlock() - c.wg.Wait() - return c.val, c.err - } - c := new(call[K, V]) - c.wg.Add(1) - r.m[key] = c - r.mu.Unlock() - - c.val, c.err = fn() - c.wg.Done() - - r.mu.Lock() - delete(r.m, key) - r.mu.Unlock() - - return c.val, c.err -} diff --git a/pkg/common/localcache/target.go b/pkg/common/localcache/target.go deleted file mode 100644 index 2edf51ddb..000000000 --- a/pkg/common/localcache/target.go +++ /dev/null @@ -1,59 +0,0 @@ -package localcache - -import ( - "fmt" - "sync/atomic" -) - -type Target interface { - IncrGetHit() - IncrGetSuccess() - IncrGetFailed() - - IncrDelHit() - IncrDelNotFound() -} - -type cacheTarget struct { - getHit int64 - getSuccess int64 - getFailed int64 - delHit int64 - delNotFound int64 -} - -func (r *cacheTarget) IncrGetHit() { - atomic.AddInt64(&r.getHit, 1) -} - -func (r *cacheTarget) IncrGetSuccess() { - atomic.AddInt64(&r.getSuccess, 1) -} - -func (r *cacheTarget) IncrGetFailed() { - atomic.AddInt64(&r.getFailed, 1) -} - -func (r *cacheTarget) IncrDelHit() { - atomic.AddInt64(&r.delHit, 1) -} - -func (r *cacheTarget) IncrDelNotFound() { - atomic.AddInt64(&r.delNotFound, 1) -} - -func (r *cacheTarget) String() string { - return fmt.Sprintf("getHit: %d, getSuccess: %d, getFailed: %d, delHit: %d, delNotFound: %d", r.getHit, r.getSuccess, r.getFailed, r.delHit, r.delNotFound) -} - -type emptyTarget struct{} - -func (e emptyTarget) IncrGetHit() {} - -func (e emptyTarget) IncrGetSuccess() {} - -func (e emptyTarget) IncrGetFailed() {} - -func (e emptyTarget) IncrDelHit() {} - -func (e emptyTarget) IncrDelNotFound() {} diff --git a/pkg/common/localcache/timingwheel.go b/pkg/common/localcache/timingwheel.go deleted file mode 100644 index ca1bac033..000000000 --- a/pkg/common/localcache/timingwheel.go +++ /dev/null @@ -1,71 +0,0 @@ -package localcache - -import ( - "sync" - "time" -) - -type Execute[K comparable, V any] func(K, V) - -type Task[K comparable, V any] struct { - key K - value V -} - -type TimeWheel[K comparable, V any] struct { - ticker *time.Ticker - slots [][]Task[K, V] - currentPos int - size int - slotMutex sync.Mutex - execute Execute[K, V] -} - -func NewTimeWheel[K comparable, V any](size int, tickDuration time.Duration, execute Execute[K, V]) *TimeWheel[K, V] { - return &TimeWheel[K, V]{ - ticker: time.NewTicker(tickDuration), - slots: make([][]Task[K, V], size), - currentPos: 0, - size: size, - execute: execute, - } -} - -func (tw *TimeWheel[K, V]) Start() { - for range tw.ticker.C { - tw.tick() - } -} - -func (tw *TimeWheel[K, V]) Stop() { - tw.ticker.Stop() -} - -func (tw *TimeWheel[K, V]) tick() { - tw.slotMutex.Lock() - defer tw.slotMutex.Unlock() - - tasks := tw.slots[tw.currentPos] - tw.slots[tw.currentPos] = nil - if len(tasks) > 0 { - go func(tasks []Task[K, V]) { - for _, task := range tasks { - tw.execute(task.key, task.value) - } - }(tasks) - } - - tw.currentPos = (tw.currentPos + 1) % tw.size -} - -func (tw *TimeWheel[K, V]) AddTask(delay int, task Task[K, V]) { - if delay < 0 || delay >= tw.size { - return - } - - tw.slotMutex.Lock() - defer tw.slotMutex.Unlock() - - pos := (tw.currentPos + delay) % tw.size - tw.slots[pos] = append(tw.slots[pos], task) -} diff --git a/pkg/localcache/business.go b/pkg/localcache/business.go new file mode 100644 index 000000000..c5260b3e2 --- /dev/null +++ b/pkg/localcache/business.go @@ -0,0 +1,71 @@ +package localcache + +// +//import ( +// "context" +// "encoding/json" +// "github.com/OpenIMSDK/tools/log" +// "github.com/dtm-labs/rockscache" +// "github.com/redis/go-redis/v9" +//) +// +//func WithRedisDeleteSubscribe(topic string, cli redis.UniversalClient) Option { +// return WithDeleteLocal(func(fn func(key ...string)) { +// if fn == nil { +// log.ZDebug(context.Background(), "WithRedisDeleteSubscribe fn is nil", "topic", topic) +// return +// } +// msg := cli.Subscribe(context.Background(), topic).Channel() +// for m := range msg { +// log.ZDebug(context.Background(), "WithRedisDeleteSubscribe delete", "topic", m.Channel, "payload", m.Payload) +// var key []string +// if err := json.Unmarshal([]byte(m.Payload), &key); err != nil { +// log.ZError(context.Background(), "WithRedisDeleteSubscribe json unmarshal error", err, "topic", topic, "payload", m.Payload) +// continue +// } +// if len(key) == 0 { +// continue +// } +// fn(key...) +// } +// }) +//} +// +//func WithRedisDeletePublish(topic string, cli redis.UniversalClient) Option { +// return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { +// data, err := json.Marshal(key) +// if err != nil { +// log.ZError(ctx, "json marshal error", err, "topic", topic, "key", key) +// return +// } +// if err := cli.Publish(ctx, topic, data).Err(); err != nil { +// log.ZError(ctx, "redis publish error", err, "topic", topic, "key", key) +// } else { +// log.ZDebug(ctx, "redis publish success", "topic", topic, "key", key) +// } +// }) +//} +// +//func WithRedisDelete(cli redis.UniversalClient) Option { +// return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { +// for _, s := range key { +// if err := cli.Del(ctx, s).Err(); err != nil { +// log.ZError(ctx, "redis delete error", err, "key", s) +// } else { +// log.ZDebug(ctx, "redis delete success", "key", s) +// } +// } +// }) +//} +// +//func WithRocksCacheDelete(cli *rockscache.Client) Option { +// return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { +// for _, k := range key { +// if err := cli.TagAsDeleted2(ctx, k); err != nil { +// log.ZError(ctx, "rocksdb delete error", err, "key", k) +// } else { +// log.ZDebug(ctx, "rocksdb delete success", "key", k) +// } +// } +// }) +//} diff --git a/pkg/common/localcache/cache.go b/pkg/localcache/cache.go similarity index 88% rename from pkg/common/localcache/cache.go rename to pkg/localcache/cache.go index 56cf14ff8..2d2e8018e 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/localcache/cache.go @@ -2,9 +2,9 @@ package localcache import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/link" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" - lopt "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" + "github.com/openimsdk/localcache/link" + "github.com/openimsdk/localcache/local" + lopt "github.com/openimsdk/localcache/option" ) type Cache[V any] interface { diff --git a/pkg/localcache/go.mod b/pkg/localcache/go.mod new file mode 100644 index 000000000..5f0793042 --- /dev/null +++ b/pkg/localcache/go.mod @@ -0,0 +1,5 @@ +module github.com/openimsdk/localcache + +go 1.19 + +require github.com/hashicorp/golang-lru/v2 v2.0.7 diff --git a/pkg/common/localcache/link/link.go b/pkg/localcache/link/link.go similarity index 100% rename from pkg/common/localcache/link/link.go rename to pkg/localcache/link/link.go diff --git a/pkg/common/localcache/link/link_test.go b/pkg/localcache/link/link_test.go similarity index 100% rename from pkg/common/localcache/link/link_test.go rename to pkg/localcache/link/link_test.go diff --git a/pkg/localcache/local/cache.go b/pkg/localcache/local/cache.go new file mode 100644 index 000000000..06569965e --- /dev/null +++ b/pkg/localcache/local/cache.go @@ -0,0 +1,50 @@ +package local + +import ( + "hash/fnv" + "time" + "unsafe" +) + +type Cache[V any] interface { + Get(key string, fetch func() (V, error)) (V, error) + Del(key string) bool +} + +func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[string, V]) Cache[V] { + c := &slot[V]{ + n: uint64(slotNum), + slots: make([]*LRU[string, V], slotNum), + target: target, + } + for i := 0; i < slotNum; i++ { + c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict) + } + return c +} + +type slot[V any] struct { + n uint64 + slots []*LRU[string, V] + target Target +} + +func (c *slot[V]) index(s string) uint64 { + h := fnv.New64a() + _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&s))) + return h.Sum64() % c.n +} + +func (c *slot[V]) Get(key string, fetch func() (V, error)) (V, error) { + return c.slots[c.index(key)].Get(key, fetch) +} + +func (c *slot[V]) Del(key string) bool { + if c.slots[c.index(key)].Del(key) { + c.target.IncrDelHit() + return true + } else { + c.target.IncrDelNotFound() + return false + } +} diff --git a/pkg/common/localcache/callback.go b/pkg/localcache/local/callback.go similarity index 86% rename from pkg/common/localcache/callback.go rename to pkg/localcache/local/callback.go index 4bd37a2c2..32aef112b 100644 --- a/pkg/common/localcache/callback.go +++ b/pkg/localcache/local/callback.go @@ -1,4 +1,4 @@ -package localcache +package local import "github.com/hashicorp/golang-lru/v2/simplelru" diff --git a/pkg/common/localcache/lru.go b/pkg/localcache/local/lru.go similarity index 91% rename from pkg/common/localcache/lru.go rename to pkg/localcache/local/lru.go index 4fd1704d2..45dc3b651 100644 --- a/pkg/common/localcache/lru.go +++ b/pkg/localcache/local/lru.go @@ -1,4 +1,4 @@ -package localcache +package local import ( "github.com/hashicorp/golang-lru/v2/simplelru" @@ -30,7 +30,6 @@ func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, successTTL: successTTL, failedTTL: failedTTL, target: target, - s: NewSingleFlight[K, V](), } } @@ -40,7 +39,6 @@ type LRU[K comparable, V any] struct { successTTL time.Duration failedTTL time.Duration target Target - s *SingleFlight[K, V] } func (x *LRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { @@ -80,10 +78,5 @@ func (x *LRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() - if ok { - x.target.IncrDelHit() - } else { - x.target.IncrDelNotFound() - } return ok } diff --git a/pkg/localcache/local/lru_test.go b/pkg/localcache/local/lru_test.go new file mode 100644 index 000000000..a6e7553ee --- /dev/null +++ b/pkg/localcache/local/lru_test.go @@ -0,0 +1,95 @@ +package local + +import ( + "fmt" + "sync" + "sync/atomic" + "testing" + "time" +) + +type cacheTarget struct { + getHit int64 + getSuccess int64 + getFailed int64 + delHit int64 + delNotFound int64 +} + +func (r *cacheTarget) IncrGetHit() { + atomic.AddInt64(&r.getHit, 1) +} + +func (r *cacheTarget) IncrGetSuccess() { + atomic.AddInt64(&r.getSuccess, 1) +} + +func (r *cacheTarget) IncrGetFailed() { + atomic.AddInt64(&r.getFailed, 1) +} + +func (r *cacheTarget) IncrDelHit() { + atomic.AddInt64(&r.delHit, 1) +} + +func (r *cacheTarget) IncrDelNotFound() { + atomic.AddInt64(&r.delNotFound, 1) +} + +func (r *cacheTarget) String() string { + return fmt.Sprintf("getHit: %d, getSuccess: %d, getFailed: %d, delHit: %d, delNotFound: %d", r.getHit, r.getSuccess, r.getFailed, r.delHit, r.delNotFound) +} + +func TestName(t *testing.T) { + target := &cacheTarget{} + l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) + //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) + + fn := func(key string, n int, fetch func() (string, error)) { + for i := 0; i < n; i++ { + //v, err := l.Get(key, fetch) + //if err == nil { + // t.Log("key", key, "value", v) + //} else { + // t.Error("key", key, err) + //} + l.Get(key, fetch) + //time.Sleep(time.Second / 100) + } + } + + tmp := make(map[string]struct{}) + + var wg sync.WaitGroup + for i := 0; i < 10000; i++ { + wg.Add(1) + key := fmt.Sprintf("key_%d", i%200) + tmp[key] = struct{}{} + go func() { + defer wg.Done() + //t.Log(key) + fn(key, 10000, func() (string, error) { + //time.Sleep(time.Second * 3) + //t.Log(time.Now(), "key", key, "fetch") + //if rand.Uint32()%5 == 0 { + // return "value_" + key, nil + //} + //return "", errors.New("rand error") + return "value_" + key, nil + }) + }() + + //wg.Add(1) + //go func() { + // defer wg.Done() + // for i := 0; i < 10; i++ { + // l.Del(key) + // time.Sleep(time.Second / 3) + // } + //}() + } + wg.Wait() + t.Log(len(tmp)) + t.Log(target.String()) + +} diff --git a/pkg/localcache/local/target.go b/pkg/localcache/local/target.go new file mode 100644 index 000000000..6cb134fb0 --- /dev/null +++ b/pkg/localcache/local/target.go @@ -0,0 +1,10 @@ +package local + +type Target interface { + IncrGetHit() + IncrGetSuccess() + IncrGetFailed() + + IncrDelHit() + IncrDelNotFound() +} diff --git a/pkg/common/localcache/option.go b/pkg/localcache/option.go similarity index 97% rename from pkg/common/localcache/option.go rename to pkg/localcache/option.go index f23a04e68..9f77bc502 100644 --- a/pkg/common/localcache/option.go +++ b/pkg/localcache/option.go @@ -2,7 +2,7 @@ package localcache import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/local" + "github.com/openimsdk/localcache/local" "time" ) diff --git a/pkg/common/localcache/option/option.go b/pkg/localcache/option/option.go similarity index 100% rename from pkg/common/localcache/option/option.go rename to pkg/localcache/option/option.go diff --git a/pkg/common/localcache/tool.go b/pkg/localcache/tool.go similarity index 100% rename from pkg/common/localcache/tool.go rename to pkg/localcache/tool.go diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 181b8fd8a..5a76990d0 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -2,9 +2,9 @@ package rpccache import ( "context" + "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/redis/go-redis/v9" ) diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 82d0a03a5..6d640ea8f 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -3,10 +3,10 @@ package rpccache import ( "context" "github.com/OpenIMSDK/tools/log" + "github.com/openimsdk/localcache" + "github.com/openimsdk/localcache/option" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache/option" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/redis/go-redis/v9" ) diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 0c3bc6b93..3c3333b92 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -2,9 +2,9 @@ package rpccache import ( "context" + "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/localcache" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/redis/go-redis/v9" ) From 749eb118307c5a2dd190c1855f68cb61dfdf06ef Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Fri, 12 Jan 2024 18:23:15 +0800 Subject: [PATCH 30/66] feat: local cache --- pkg/localcache/local/lru.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pkg/localcache/local/lru.go b/pkg/localcache/local/lru.go index 45dc3b651..5ce0f1cc4 100644 --- a/pkg/localcache/local/lru.go +++ b/pkg/localcache/local/lru.go @@ -6,10 +6,32 @@ import ( "time" ) +type LRU1[K comparable, V any] interface { + Get(key K, fetch func() (V, error)) (V, error) + Del(key K) bool + Stop() +} + +//type expirableLRU[K comparable, V any] struct { +// core expirable.LRU[K, V] +//} +// +//func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { +// +// return x.core.Get(key, fetch) +//} +// +//func (x *expirableLRU[K, V]) Del(key K) bool { +// return x.core.Remove(key) +//} +// +//func (x *expirableLRU[K, V]) Stop() { +// +//} + type waitItem[V any] struct { lock sync.Mutex expires int64 - active bool err error value V } @@ -80,3 +102,7 @@ func (x *LRU[K, V]) Del(key K) bool { x.lock.Unlock() return ok } + +func (x *LRU[K, V]) Stop() { + +} From 166d51ab2335521195e5a1486b2cd1b0ee93ad57 Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:54:15 +0800 Subject: [PATCH 31/66] feat: cache add single-flight and timing-wheel. --- pkg/common/localcache/cache.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/common/localcache/cache.go b/pkg/common/localcache/cache.go index f81a1a214..da1932d06 100644 --- a/pkg/common/localcache/cache.go +++ b/pkg/common/localcache/cache.go @@ -16,7 +16,7 @@ type Cache[V any] interface { Del(ctx context.Context, key ...string) } -func New[V any](opts ...Option) Cache[V] { +func NewCache[V any](expire time.Duration, opts ...Option) Cache[V] { opt := defaultOption() for _, o := range opts { o(opt) @@ -27,6 +27,7 @@ func New[V any](opts ...Option) Cache[V] { n: uint64(opt.localSlotNum), } c.timingWheel = NewTimeWheel[string, V](TimingWheelSize, time.Second, c.exec) + go c.timingWheel.Start() for i := 0; i < opt.localSlotNum; i++ { c.slots[i] = NewLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) } @@ -39,6 +40,7 @@ func New[V any](opts ...Option) Cache[V] { type cache[V any] struct { n uint64 slots []*LRU[string, V] + expire time.Duration opt *option link link.Link timingWheel *TimeWheel[string, V] @@ -97,6 +99,10 @@ func (c *cache[V]) Del(ctx context.Context, key ...string) { c.del(key...) } } -func (c *cache[V]) exec(key string, value V) { +func (c *cache[V]) set(key string, value V) { + +} +func (c *cache[V]) exec(key string, _ V) { + c.del(key) } From 3e30e50a09aa984a563dcfde1de0c91ec689c6d3 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 11:42:18 +0800 Subject: [PATCH 32/66] feat: local cache --- pkg/localcache/local/cache.go | 6 +- pkg/localcache/local/callback.go | 4 - pkg/localcache/local/lru.go | 108 ++---------------- pkg/localcache/local/lru_expirable.go | 73 ++++++++++++ pkg/localcache/local/lru_inertia.go | 85 ++++++++++++++ .../{lru_test.go => lru_inertia_test.go} | 2 +- pkg/localcache/local/target.go | 10 -- 7 files changed, 172 insertions(+), 116 deletions(-) create mode 100644 pkg/localcache/local/lru_expirable.go create mode 100644 pkg/localcache/local/lru_inertia.go rename pkg/localcache/local/{lru_test.go => lru_inertia_test.go} (95%) delete mode 100644 pkg/localcache/local/target.go diff --git a/pkg/localcache/local/cache.go b/pkg/localcache/local/cache.go index 06569965e..e21d72425 100644 --- a/pkg/localcache/local/cache.go +++ b/pkg/localcache/local/cache.go @@ -14,18 +14,18 @@ type Cache[V any] interface { func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[string, V]) Cache[V] { c := &slot[V]{ n: uint64(slotNum), - slots: make([]*LRU[string, V], slotNum), + slots: make([]*InertiaLRU[string, V], slotNum), target: target, } for i := 0; i < slotNum; i++ { - c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict) + c.slots[i] = NewInertiaLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict) } return c } type slot[V any] struct { n uint64 - slots []*LRU[string, V] + slots []*InertiaLRU[string, V] target Target } diff --git a/pkg/localcache/local/callback.go b/pkg/localcache/local/callback.go index 32aef112b..469c3dc0d 100644 --- a/pkg/localcache/local/callback.go +++ b/pkg/localcache/local/callback.go @@ -1,5 +1 @@ package local - -import "github.com/hashicorp/golang-lru/v2/simplelru" - -type EvictCallback[K comparable, V any] simplelru.EvictCallback[K, V] diff --git a/pkg/localcache/local/lru.go b/pkg/localcache/local/lru.go index 5ce0f1cc4..9cb984b81 100644 --- a/pkg/localcache/local/lru.go +++ b/pkg/localcache/local/lru.go @@ -1,108 +1,20 @@ package local -import ( - "github.com/hashicorp/golang-lru/v2/simplelru" - "sync" - "time" -) +import "github.com/hashicorp/golang-lru/v2/simplelru" -type LRU1[K comparable, V any] interface { +type EvictCallback[K comparable, V any] simplelru.EvictCallback[K, V] + +type LRU[K comparable, V any] interface { Get(key K, fetch func() (V, error)) (V, error) Del(key K) bool Stop() } -//type expirableLRU[K comparable, V any] struct { -// core expirable.LRU[K, V] -//} -// -//func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { -// -// return x.core.Get(key, fetch) -//} -// -//func (x *expirableLRU[K, V]) Del(key K) bool { -// return x.core.Remove(key) -//} -// -//func (x *expirableLRU[K, V]) Stop() { -// -//} - -type waitItem[V any] struct { - lock sync.Mutex - expires int64 - err error - value V -} - -func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *LRU[K, V] { - var cb simplelru.EvictCallback[K, *waitItem[V]] - if onEvict != nil { - cb = func(key K, value *waitItem[V]) { - onEvict(key, value.value) - } - } - core, err := simplelru.NewLRU[K, *waitItem[V]](size, cb) - if err != nil { - panic(err) - } - return &LRU[K, V]{ - core: core, - successTTL: successTTL, - failedTTL: failedTTL, - target: target, - } -} - -type LRU[K comparable, V any] struct { - lock sync.Mutex - core *simplelru.LRU[K, *waitItem[V]] - successTTL time.Duration - failedTTL time.Duration - target Target -} - -func (x *LRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { - x.lock.Lock() - v, ok := x.core.Get(key) - if ok { - x.lock.Unlock() - v.lock.Lock() - expires, value, err := v.expires, v.value, v.err - if expires != 0 && expires > time.Now().UnixMilli() { - v.lock.Unlock() - x.target.IncrGetHit() - return value, err - } - } else { - v = &waitItem[V]{} - x.core.Add(key, v) - v.lock.Lock() - x.lock.Unlock() - } - defer v.lock.Unlock() - if v.expires > time.Now().UnixMilli() { - return v.value, v.err - } - v.value, v.err = fetch() - if v.err == nil { - v.expires = time.Now().Add(x.successTTL).UnixMilli() - x.target.IncrGetSuccess() - } else { - v.expires = time.Now().Add(x.failedTTL).UnixMilli() - x.target.IncrGetFailed() - } - return v.value, v.err -} - -func (x *LRU[K, V]) Del(key K) bool { - x.lock.Lock() - ok := x.core.Remove(key) - x.lock.Unlock() - return ok -} - -func (x *LRU[K, V]) Stop() { +type Target interface { + IncrGetHit() + IncrGetSuccess() + IncrGetFailed() + IncrDelHit() + IncrDelNotFound() } diff --git a/pkg/localcache/local/lru_expirable.go b/pkg/localcache/local/lru_expirable.go new file mode 100644 index 000000000..72acb4ffa --- /dev/null +++ b/pkg/localcache/local/lru_expirable.go @@ -0,0 +1,73 @@ +package local + +import ( + "github.com/hashicorp/golang-lru/v2/expirable" + "sync" + "time" +) + +func NewExpirableLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) LRU[K, V] { + var cb expirable.EvictCallback[K, *expirableLruItem[V]] + if onEvict != nil { + cb = func(key K, value *expirableLruItem[V]) { + onEvict(key, value.value) + } + } + core := expirable.NewLRU[K, *expirableLruItem[V]](size, cb, successTTL) + return &expirableLRU[K, V]{ + core: core, + successTTL: successTTL, + failedTTL: failedTTL, + target: target, + } +} + +type expirableLruItem[V any] struct { + lock sync.RWMutex + err error + value V +} + +type expirableLRU[K comparable, V any] struct { + lock sync.Mutex + core *expirable.LRU[K, *expirableLruItem[V]] + successTTL time.Duration + failedTTL time.Duration + target Target +} + +func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { + x.lock.Lock() + v, ok := x.core.Get(key) + if ok { + x.lock.Unlock() + x.target.IncrGetSuccess() + v.lock.RLock() + defer v.lock.RUnlock() + return v.value, v.err + } else { + v = &expirableLruItem[V]{} + x.core.Add(key, v) + v.lock.Lock() + x.lock.Unlock() + defer v.lock.Unlock() + v.value, v.err = fetch() + if v.err == nil { + x.target.IncrGetSuccess() + } else { + x.target.IncrGetFailed() + x.core.Remove(key) + } + return v.value, v.err + } +} + +func (x *expirableLRU[K, V]) Del(key K) bool { + x.lock.Lock() + ok := x.core.Remove(key) + x.lock.Unlock() + return ok +} + +func (x *expirableLRU[K, V]) Stop() { +} diff --git a/pkg/localcache/local/lru_inertia.go b/pkg/localcache/local/lru_inertia.go new file mode 100644 index 000000000..cd161e16e --- /dev/null +++ b/pkg/localcache/local/lru_inertia.go @@ -0,0 +1,85 @@ +package local + +import ( + "github.com/hashicorp/golang-lru/v2/simplelru" + "sync" + "time" +) + +type inertiaLruItem[V any] struct { + lock sync.Mutex + expires int64 + err error + value V +} + +func NewInertiaLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *InertiaLRU[K, V] { + var cb simplelru.EvictCallback[K, *inertiaLruItem[V]] + if onEvict != nil { + cb = func(key K, value *inertiaLruItem[V]) { + onEvict(key, value.value) + } + } + core, err := simplelru.NewLRU[K, *inertiaLruItem[V]](size, cb) + if err != nil { + panic(err) + } + return &InertiaLRU[K, V]{ + core: core, + successTTL: successTTL, + failedTTL: failedTTL, + target: target, + } +} + +type InertiaLRU[K comparable, V any] struct { + lock sync.Mutex + core *simplelru.LRU[K, *inertiaLruItem[V]] + successTTL time.Duration + failedTTL time.Duration + target Target +} + +func (x *InertiaLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { + x.lock.Lock() + v, ok := x.core.Get(key) + if ok { + x.lock.Unlock() + v.lock.Lock() + expires, value, err := v.expires, v.value, v.err + if expires != 0 && expires > time.Now().UnixMilli() { + v.lock.Unlock() + x.target.IncrGetHit() + return value, err + } + } else { + v = &inertiaLruItem[V]{} + x.core.Add(key, v) + v.lock.Lock() + x.lock.Unlock() + } + defer v.lock.Unlock() + if v.expires > time.Now().UnixMilli() { + return v.value, v.err + } + v.value, v.err = fetch() + if v.err == nil { + v.expires = time.Now().Add(x.successTTL).UnixMilli() + x.target.IncrGetSuccess() + } else { + v.expires = time.Now().Add(x.failedTTL).UnixMilli() + x.target.IncrGetFailed() + } + return v.value, v.err +} + +func (x *InertiaLRU[K, V]) Del(key K) bool { + x.lock.Lock() + ok := x.core.Remove(key) + x.lock.Unlock() + return ok +} + +func (x *InertiaLRU[K, V]) Stop() { + +} diff --git a/pkg/localcache/local/lru_test.go b/pkg/localcache/local/lru_inertia_test.go similarity index 95% rename from pkg/localcache/local/lru_test.go rename to pkg/localcache/local/lru_inertia_test.go index a6e7553ee..6bff90011 100644 --- a/pkg/localcache/local/lru_test.go +++ b/pkg/localcache/local/lru_inertia_test.go @@ -43,7 +43,7 @@ func (r *cacheTarget) String() string { func TestName(t *testing.T) { target := &cacheTarget{} l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) - //l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target) + //l := NewInertiaLRU[string, string](1000, time.Second*20, time.Second*5, target) fn := func(key string, n int, fetch func() (string, error)) { for i := 0; i < n; i++ { diff --git a/pkg/localcache/local/target.go b/pkg/localcache/local/target.go deleted file mode 100644 index 6cb134fb0..000000000 --- a/pkg/localcache/local/target.go +++ /dev/null @@ -1,10 +0,0 @@ -package local - -type Target interface { - IncrGetHit() - IncrGetSuccess() - IncrGetFailed() - - IncrDelHit() - IncrDelNotFound() -} From abbb701192b79a67104e73138ffdd62e3e9a4384 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 15:23:42 +0800 Subject: [PATCH 33/66] feat: local cache --- pkg/localcache/cache.go | 70 +++++++++++++------ pkg/localcache/local/cache.go | 50 ------------- pkg/localcache/local/callback.go | 1 - pkg/localcache/{local => lru}/lru.go | 2 +- .../lru_expirable.go => lru/lru_actively.go} | 31 ++++---- pkg/localcache/{local => lru}/lru_inertia.go | 7 +- .../{local => lru}/lru_inertia_test.go | 15 +++- pkg/localcache/lru/lru_slot.go | 37 ++++++++++ pkg/localcache/option.go | 20 +++++- pkg/localcache/option/option.go | 20 ------ pkg/rpccache/conversation.go | 7 +- pkg/rpccache/friend.go | 31 +++----- pkg/rpccache/group.go | 7 +- 13 files changed, 158 insertions(+), 140 deletions(-) delete mode 100644 pkg/localcache/local/cache.go delete mode 100644 pkg/localcache/local/callback.go rename pkg/localcache/{local => lru}/lru.go (96%) rename pkg/localcache/{local/lru_expirable.go => lru/lru_actively.go} (53%) rename pkg/localcache/{local => lru}/lru_inertia.go (95%) rename pkg/localcache/{local => lru}/lru_inertia_test.go (83%) create mode 100644 pkg/localcache/lru/lru_slot.go delete mode 100644 pkg/localcache/option/option.go diff --git a/pkg/localcache/cache.go b/pkg/localcache/cache.go index 2d2e8018e..ed0e16419 100644 --- a/pkg/localcache/cache.go +++ b/pkg/localcache/cache.go @@ -3,13 +3,17 @@ package localcache import ( "context" "github.com/openimsdk/localcache/link" - "github.com/openimsdk/localcache/local" - lopt "github.com/openimsdk/localcache/option" + "github.com/openimsdk/localcache/lru" + "hash/fnv" + "unsafe" ) type Cache[V any] interface { - Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*lopt.Option) (V, error) + Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error)) (V, error) + GetLink(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), link ...string) (V, error) Del(ctx context.Context, key ...string) + DelLocal(ctx context.Context, key ...string) + Stop() } func New[V any](opts ...Option) Cache[V] { @@ -19,10 +23,22 @@ func New[V any](opts ...Option) Cache[V] { } c := cache[V]{opt: opt} if opt.localSlotNum > 0 && opt.localSlotSize > 0 { - c.local = local.NewCache[V](opt.localSlotNum, opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) - go func() { - c.opt.delCh(c.del) - }() + createSimpleLRU := func() lru.LRU[string, V] { + if opt.actively { + return lru.NewActivelyLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + } else { + return lru.NewInertiaLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + } + } + if opt.localSlotNum == 1 { + c.local = createSimpleLRU() + } else { + c.local = lru.NewSlotLRU[string, V](opt.localSlotNum, func(key string) uint64 { + h := fnv.New64a() + h.Write(*(*[]byte)(unsafe.Pointer(&key))) + return h.Sum64() + }, createSimpleLRU) + } if opt.linkSlotNum > 0 { c.link = link.New(opt.linkSlotNum) } @@ -33,7 +49,7 @@ func New[V any](opts ...Option) Cache[V] { type cache[V any] struct { opt *option link link.Link - local local.Cache[V] + local lru.LRU[string, V] } func (c *cache[V]) onEvict(key string, value V) { @@ -48,22 +64,29 @@ func (c *cache[V]) onEvict(key string, value V) { } func (c *cache[V]) del(key ...string) { + if c.local == nil { + return + } for _, k := range key { - lks := c.link.Del(k) c.local.Del(k) - for k := range lks { - c.local.Del(k) + if c.link != nil { + lks := c.link.Del(k) + for k := range lks { + c.local.Del(k) + } } } } -func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), opts ...*lopt.Option) (V, error) { +func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.Context) (V, error)) (V, error) { + return c.GetLink(ctx, key, fetch) +} + +func (c *cache[V]) GetLink(ctx context.Context, key string, fetch func(ctx context.Context) (V, error), link ...string) (V, error) { if c.local != nil { return c.local.Get(key, func() (V, error) { - if c.link != nil { - for _, o := range opts { - c.link.Link(key, o.Link...) - } + if len(link) > 0 { + c.link.Link(key, link...) } return fetch(ctx) }) @@ -73,13 +96,16 @@ func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.C } func (c *cache[V]) Del(ctx context.Context, key ...string) { - if len(key) == 0 { - return - } for _, fn := range c.opt.delFn { fn(ctx, key...) } - if c.local != nil { - c.del(key...) - } + c.del(key...) +} + +func (c *cache[V]) DelLocal(ctx context.Context, key ...string) { + c.del(key...) +} + +func (c *cache[V]) Stop() { + c.local.Stop() } diff --git a/pkg/localcache/local/cache.go b/pkg/localcache/local/cache.go deleted file mode 100644 index e21d72425..000000000 --- a/pkg/localcache/local/cache.go +++ /dev/null @@ -1,50 +0,0 @@ -package local - -import ( - "hash/fnv" - "time" - "unsafe" -) - -type Cache[V any] interface { - Get(key string, fetch func() (V, error)) (V, error) - Del(key string) bool -} - -func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[string, V]) Cache[V] { - c := &slot[V]{ - n: uint64(slotNum), - slots: make([]*InertiaLRU[string, V], slotNum), - target: target, - } - for i := 0; i < slotNum; i++ { - c.slots[i] = NewInertiaLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict) - } - return c -} - -type slot[V any] struct { - n uint64 - slots []*InertiaLRU[string, V] - target Target -} - -func (c *slot[V]) index(s string) uint64 { - h := fnv.New64a() - _, _ = h.Write(*(*[]byte)(unsafe.Pointer(&s))) - return h.Sum64() % c.n -} - -func (c *slot[V]) Get(key string, fetch func() (V, error)) (V, error) { - return c.slots[c.index(key)].Get(key, fetch) -} - -func (c *slot[V]) Del(key string) bool { - if c.slots[c.index(key)].Del(key) { - c.target.IncrDelHit() - return true - } else { - c.target.IncrDelNotFound() - return false - } -} diff --git a/pkg/localcache/local/callback.go b/pkg/localcache/local/callback.go deleted file mode 100644 index 469c3dc0d..000000000 --- a/pkg/localcache/local/callback.go +++ /dev/null @@ -1 +0,0 @@ -package local diff --git a/pkg/localcache/local/lru.go b/pkg/localcache/lru/lru.go similarity index 96% rename from pkg/localcache/local/lru.go rename to pkg/localcache/lru/lru.go index 9cb984b81..2995ec028 100644 --- a/pkg/localcache/local/lru.go +++ b/pkg/localcache/lru/lru.go @@ -1,4 +1,4 @@ -package local +package lru import "github.com/hashicorp/golang-lru/v2/simplelru" diff --git a/pkg/localcache/local/lru_expirable.go b/pkg/localcache/lru/lru_actively.go similarity index 53% rename from pkg/localcache/local/lru_expirable.go rename to pkg/localcache/lru/lru_actively.go index 72acb4ffa..55fa4d0d1 100644 --- a/pkg/localcache/local/lru_expirable.go +++ b/pkg/localcache/lru/lru_actively.go @@ -1,4 +1,4 @@ -package local +package lru import ( "github.com/hashicorp/golang-lru/v2/expirable" @@ -6,15 +6,15 @@ import ( "time" ) -func NewExpirableLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) LRU[K, V] { - var cb expirable.EvictCallback[K, *expirableLruItem[V]] +func NewActivelyLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) LRU[K, V] { + var cb expirable.EvictCallback[K, *activelyLruItem[V]] if onEvict != nil { - cb = func(key K, value *expirableLruItem[V]) { + cb = func(key K, value *activelyLruItem[V]) { onEvict(key, value.value) } } - core := expirable.NewLRU[K, *expirableLruItem[V]](size, cb, successTTL) - return &expirableLRU[K, V]{ + core := expirable.NewLRU[K, *activelyLruItem[V]](size, cb, successTTL) + return &activelyLRU[K, V]{ core: core, successTTL: successTTL, failedTTL: failedTTL, @@ -22,21 +22,21 @@ func NewExpirableLRU[K comparable, V any](size int, successTTL, failedTTL time.D } } -type expirableLruItem[V any] struct { +type activelyLruItem[V any] struct { lock sync.RWMutex err error value V } -type expirableLRU[K comparable, V any] struct { +type activelyLRU[K comparable, V any] struct { lock sync.Mutex - core *expirable.LRU[K, *expirableLruItem[V]] + core *expirable.LRU[K, *activelyLruItem[V]] successTTL time.Duration failedTTL time.Duration target Target } -func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { +func (x *activelyLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { x.lock.Lock() v, ok := x.core.Get(key) if ok { @@ -46,7 +46,7 @@ func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { defer v.lock.RUnlock() return v.value, v.err } else { - v = &expirableLruItem[V]{} + v = &activelyLruItem[V]{} x.core.Add(key, v) v.lock.Lock() x.lock.Unlock() @@ -62,12 +62,17 @@ func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { } } -func (x *expirableLRU[K, V]) Del(key K) bool { +func (x *activelyLRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() + if ok { + x.target.IncrDelHit() + } else { + x.target.IncrDelNotFound() + } return ok } -func (x *expirableLRU[K, V]) Stop() { +func (x *activelyLRU[K, V]) Stop() { } diff --git a/pkg/localcache/local/lru_inertia.go b/pkg/localcache/lru/lru_inertia.go similarity index 95% rename from pkg/localcache/local/lru_inertia.go rename to pkg/localcache/lru/lru_inertia.go index cd161e16e..b1f9f24af 100644 --- a/pkg/localcache/local/lru_inertia.go +++ b/pkg/localcache/lru/lru_inertia.go @@ -1,4 +1,4 @@ -package local +package lru import ( "github.com/hashicorp/golang-lru/v2/simplelru" @@ -77,6 +77,11 @@ func (x *InertiaLRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() + if ok { + x.target.IncrDelHit() + } else { + x.target.IncrDelNotFound() + } return ok } diff --git a/pkg/localcache/local/lru_inertia_test.go b/pkg/localcache/lru/lru_inertia_test.go similarity index 83% rename from pkg/localcache/local/lru_inertia_test.go rename to pkg/localcache/lru/lru_inertia_test.go index 6bff90011..df4919d2d 100644 --- a/pkg/localcache/local/lru_inertia_test.go +++ b/pkg/localcache/lru/lru_inertia_test.go @@ -1,11 +1,13 @@ -package local +package lru import ( "fmt" + "hash/fnv" "sync" "sync/atomic" "testing" "time" + "unsafe" ) type cacheTarget struct { @@ -42,7 +44,13 @@ func (r *cacheTarget) String() string { func TestName(t *testing.T) { target := &cacheTarget{} - l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil) + l := NewSlotLRU[string, string](100, func(k string) uint64 { + h := fnv.New64a() + h.Write(*(*[]byte)(unsafe.Pointer(&k))) + return h.Sum64() + }, func() LRU[string, string] { + return NewActivelyLRU[string, string](100, time.Second*60, time.Second, target, nil) + }) //l := NewInertiaLRU[string, string](1000, time.Second*20, time.Second*5, target) fn := func(key string, n int, fetch func() (string, error)) { @@ -53,8 +61,9 @@ func TestName(t *testing.T) { //} else { // t.Error("key", key, err) //} - l.Get(key, fetch) + v, err := l.Get(key, fetch) //time.Sleep(time.Second / 100) + func(v ...any) {}(v, err) } } diff --git a/pkg/localcache/lru/lru_slot.go b/pkg/localcache/lru/lru_slot.go new file mode 100644 index 000000000..c1b8b94d0 --- /dev/null +++ b/pkg/localcache/lru/lru_slot.go @@ -0,0 +1,37 @@ +package lru + +func NewSlotLRU[K comparable, V any](slotNum int, hash func(K) uint64, create func() LRU[K, V]) LRU[K, V] { + x := &slotLRU[K, V]{ + n: uint64(slotNum), + slots: make([]LRU[K, V], slotNum), + hash: hash, + } + for i := 0; i < slotNum; i++ { + x.slots[i] = create() + } + return x +} + +type slotLRU[K comparable, V any] struct { + n uint64 + slots []LRU[K, V] + hash func(k K) uint64 +} + +func (x *slotLRU[K, V]) getIndex(k K) uint64 { + return x.hash(k) % x.n +} + +func (x *slotLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { + return x.slots[x.getIndex(key)].Get(key, fetch) +} + +func (x *slotLRU[K, V]) Del(key K) bool { + return x.slots[x.getIndex(key)].Del(key) +} + +func (x *slotLRU[K, V]) Stop() { + for _, slot := range x.slots { + slot.Stop() + } +} diff --git a/pkg/localcache/option.go b/pkg/localcache/option.go index 9f77bc502..178faf273 100644 --- a/pkg/localcache/option.go +++ b/pkg/localcache/option.go @@ -2,7 +2,7 @@ package localcache import ( "context" - "github.com/openimsdk/localcache/local" + "github.com/openimsdk/localcache/lru" "time" ) @@ -11,6 +11,7 @@ func defaultOption() *option { localSlotNum: 500, localSlotSize: 20000, linkSlotNum: 500, + actively: false, localSuccessTTL: time.Minute, localFailedTTL: time.Second * 5, delFn: make([]func(ctx context.Context, key ...string), 0, 2), @@ -22,15 +23,28 @@ type option struct { localSlotNum int localSlotSize int linkSlotNum int + actively bool localSuccessTTL time.Duration localFailedTTL time.Duration delFn []func(ctx context.Context, key ...string) delCh func(fn func(key ...string)) - target local.Target + target lru.Target } type Option func(o *option) +func WithActively() Option { + return func(o *option) { + o.actively = true + } +} + +func WithInertia() Option { + return func(o *option) { + o.actively = false + } +} + func WithLocalDisable() Option { return WithLinkSlotNum(0) } @@ -75,7 +89,7 @@ func WithLocalFailedTTL(localFailedTTL time.Duration) Option { } } -func WithTarget(target local.Target) Option { +func WithTarget(target lru.Target) Option { if target == nil { panic("target should not be nil") } diff --git a/pkg/localcache/option/option.go b/pkg/localcache/option/option.go deleted file mode 100644 index 8547b3339..000000000 --- a/pkg/localcache/option/option.go +++ /dev/null @@ -1,20 +0,0 @@ -package option - -func NewOption() *Option { - return &Option{} -} - -type Option struct { - Link []string -} - -func (o *Option) WithLink(key ...string) *Option { - if len(key) > 0 { - if len(o.Link) == 0 { - o.Link = key - } else { - o.Link = append(o.Link, key...) - } - } - return o -} diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 5a76990d0..a0aba105a 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -11,14 +11,17 @@ import ( func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis.UniversalClient) *ConversationLocalCache { return &ConversationLocalCache{ - local: localcache.New[any](localcache.WithRedisDeleteSubscribe(config.Config.LocalCache.Conversation.Topic, cli)), client: client, + local: localcache.New[any]( + localcache.WithLocalSlotNum(config.Config.LocalCache.Conversation.SlotNum), + localcache.WithLocalSlotSize(config.Config.LocalCache.Conversation.SlotSize), + ), } } type ConversationLocalCache struct { - local localcache.Cache[any] client rpcclient.ConversationRpcClient + local localcache.Cache[any] } func (c *ConversationLocalCache) GetConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) { diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 6d640ea8f..fc5f0e96c 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -4,7 +4,6 @@ import ( "context" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/localcache" - "github.com/openimsdk/localcache/option" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" @@ -13,31 +12,19 @@ import ( func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalClient) *FriendLocalCache { return &FriendLocalCache{ - local: localcache.New[any](localcache.WithRedisDeleteSubscribe(config.Config.LocalCache.Friend.Topic, cli)), client: client, + local: localcache.New[any]( + localcache.WithLocalSlotNum(config.Config.LocalCache.Friend.SlotNum), + localcache.WithLocalSlotSize(config.Config.LocalCache.Friend.SlotSize), + ), } } type FriendLocalCache struct { - local localcache.Cache[any] client rpcclient.FriendRpcClient + local localcache.Cache[any] } -//func (f *FriendLocalCache) GetFriendIDs(ctx context.Context, ownerUserID string) (val []string, err error) { -// log.ZDebug(ctx, "FriendLocalCache GetFriendIDs req", "ownerUserID", ownerUserID) -// defer func() { -// if err == nil { -// log.ZDebug(ctx, "FriendLocalCache GetFriendIDs return", "value", val) -// } else { -// log.ZError(ctx, "FriendLocalCache GetFriendIDs return", err) -// } -// }() -// return localcache.AnyValue[[]string](f.local.Get(ctx, cachekey.GetFriendIDsKey(ownerUserID), func(ctx context.Context) (any, error) { -// log.ZDebug(ctx, "FriendLocalCache GetFriendIDs call rpc", "ownerUserID", ownerUserID) -// return f.client.GetFriendIDs(ctx, ownerUserID) -// })) -//} - func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (val bool, err error) { log.ZDebug(ctx, "FriendLocalCache IsFriend req", "possibleFriendUserID", possibleFriendUserID, "userID", userID) defer func() { @@ -47,10 +34,10 @@ func (f *FriendLocalCache) IsFriend(ctx context.Context, possibleFriendUserID, u log.ZError(ctx, "FriendLocalCache IsFriend return", err) } }() - return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { + return localcache.AnyValue[bool](f.local.GetLink(ctx, cachekey.GetIsFriendKey(possibleFriendUserID, userID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "FriendLocalCache IsFriend rpc", "possibleFriendUserID", possibleFriendUserID, "userID", userID) return f.client.IsFriend(ctx, possibleFriendUserID, userID) - }, option.NewOption().WithLink(cachekey.GetFriendIDsKey(possibleFriendUserID)))) + }, cachekey.GetFriendIDsKey(possibleFriendUserID))) } // IsBlack possibleBlackUserID selfUserID @@ -63,8 +50,8 @@ func (f *FriendLocalCache) IsBlack(ctx context.Context, possibleBlackUserID, use log.ZError(ctx, "FriendLocalCache IsBlack return", err) } }() - return localcache.AnyValue[bool](f.local.Get(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { + return localcache.AnyValue[bool](f.local.GetLink(ctx, cachekey.GetIsBlackIDsKey(possibleBlackUserID, userID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "FriendLocalCache IsBlack rpc", "possibleBlackUserID", possibleBlackUserID, "userID", userID) return f.client.IsBlack(ctx, possibleBlackUserID, userID) - }, option.NewOption().WithLink(cachekey.GetBlackIDsKey(userID)))) + }, cachekey.GetBlackIDsKey(userID))) } diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 3c3333b92..d3617bfc4 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -11,14 +11,17 @@ import ( func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClient) *GroupLocalCache { return &GroupLocalCache{ - local: localcache.New[any](localcache.WithRedisDeleteSubscribe(config.Config.LocalCache.Group.Topic, cli)), client: client, + local: localcache.New[any]( + localcache.WithLocalSlotNum(config.Config.LocalCache.Group.SlotNum), + localcache.WithLocalSlotSize(config.Config.LocalCache.Group.SlotSize), + ), } } type GroupLocalCache struct { - local localcache.Cache[any] client rpcclient.GroupRpcClient + local localcache.Cache[any] } func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { From c9193302a83dccb760febba742f5946c0897ad20 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 15:53:40 +0800 Subject: [PATCH 34/66] feat: local cache --- internal/rpc/msg/verify.go | 14 ++++++------- pkg/localcache/option.go | 9 --------- pkg/rpccache/group.go | 41 +++++++++++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index e316ef88d..9f6ca727a 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -95,13 +95,13 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe data.MsgData.ContentType >= constant.NotificationBegin { return nil } - // memberIDs, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, data.MsgData.GroupID) - // if err != nil { - // return err - // } - // if !utils.IsContain(data.MsgData.SendID, memberIDs) { - // return errs.ErrNotInGroupYet.Wrap() - // } + memberIDs, err := m.GroupLocalCache.GetGroupMemberIDMap(ctx, data.MsgData.GroupID) + if err != nil { + return err + } + if _, ok := memberIDs[data.MsgData.SendID]; !ok { + return errs.ErrNotInGroupYet.Wrap() + } groupMemberInfo, err := m.Group.GetGroupMemberCache(ctx, data.MsgData.GroupID, data.MsgData.SendID) if err != nil { diff --git a/pkg/localcache/option.go b/pkg/localcache/option.go index 178faf273..40f56f2d0 100644 --- a/pkg/localcache/option.go +++ b/pkg/localcache/option.go @@ -107,15 +107,6 @@ func WithDeleteKeyBefore(fn func(ctx context.Context, key ...string)) Option { } } -func WithDeleteLocal(fn func(fn func(key ...string))) Option { - if fn == nil { - panic("fn should not be nil") - } - return func(o *option) { - o.delCh = fn - } -} - type emptyTarget struct{} func (e emptyTarget) IncrGetHit() {} diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index d3617bfc4..333966fbb 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -24,8 +24,43 @@ type GroupLocalCache struct { local localcache.Cache[any] } -func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { - return localcache.AnyValue[[]string](g.local.Get(ctx, cachekey.GetGroupMemberIDsKey(groupID), func(ctx context.Context) (any, error) { - return g.client.GetGroupMemberIDs(ctx, groupID) +type listMap[V comparable] struct { + List []V + Map map[V]struct{} +} + +func newListMap[V comparable](values []V, err error) (*listMap[V], error) { + if err != nil { + return nil, err + } + lm := &listMap[V]{ + List: values, + Map: make(map[V]struct{}, len(values)), + } + for _, value := range values { + lm.Map[value] = struct{}{} + } + return lm, nil +} + +func (g *GroupLocalCache) getGroupMemberIDs(ctx context.Context, groupID string) (*listMap[string], error) { + return localcache.AnyValue[*listMap[string]](g.local.Get(ctx, cachekey.GetGroupMemberIDsKey(groupID), func(ctx context.Context) (any, error) { + return newListMap(g.client.GetGroupMemberIDs(ctx, groupID)) })) } + +func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { + res, err := g.getGroupMemberIDs(ctx, groupID) + if err != nil { + return nil, err + } + return res.List, nil +} + +func (g *GroupLocalCache) GetGroupMemberIDMap(ctx context.Context, groupID string) (map[string]struct{}, error) { + res, err := g.getGroupMemberIDs(ctx, groupID) + if err != nil { + return nil, err + } + return res.Map, nil +} From ad5e4e15408d6ff1ccc51e02775f648a14863da8 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 16:02:34 +0800 Subject: [PATCH 35/66] feat: local cache --- pkg/rpccache/conversation.go | 9 ++++++--- pkg/rpccache/friend.go | 9 ++++++--- pkg/rpccache/group.go | 9 ++++++--- pkg/rpccache/subscriber.go | 23 +++++++++++++++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 pkg/rpccache/subscriber.go diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index a0aba105a..b6e190bb9 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -10,13 +10,16 @@ import ( ) func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis.UniversalClient) *ConversationLocalCache { - return &ConversationLocalCache{ + lc := config.Config.LocalCache.Conversation + x := &ConversationLocalCache{ client: client, local: localcache.New[any]( - localcache.WithLocalSlotNum(config.Config.LocalCache.Conversation.SlotNum), - localcache.WithLocalSlotSize(config.Config.LocalCache.Conversation.SlotSize), + localcache.WithLocalSlotNum(lc.SlotNum), + localcache.WithLocalSlotSize(lc.SlotSize), ), } + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + return x } type ConversationLocalCache struct { diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index fc5f0e96c..0e1faa78b 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -11,13 +11,16 @@ import ( ) func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalClient) *FriendLocalCache { - return &FriendLocalCache{ + lc := config.Config.LocalCache.Friend + x := &FriendLocalCache{ client: client, local: localcache.New[any]( - localcache.WithLocalSlotNum(config.Config.LocalCache.Friend.SlotNum), - localcache.WithLocalSlotSize(config.Config.LocalCache.Friend.SlotSize), + localcache.WithLocalSlotNum(lc.SlotNum), + localcache.WithLocalSlotSize(lc.SlotSize), ), } + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + return x } type FriendLocalCache struct { diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 333966fbb..6e5595a42 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -10,13 +10,16 @@ import ( ) func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClient) *GroupLocalCache { - return &GroupLocalCache{ + lc := config.Config.LocalCache.Group + x := &GroupLocalCache{ client: client, local: localcache.New[any]( - localcache.WithLocalSlotNum(config.Config.LocalCache.Group.SlotNum), - localcache.WithLocalSlotSize(config.Config.LocalCache.Group.SlotSize), + localcache.WithLocalSlotNum(lc.SlotNum), + localcache.WithLocalSlotSize(lc.SlotSize), ), } + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + return x } type GroupLocalCache struct { diff --git a/pkg/rpccache/subscriber.go b/pkg/rpccache/subscriber.go new file mode 100644 index 000000000..571ff6d2d --- /dev/null +++ b/pkg/rpccache/subscriber.go @@ -0,0 +1,23 @@ +package rpccache + +import ( + "context" + "encoding/json" + "github.com/OpenIMSDK/tools/log" + "github.com/redis/go-redis/v9" +) + +func subscriberRedisDeleteCache(ctx context.Context, client redis.UniversalClient, channel string, del func(ctx context.Context, key ...string)) { + for message := range client.Subscribe(ctx, channel).Channel() { + log.ZDebug(ctx, "subscriberRedisDeleteCache", "channel", channel, "payload", message.Payload) + var keys []string + if err := json.Unmarshal([]byte(message.Payload), &keys); err != nil { + log.ZError(ctx, "subscriberRedisDeleteCache json.Unmarshal error", err) + continue + } + if len(keys) == 0 { + continue + } + del(ctx, keys...) + } +} From 32221da613bd42b67a2e556567580cff097dc35e Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 16:27:02 +0800 Subject: [PATCH 36/66] feat: local cache --- pkg/localcache/option.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/localcache/option.go b/pkg/localcache/option.go index 40f56f2d0..17780161a 100644 --- a/pkg/localcache/option.go +++ b/pkg/localcache/option.go @@ -27,7 +27,6 @@ type option struct { localSuccessTTL time.Duration localFailedTTL time.Duration delFn []func(ctx context.Context, key ...string) - delCh func(fn func(key ...string)) target lru.Target } From 1db6e3b3897888da5f8f5e959f0d6b9c8e9c41f3 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 16:33:25 +0800 Subject: [PATCH 37/66] feat: local cache --- pkg/common/cachekey/user.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pkg/common/cachekey/user.go diff --git a/pkg/common/cachekey/user.go b/pkg/common/cachekey/user.go new file mode 100644 index 000000000..fbea5168b --- /dev/null +++ b/pkg/common/cachekey/user.go @@ -0,0 +1,14 @@ +package cachekey + +const ( + userInfoKey = "USER_INFO:" + userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:" +) + +func GetUserInfoKey(userID string) string { + return userInfoKey + userID +} + +func GetUserGlobalRecvMsgOptKey(userID string) string { + return userGlobalRecvMsgOptKey + userID +} From 656bfef069bb0206d1a53fd2c1f22f4d5af1f5af Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 16:48:21 +0800 Subject: [PATCH 38/66] feat: local cache --- pkg/rpccache/conversation.go | 12 +++++++++++- pkg/rpccache/group.go | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index b6e190bb9..e5c28f464 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -2,6 +2,7 @@ package rpccache import ( "context" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -27,8 +28,17 @@ type ConversationLocalCache struct { local localcache.Cache[any] } -func (c *ConversationLocalCache) GetConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) { +func (c *ConversationLocalCache) GetConversationIDs(ctx context.Context, ownerUserID string) (val []string, err error) { + log.ZDebug(ctx, "ConversationLocalCache GetConversationIDs req", "ownerUserID", ownerUserID) + defer func() { + if err == nil { + log.ZDebug(ctx, "ConversationLocalCache GetConversationIDs return", "value", val) + } else { + log.ZError(ctx, "ConversationLocalCache GetConversationIDs return", err) + } + }() return localcache.AnyValue[[]string](c.local.Get(ctx, cachekey.GetConversationIDsKey(ownerUserID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "ConversationLocalCache GetConversationIDs rpc", "ownerUserID", ownerUserID) return c.client.GetConversationIDs(ctx, ownerUserID) })) } diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 6e5595a42..4b9281366 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -2,6 +2,7 @@ package rpccache import ( "context" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -46,8 +47,17 @@ func newListMap[V comparable](values []V, err error) (*listMap[V], error) { return lm, nil } -func (g *GroupLocalCache) getGroupMemberIDs(ctx context.Context, groupID string) (*listMap[string], error) { +func (g *GroupLocalCache) getGroupMemberIDs(ctx context.Context, groupID string) (val *listMap[string], err error) { + log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs req", "groupID", groupID) + defer func() { + if err == nil { + log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs return", "value", val.List) + } else { + log.ZError(ctx, "GroupLocalCache getGroupMemberIDs return", err) + } + }() return localcache.AnyValue[*listMap[string]](g.local.Get(ctx, cachekey.GetGroupMemberIDsKey(groupID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs rpc", "groupID", groupID) return newListMap(g.client.GetGroupMemberIDs(ctx, groupID)) })) } From 6e1f96a7206ad08035f8c91345701372b1ef5211 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Mon, 15 Jan 2024 17:08:02 +0800 Subject: [PATCH 39/66] feat: local cache --- pkg/common/db/cache/msg.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index c1749e719..768e75a27 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -17,6 +17,7 @@ package cache import ( "context" "errors" + "github.com/dtm-labs/rockscache" "strconv" "time" @@ -128,7 +129,8 @@ type MsgModel interface { } func NewMsgCacheModel(client redis.UniversalClient) MsgModel { - return &msgCache{rdb: client} + rcClient := rockscache.NewClient(client, rockscache.NewDefaultOptions()) + return &msgCache{metaCache: NewMetaCacheRedis(rcClient), rdb: client} } type msgCache struct { From d26a1b2090cc748fcbbedbb8f9437a12be6809f9 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 10:20:57 +0800 Subject: [PATCH 40/66] feat: local cache --- pkg/rpccache/common.go | 20 ++++++++++++++++++++ pkg/rpccache/conversation.go | 4 +++- pkg/rpccache/friend.go | 4 +++- pkg/rpccache/group.go | 23 +++-------------------- 4 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 pkg/rpccache/common.go diff --git a/pkg/rpccache/common.go b/pkg/rpccache/common.go new file mode 100644 index 000000000..6dc826e30 --- /dev/null +++ b/pkg/rpccache/common.go @@ -0,0 +1,20 @@ +package rpccache + +func newListMap[V comparable](values []V, err error) (*listMap[V], error) { + if err != nil { + return nil, err + } + lm := &listMap[V]{ + List: values, + Map: make(map[V]struct{}, len(values)), + } + for _, value := range values { + lm.Map[value] = struct{}{} + } + return lm, nil +} + +type listMap[V comparable] struct { + List []V + Map map[V]struct{} +} diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index e5c28f464..45c37529b 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -19,7 +19,9 @@ func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis localcache.WithLocalSlotSize(lc.SlotSize), ), } - go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + if lc.Enable() { + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + } return x } diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index 0e1faa78b..f1dec36cc 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -19,7 +19,9 @@ func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalCl localcache.WithLocalSlotSize(lc.SlotSize), ), } - go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + if lc.Enable() { + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + } return x } diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 4b9281366..4c3e32170 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -19,7 +19,9 @@ func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClie localcache.WithLocalSlotSize(lc.SlotSize), ), } - go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + if lc.Enable() { + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + } return x } @@ -28,25 +30,6 @@ type GroupLocalCache struct { local localcache.Cache[any] } -type listMap[V comparable] struct { - List []V - Map map[V]struct{} -} - -func newListMap[V comparable](values []V, err error) (*listMap[V], error) { - if err != nil { - return nil, err - } - lm := &listMap[V]{ - List: values, - Map: make(map[V]struct{}, len(values)), - } - for _, value := range values { - lm.Map[value] = struct{}{} - } - return lm, nil -} - func (g *GroupLocalCache) getGroupMemberIDs(ctx context.Context, groupID string) (val *listMap[string], err error) { log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs req", "groupID", groupID) defer func() { From 1afdd0bcf7c708b74a7ed84cb58f636deb007ed0 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 11:15:28 +0800 Subject: [PATCH 41/66] feat: local cache --- internal/rpc/msg/send.go | 1 + internal/rpc/msg/verify.go | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index 630b74a4a..b1052b192 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -144,6 +144,7 @@ func (m *msgServer) sendMsgNotification( } func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) { + log.ZDebug(ctx, "sendMsgSingleChat return") if err := m.messageVerification(ctx, req); err != nil { return nil, err } diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 9f6ca727a..792b9c3d6 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -16,6 +16,7 @@ package msg import ( "context" + "github.com/OpenIMSDK/tools/log" "math/rand" "strconv" "time" @@ -186,7 +187,8 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt( sessionType int, pb *msg.SendMsgReq, ) (bool, error) { - opt, err := m.User.GetUserGlobalMsgRecvOpt(ctx, userID) + defer log.ZDebug(ctx, "modifyMessageByUserMessageReceiveOpt return") + opt, err := m.User.GetUserGlobalMsgRecvOpt(ctx, userID) // todo local cache if err != nil { return false, err } @@ -202,7 +204,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt( return true, nil } // conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType) - singleOpt, err := m.Conversation.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID) + singleOpt, err := m.Conversation.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID) // todo local cache if errs.ErrRecordNotFound.Is(err) { return true, nil } else if err != nil { From 1c93f87e954356b042d4cd366f0ba7677fcd2789 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 15:13:49 +0800 Subject: [PATCH 42/66] feat: local cache --- internal/rpc/msg/verify.go | 6 ++++ pkg/localcache/business.go | 71 -------------------------------------- 2 files changed, 6 insertions(+), 71 deletions(-) delete mode 100644 pkg/localcache/business.go diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 792b9c3d6..8c494beb8 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -204,7 +204,13 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt( return true, nil } // conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType) + log.ZDebug(ctx, "GetSingleConversationRecvMsgOpt req", "userID", userID, "conversationID", conversationID) singleOpt, err := m.Conversation.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID) // todo local cache + if err == nil { + log.ZDebug(ctx, "GetSingleConversationRecvMsgOpt resp", "userID", userID, "conversationID", conversationID, "RecvMsgOpt", singleOpt) + } else { + log.ZError(ctx, "GetSingleConversationRecvMsgOpt resp", err, "userID", userID, "conversationID", conversationID, "RecvMsgOpt", singleOpt) + } if errs.ErrRecordNotFound.Is(err) { return true, nil } else if err != nil { diff --git a/pkg/localcache/business.go b/pkg/localcache/business.go deleted file mode 100644 index c5260b3e2..000000000 --- a/pkg/localcache/business.go +++ /dev/null @@ -1,71 +0,0 @@ -package localcache - -// -//import ( -// "context" -// "encoding/json" -// "github.com/OpenIMSDK/tools/log" -// "github.com/dtm-labs/rockscache" -// "github.com/redis/go-redis/v9" -//) -// -//func WithRedisDeleteSubscribe(topic string, cli redis.UniversalClient) Option { -// return WithDeleteLocal(func(fn func(key ...string)) { -// if fn == nil { -// log.ZDebug(context.Background(), "WithRedisDeleteSubscribe fn is nil", "topic", topic) -// return -// } -// msg := cli.Subscribe(context.Background(), topic).Channel() -// for m := range msg { -// log.ZDebug(context.Background(), "WithRedisDeleteSubscribe delete", "topic", m.Channel, "payload", m.Payload) -// var key []string -// if err := json.Unmarshal([]byte(m.Payload), &key); err != nil { -// log.ZError(context.Background(), "WithRedisDeleteSubscribe json unmarshal error", err, "topic", topic, "payload", m.Payload) -// continue -// } -// if len(key) == 0 { -// continue -// } -// fn(key...) -// } -// }) -//} -// -//func WithRedisDeletePublish(topic string, cli redis.UniversalClient) Option { -// return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { -// data, err := json.Marshal(key) -// if err != nil { -// log.ZError(ctx, "json marshal error", err, "topic", topic, "key", key) -// return -// } -// if err := cli.Publish(ctx, topic, data).Err(); err != nil { -// log.ZError(ctx, "redis publish error", err, "topic", topic, "key", key) -// } else { -// log.ZDebug(ctx, "redis publish success", "topic", topic, "key", key) -// } -// }) -//} -// -//func WithRedisDelete(cli redis.UniversalClient) Option { -// return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { -// for _, s := range key { -// if err := cli.Del(ctx, s).Err(); err != nil { -// log.ZError(ctx, "redis delete error", err, "key", s) -// } else { -// log.ZDebug(ctx, "redis delete success", "key", s) -// } -// } -// }) -//} -// -//func WithRocksCacheDelete(cli *rockscache.Client) Option { -// return WithDeleteKeyBefore(func(ctx context.Context, key ...string) { -// for _, k := range key { -// if err := cli.TagAsDeleted2(ctx, k); err != nil { -// log.ZError(ctx, "rocksdb delete error", err, "key", k) -// } else { -// log.ZDebug(ctx, "rocksdb delete success", "key", k) -// } -// } -// }) -//} From f1506e40c469ac6e6c4d7f3dd4aa8a2c565e1dcd Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 15:53:00 +0800 Subject: [PATCH 43/66] feat: local cache --- internal/rpc/msg/verify.go | 8 +------- pkg/rpccache/conversation.go | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 8c494beb8..cd569a3ea 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -204,13 +204,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt( return true, nil } // conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType) - log.ZDebug(ctx, "GetSingleConversationRecvMsgOpt req", "userID", userID, "conversationID", conversationID) - singleOpt, err := m.Conversation.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID) // todo local cache - if err == nil { - log.ZDebug(ctx, "GetSingleConversationRecvMsgOpt resp", "userID", userID, "conversationID", conversationID, "RecvMsgOpt", singleOpt) - } else { - log.ZError(ctx, "GetSingleConversationRecvMsgOpt resp", err, "userID", userID, "conversationID", conversationID, "RecvMsgOpt", singleOpt) - } + singleOpt, err := m.ConversationLocalCache.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID) if errs.ErrRecordNotFound.Is(err) { return true, nil } else if err != nil { diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 45c37529b..8ca2f323f 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -2,6 +2,7 @@ package rpccache import ( "context" + pbconversation "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" @@ -44,3 +45,26 @@ func (c *ConversationLocalCache) GetConversationIDs(ctx context.Context, ownerUs return c.client.GetConversationIDs(ctx, ownerUserID) })) } + +func (c *ConversationLocalCache) GetConversation(ctx context.Context, userID, conversationID string) (val *pbconversation.Conversation, err error) { + log.ZDebug(ctx, "ConversationLocalCache GetConversation req", "userID", userID, "conversationID", conversationID) + defer func() { + if err == nil { + log.ZDebug(ctx, "ConversationLocalCache GetConversation return", "value", val) + } else { + log.ZError(ctx, "ConversationLocalCache GetConversation return", err) + } + }() + return localcache.AnyValue[*pbconversation.Conversation](c.local.Get(ctx, cachekey.GetConversationKey(userID, conversationID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "ConversationLocalCache GetConversation rpc", "userID", userID, "conversationID", conversationID) + return c.client.GetConversation(ctx, userID, conversationID) + })) +} + +func (c *ConversationLocalCache) GetSingleConversationRecvMsgOpt(ctx context.Context, userID, conversationID string) (int32, error) { + conv, err := c.GetConversation(ctx, userID, conversationID) + if err != nil { + return 0, err + } + return conv.RecvMsgOpt, nil +} From f53dd855aa47b91c2ce654e6732a54b9419b02f7 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 16:42:47 +0800 Subject: [PATCH 44/66] feat: local cache --- pkg/rpccache/conversation.go | 1 + pkg/rpccache/friend.go | 1 + pkg/rpccache/group.go | 1 + 3 files changed, 3 insertions(+) diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 8ca2f323f..95520f7cb 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -13,6 +13,7 @@ import ( func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis.UniversalClient) *ConversationLocalCache { lc := config.Config.LocalCache.Conversation + log.ZDebug(context.Background(), "ConversationLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable()) x := &ConversationLocalCache{ client: client, local: localcache.New[any]( diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index f1dec36cc..d9ee377b3 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -12,6 +12,7 @@ import ( func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalClient) *FriendLocalCache { lc := config.Config.LocalCache.Friend + log.ZDebug(context.Background(), "FriendLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable()) x := &FriendLocalCache{ client: client, local: localcache.New[any]( diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 4c3e32170..c3dfd9d93 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -12,6 +12,7 @@ import ( func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClient) *GroupLocalCache { lc := config.Config.LocalCache.Group + log.ZDebug(context.Background(), "GroupLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable()) x := &GroupLocalCache{ client: client, local: localcache.New[any]( From 9e4a7d082d2e3b06beb450417a3f213ec980da14 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 17:01:38 +0800 Subject: [PATCH 45/66] feat: local cache --- pkg/common/db/cache/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/common/db/cache/config.go b/pkg/common/db/cache/config.go index dc5c55143..7fd08e247 100644 --- a/pkg/common/db/cache/config.go +++ b/pkg/common/db/cache/config.go @@ -31,7 +31,7 @@ func getPublishKey(topic string, key []string) []string { }, { Local: config.Config.LocalCache.Conversation, - Keys: []string{cachekey.ConversationIDsKey}, + Keys: []string{cachekey.ConversationIDsKey, cachekey.ConversationKey}, }, } subscribe = make(map[string][]string) From 4a042489bfc3f5eff53652ea1e6d8d7cb592fad8 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 17:19:29 +0800 Subject: [PATCH 46/66] feat: local cache --- internal/push/push_rpc_server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 8a9da0577..92a6c4b8f 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -51,8 +51,8 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e client, offlinePusher, database, - rpccache.NewGroupLocalCache(rpcclient.NewGroupRpcClient(client), rdb), - rpccache.NewConversationLocalCache(rpcclient.NewConversationRpcClient(client), rdb), + rpccache.NewGroupLocalCache(groupRpcClient, rdb), + rpccache.NewConversationLocalCache(conversationRpcClient, rdb), &conversationRpcClient, &groupRpcClient, &msgRpcClient, From af5e191144440f88adff61cfaef09d32d83bb674 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 17:46:28 +0800 Subject: [PATCH 47/66] feat: local cache --- pkg/localcache/cache_test.go | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 pkg/localcache/cache_test.go diff --git a/pkg/localcache/cache_test.go b/pkg/localcache/cache_test.go new file mode 100644 index 000000000..90413fd20 --- /dev/null +++ b/pkg/localcache/cache_test.go @@ -0,0 +1,79 @@ +package localcache + +import ( + "context" + "fmt" + "math/rand" + "sync" + "sync/atomic" + "testing" + "time" +) + +func TestName(t *testing.T) { + c := New[string](WithActively()) + //c := New[string]() + ctx := context.Background() + + const ( + num = 10000 + tNum = 10000 + kNum = 100000 + pNum = 100 + ) + + getKey := func(v uint64) string { + return fmt.Sprintf("key_%d", v%kNum) + } + + start := time.Now() + t.Log("start", start) + + var ( + get atomic.Int64 + del atomic.Int64 + ) + + incrGet := func() { + if v := get.Add(1); v%pNum == 0 { + //t.Log("#get count", v/pNum) + } + } + incrDel := func() { + if v := del.Add(1); v%pNum == 0 { + //t.Log("@del count", v/pNum) + } + } + + var wg sync.WaitGroup + + for i := 0; i < tNum; i++ { + wg.Add(2) + go func() { + defer wg.Done() + for i := 0; i < num; i++ { + c.Get(ctx, getKey(rand.Uint64()), func(ctx context.Context) (string, error) { + return fmt.Sprintf("index_%d", i), nil + }) + incrGet() + } + }() + + go func() { + defer wg.Done() + time.Sleep(time.Second / 10) + for i := 0; i < num; i++ { + c.Del(ctx, getKey(rand.Uint64())) + incrDel() + } + }() + } + + wg.Wait() + end := time.Now() + t.Log("end", end) + t.Log("time", end.Sub(start)) + t.Log("get", get.Load()) + t.Log("del", del.Load()) + // 137.35s +} From 025eb441135057acd8a8391cdfc55ddadc94150a Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 16 Jan 2024 17:52:33 +0800 Subject: [PATCH 48/66] feat: local cache --- internal/rpc/msg/server.go | 9 +++++---- internal/rpc/msg/verify.go | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 08786b43b..798ba0ee6 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -39,7 +39,7 @@ type ( Group *rpcclient.GroupRpcClient User *rpcclient.UserRpcClient Conversation *rpcclient.ConversationRpcClient - friend *rpccache.FriendLocalCache + FriendLocalCache *rpccache.FriendLocalCache GroupLocalCache *rpccache.GroupLocalCache ConversationLocalCache *rpccache.ConversationLocalCache Handlers MessageInterceptorChain @@ -79,6 +79,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e conversationClient := rpcclient.NewConversationRpcClient(client) userRpcClient := rpcclient.NewUserRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) + friendRpcClient := rpcclient.NewFriendRpcClient(client) msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) s := &msgServer{ @@ -87,9 +88,9 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e Group: &groupRpcClient, MsgDatabase: msgDatabase, RegisterCenter: client, - GroupLocalCache: rpccache.NewGroupLocalCache(rpcclient.NewGroupRpcClient(client), rdb), - ConversationLocalCache: rpccache.NewConversationLocalCache(rpcclient.NewConversationRpcClient(client), rdb), - friend: rpccache.NewFriendLocalCache(rpcclient.NewFriendRpcClient(client), rdb), + GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, rdb), + ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, rdb), + FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, rdb), } s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg)) s.addInterceptorHandler(MessageHasReadEnabled) diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index cd569a3ea..0af56aaa2 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -59,7 +59,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe data.MsgData.ContentType >= constant.NotificationBegin { return nil } - black, err := m.friend.IsBlack(ctx, data.MsgData.SendID, data.MsgData.RecvID) + black, err := m.FriendLocalCache.IsBlack(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { return err } @@ -67,7 +67,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe return errs.ErrBlockedByPeer.Wrap() } if *config.Config.MessageVerify.FriendVerify { - friend, err := m.friend.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID) + friend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { return err } From c16f17582a7d6735895306cebcf7a012c371d68e Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 17 Jan 2024 16:37:26 +0800 Subject: [PATCH 49/66] feat: msg rpc local cache --- deployments/templates/openim.yaml | 9 ++- internal/rpc/msg/as_read.go | 6 +- internal/rpc/msg/revoke.go | 9 +-- internal/rpc/msg/send.go | 2 +- internal/rpc/msg/server.go | 6 +- internal/rpc/msg/statistics.go | 4 +- internal/rpc/msg/sync_msg.go | 8 +-- internal/rpc/msg/verify.go | 8 +-- pkg/common/cachekey/user.go | 8 +-- pkg/common/config/config.go | 3 +- pkg/common/db/cache/black.go | 5 +- pkg/common/db/cache/config.go | 6 +- pkg/common/db/cache/conversation.go | 5 +- pkg/common/db/cache/friend.go | 2 +- pkg/common/db/cache/group.go | 4 +- pkg/common/db/cache/user.go | 9 ++- pkg/rpccache/conversation.go | 16 +++++ pkg/rpccache/group.go | 77 +++++++++++++++++++++++ pkg/rpccache/user.go | 94 +++++++++++++++++++++++++++++ 19 files changed, 242 insertions(+), 39 deletions(-) create mode 100644 pkg/rpccache/user.go diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 3a416a806..b7548ffce 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -529,8 +529,8 @@ prometheus: messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports localCache: - friend: - topic: delete_cache_friend + user: + topic: delete_cache_user slotNum: 500 slotSize: 20000 @@ -539,6 +539,11 @@ localCache: slotNum: 500 slotSize: 20000 + friend: + topic: delete_cache_friend + slotNum: 500 + slotSize: 20000 + conversation: topic: delete_cache_conversation slotNum: 500 diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index 71e038b39..a2bcb84bf 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -43,7 +43,7 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m if err != nil { return nil, err } - conversations, err := m.Conversation.GetConversations(ctx, req.UserID, conversationIDs) + conversations, err := m.ConversationLocalCache.GetConversations(ctx, req.UserID, conversationIDs) if err != nil { return nil, err } @@ -106,7 +106,7 @@ func (m *msgServer) MarkMsgsAsRead( if hasReadSeq > maxSeq { return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq") } - conversation, err := m.Conversation.GetConversation(ctx, req.UserID, req.ConversationID) + conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID) if err != nil { return } @@ -135,7 +135,7 @@ func (m *msgServer) MarkConversationAsRead( ctx context.Context, req *msg.MarkConversationAsReadReq, ) (resp *msg.MarkConversationAsReadResp, err error) { - conversation, err := m.Conversation.GetConversation(ctx, req.UserID, req.ConversationID) + conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID) if err != nil { return nil, err } diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index d7362d339..8640524ec 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -47,7 +47,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { return nil, err } - user, err := m.User.GetUserInfo(ctx, req.UserID) + user, err := m.UserLocalCache.GetUserInfo(ctx, req.UserID) if err != nil { return nil, err } @@ -73,12 +73,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. } role = user.AppMangerLevel case constant.SuperGroupChatType: - members, err := m.Group.GetGroupMemberInfoMap( - ctx, - msgs[0].GroupID, - utils.Distinct([]string{req.UserID, msgs[0].SendID}), - true, - ) + members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, utils.Distinct([]string{req.UserID, msgs[0].SendID})) if err != nil { return nil, err } diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index b1052b192..20e3f85f1 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -98,7 +98,7 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa } tagAll := utils.IsContain(constant.AtAllString, msg.AtUserIDList) if tagAll { - memberUserIDList, err := m.Group.GetGroupMemberIDs(ctx, msg.GroupID) + memberUserIDList, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, msg.GroupID) if err != nil { log.ZWarn(ctx, "GetGroupMemberIDs", err) return diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 798ba0ee6..e1593443d 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -36,9 +36,8 @@ type ( msgServer struct { RegisterCenter discoveryregistry.SvcDiscoveryRegistry MsgDatabase controller.CommonMsgDatabase - Group *rpcclient.GroupRpcClient - User *rpcclient.UserRpcClient Conversation *rpcclient.ConversationRpcClient + UserLocalCache *rpccache.UserLocalCache FriendLocalCache *rpccache.FriendLocalCache GroupLocalCache *rpccache.GroupLocalCache ConversationLocalCache *rpccache.ConversationLocalCache @@ -84,10 +83,9 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e s := &msgServer{ Conversation: &conversationClient, - User: &userRpcClient, - Group: &groupRpcClient, MsgDatabase: msgDatabase, RegisterCenter: client, + UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, rdb), GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, rdb), ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, rdb), FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, rdb), diff --git a/internal/rpc/msg/statistics.go b/internal/rpc/msg/statistics.go index ac09e3f69..620e6c7b0 100644 --- a/internal/rpc/msg/statistics.go +++ b/internal/rpc/msg/statistics.go @@ -41,7 +41,7 @@ func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq var pbUsers []*msg.ActiveUser if len(users) > 0 { userIDs := utils.Slice(users, func(e *unrelation.UserCount) string { return e.UserID }) - userMap, err := m.User.GetUsersInfoMap(ctx, userIDs) + userMap, err := m.UserLocalCache.GetUsersInfoMap(ctx, userIDs) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (m *msgServer) GetActiveGroup(ctx context.Context, req *msg.GetActiveGroupR var pbgroups []*msg.ActiveGroup if len(groups) > 0 { groupIDs := utils.Slice(groups, func(e *unrelation.GroupCount) string { return e.GroupID }) - resp, err := m.Group.GetGroupInfos(ctx, groupIDs, false) + resp, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs) if err != nil { return nil, err } diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index dbd8da4d8..404ca6218 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -37,7 +37,7 @@ func (m *msgServer) PullMessageBySeqs( resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs) for _, seq := range req.SeqRanges { if !msgprocessor.IsNotification(seq.ConversationID) { - conversation, err := m.Conversation.GetConversation(ctx, req.UserID, seq.ConversationID) + conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, seq.ConversationID) if err != nil { log.ZError(ctx, "GetConversation error", err, "conversationID", seq.ConversationID) continue @@ -140,7 +140,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq } } if len(sendIDs) != 0 { - sendInfos, err := m.User.GetUsersInfo(ctx, sendIDs) + sendInfos, err := m.UserLocalCache.GetUsersInfo(ctx, sendIDs) if err != nil { return nil, err } @@ -149,7 +149,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq } } if len(recvIDs) != 0 { - recvInfos, err := m.User.GetUsersInfo(ctx, recvIDs) + recvInfos, err := m.UserLocalCache.GetUsersInfo(ctx, recvIDs) if err != nil { return nil, err } @@ -158,7 +158,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq } } if len(groupIDs) != 0 { - groupInfos, err := m.Group.GetGroupInfos(ctx, groupIDs, true) + groupInfos, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs) if err != nil { return nil, err } diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 0af56aaa2..50b7718ce 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -78,7 +78,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe } return nil case constant.SuperGroupChatType: - groupInfo, err := m.Group.GetGroupInfoCache(ctx, data.MsgData.GroupID) + groupInfo, err := m.GroupLocalCache.GetGroupInfo(ctx, data.MsgData.GroupID) if err != nil { return err } @@ -104,9 +104,9 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe return errs.ErrNotInGroupYet.Wrap() } - groupMemberInfo, err := m.Group.GetGroupMemberCache(ctx, data.MsgData.GroupID, data.MsgData.SendID) + groupMemberInfo, err := m.GroupLocalCache.GetGroupMember(ctx, data.MsgData.GroupID, data.MsgData.SendID) if err != nil { - if err == errs.ErrRecordNotFound { + if errs.ErrRecordNotFound.Is(err) { return errs.ErrNotInGroupYet.Wrap(err.Error()) } return err @@ -188,7 +188,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt( pb *msg.SendMsgReq, ) (bool, error) { defer log.ZDebug(ctx, "modifyMessageByUserMessageReceiveOpt return") - opt, err := m.User.GetUserGlobalMsgRecvOpt(ctx, userID) // todo local cache + opt, err := m.UserLocalCache.GetUserGlobalMsgRecvOpt(ctx, userID) if err != nil { return false, err } diff --git a/pkg/common/cachekey/user.go b/pkg/common/cachekey/user.go index fbea5168b..3fb877e22 100644 --- a/pkg/common/cachekey/user.go +++ b/pkg/common/cachekey/user.go @@ -1,14 +1,14 @@ package cachekey const ( - userInfoKey = "USER_INFO:" - userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:" + UserInfoKey = "USER_INFO:" + UserGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:" ) func GetUserInfoKey(userID string) string { - return userInfoKey + userID + return UserInfoKey + userID } func GetUserGlobalRecvMsgOptKey(userID string) string { - return userGlobalRecvMsgOptKey + userID + return UserGlobalRecvMsgOptKey + userID } diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index b06209c16..3cc1f4e6e 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -383,8 +383,9 @@ func (l LocalCache) Enable() bool { } type localCache struct { - Friend LocalCache `yaml:"friend"` + User LocalCache `yaml:"user"` Group LocalCache `yaml:"group"` + Friend LocalCache `yaml:"friend"` Conversation LocalCache `yaml:"conversation"` } diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index 716c9eef0..8328306ff 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -16,6 +16,7 @@ package cache import ( "context" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "time" @@ -55,7 +56,9 @@ func NewBlackCacheRedis( ) BlackCache { rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) - mc.SetTopic(config.Config.LocalCache.Friend.Topic) + b := config.Config.LocalCache.Friend + log.ZDebug(context.Background(), "black local cache init", "Topic", b.Topic, "SlotNum", b.SlotNum, "SlotSize", b.SlotSize, "enable", b.Enable()) + mc.SetTopic(b.Topic) mc.SetRawRedisClient(rdb) return &BlackCacheRedis{ expireTime: blackExpireTime, diff --git a/pkg/common/db/cache/config.go b/pkg/common/db/cache/config.go index 7fd08e247..52ece95f7 100644 --- a/pkg/common/db/cache/config.go +++ b/pkg/common/db/cache/config.go @@ -21,9 +21,13 @@ func getPublishKey(topic string, key []string) []string { Local config.LocalCache Keys []string }{ + { + Local: config.Config.LocalCache.User, + Keys: []string{cachekey.UserInfoKey, cachekey.UserGlobalRecvMsgOptKey}, + }, { Local: config.Config.LocalCache.Group, - Keys: []string{cachekey.GroupMemberIDsKey}, + Keys: []string{cachekey.GroupMemberIDsKey, cachekey.GroupInfoKey, cachekey.GroupMemberInfoKey}, }, { Local: config.Config.LocalCache.Friend, diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index 9c0391e67..61489ff92 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -17,6 +17,7 @@ package cache import ( "context" "errors" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "math/big" @@ -87,7 +88,9 @@ type ConversationCache interface { func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationtb.ConversationModelInterface) ConversationCache { rcClient := rockscache.NewClient(rdb, opts) mc := NewMetaCacheRedis(rcClient) - mc.SetTopic(config.Config.LocalCache.Conversation.Topic) + c := config.Config.LocalCache.Conversation + log.ZDebug(context.Background(), "black local cache init", "Topic", c.Topic, "SlotNum", c.SlotNum, "SlotSize", c.SlotSize, "enable", c.Enable()) + mc.SetTopic(c.Topic) mc.SetRawRedisClient(rdb) return &ConversationRedisCache{ rcClient: rcClient, diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index 432d08572..d09d00312 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -65,7 +65,7 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendMo rcClient := rockscache.NewClient(rdb, options) mc := NewMetaCacheRedis(rcClient) f := config.Config.LocalCache.Friend - log.ZDebug(context.Background(), "friend local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize) + log.ZDebug(context.Background(), "friend local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize, "enable", f.Enable()) mc.SetTopic(f.Topic) mc.SetRawRedisClient(rdb) return &FriendCacheRedis{ diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index 783f2e515..71f5d06fd 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -106,7 +106,9 @@ func NewGroupCacheRedis( ) GroupCache { rcClient := rockscache.NewClient(rdb, opts) mc := NewMetaCacheRedis(rcClient) - mc.SetTopic(config.Config.LocalCache.Group.Topic) + g := config.Config.LocalCache.Group + mc.SetTopic(g.Topic) + log.ZDebug(context.Background(), "group local cache init", "Topic", g.Topic, "SlotNum", g.SlotNum, "SlotSize", g.SlotSize, "enable", g.Enable()) mc.SetRawRedisClient(rdb) return &GroupCacheRedis{ rcClient: rcClient, expireTime: groupExpireTime, diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index 14ed7988e..c18f2af25 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -19,6 +19,7 @@ import ( "encoding/json" "errors" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "hash/crc32" "strconv" "time" @@ -73,7 +74,11 @@ func NewUserCacheRedis( options rockscache.Options, ) UserCache { rcClient := rockscache.NewClient(rdb, options) - + mc := NewMetaCacheRedis(rcClient) + u := config.Config.LocalCache.User + log.ZDebug(context.Background(), "user local cache init", "Topic", u.Topic, "SlotNum", u.SlotNum, "SlotSize", u.SlotSize, "enable", u.Enable()) + mc.SetTopic(u.Topic) + mc.SetRawRedisClient(rdb) return &UserCacheRedis{ rdb: rdb, metaCache: NewMetaCacheRedis(rcClient), @@ -86,7 +91,7 @@ func NewUserCacheRedis( func (u *UserCacheRedis) NewCache() UserCache { return &UserCacheRedis{ rdb: u.rdb, - metaCache: NewMetaCacheRedis(u.rcClient, u.metaCache.GetPreDelKeys()...), + metaCache: u.Copy(), userDB: u.userDB, expireTime: u.expireTime, rcClient: u.rcClient, diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 95520f7cb..061b05bec 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -3,6 +3,7 @@ package rpccache import ( "context" pbconversation "github.com/OpenIMSDK/protocol/conversation" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" @@ -69,3 +70,18 @@ func (c *ConversationLocalCache) GetSingleConversationRecvMsgOpt(ctx context.Con } return conv.RecvMsgOpt, nil } + +func (c *ConversationLocalCache) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*pbconversation.Conversation, error) { + conversations := make([]*pbconversation.Conversation, 0, len(conversationIDs)) + for _, conversationID := range conversationIDs { + conversation, err := c.GetConversation(ctx, ownerUserID, conversationID) + if err != nil { + if errs.ErrRecordNotFound.Is(err) { + continue + } + return nil, err + } + conversations = append(conversations, conversation) + } + return conversations, nil +} diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index c3dfd9d93..4b4720539 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -2,6 +2,8 @@ package rpccache import ( "context" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" @@ -61,3 +63,78 @@ func (g *GroupLocalCache) GetGroupMemberIDMap(ctx context.Context, groupID strin } return res.Map, nil } + +func (g *GroupLocalCache) GetGroupInfo(ctx context.Context, groupID string) (val *sdkws.GroupInfo, err error) { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo req", "groupID", groupID) + defer func() { + if err == nil { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo return", "value", val) + } else { + log.ZError(ctx, "GroupLocalCache GetGroupInfo return", err) + } + }() + return localcache.AnyValue[*sdkws.GroupInfo](g.local.Get(ctx, cachekey.GetGroupMemberIDsKey(groupID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo rpc", "groupID", groupID) + return g.client.GetGroupInfoCache(ctx, groupID) + })) +} + +func (g *GroupLocalCache) GetGroupInfos(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) { + groupInfos := make([]*sdkws.GroupInfo, 0, len(groupIDs)) + for _, groupID := range groupIDs { + groupInfo, err := g.GetGroupInfo(ctx, groupID) + if err != nil { + if errs.ErrRecordNotFound.Is(err) { + continue + } + return nil, err + } + groupInfos = append(groupInfos, groupInfo) + } + return groupInfos, nil +} + +func (g *GroupLocalCache) GetGroupMember(ctx context.Context, groupID, userID string) (val *sdkws.GroupMemberFullInfo, err error) { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo req", "groupID", groupID, "userID", userID) + defer func() { + if err == nil { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo return", "value", val) + } else { + log.ZError(ctx, "GroupLocalCache GetGroupInfo return", err) + } + }() + return localcache.AnyValue[*sdkws.GroupMemberFullInfo](g.local.Get(ctx, cachekey.GetGroupMemberInfoKey(groupID, userID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo rpc", "groupID", groupID, "userID", userID) + return g.client.GetGroupMemberCache(ctx, groupID, userID) + })) +} + +func (g *GroupLocalCache) GetGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) { + members := make([]*sdkws.GroupMemberFullInfo, 0, len(userIDs)) + for _, userID := range userIDs { + member, err := g.GetGroupMember(ctx, groupID, userID) + if err != nil { + if errs.ErrRecordNotFound.Is(err) { + continue + } + return nil, err + } + members = append(members, member) + } + return members, nil +} + +func (g *GroupLocalCache) GetGroupMemberInfoMap(ctx context.Context, groupID string, userIDs []string) (map[string]*sdkws.GroupMemberFullInfo, error) { + members := make(map[string]*sdkws.GroupMemberFullInfo) + for _, userID := range userIDs { + member, err := g.GetGroupMember(ctx, groupID, userID) + if err != nil { + if errs.ErrRecordNotFound.Is(err) { + continue + } + return nil, err + } + members[userID] = member + } + return members, nil +} diff --git a/pkg/rpccache/user.go b/pkg/rpccache/user.go new file mode 100644 index 000000000..fd4c24c75 --- /dev/null +++ b/pkg/rpccache/user.go @@ -0,0 +1,94 @@ +package rpccache + +import ( + "context" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" + "github.com/openimsdk/localcache" + "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" +) + +func NewUserLocalCache(client rpcclient.UserRpcClient, cli redis.UniversalClient) *UserLocalCache { + lc := config.Config.LocalCache.User + log.ZDebug(context.Background(), "UserLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable()) + x := &UserLocalCache{ + client: client, + local: localcache.New[any]( + localcache.WithLocalSlotNum(lc.SlotNum), + localcache.WithLocalSlotSize(lc.SlotSize), + ), + } + if lc.Enable() { + go subscriberRedisDeleteCache(context.Background(), cli, lc.Topic, x.local.DelLocal) + } + return x +} + +type UserLocalCache struct { + client rpcclient.UserRpcClient + local localcache.Cache[any] +} + +func (u *UserLocalCache) GetUserInfo(ctx context.Context, userID string) (val *sdkws.UserInfo, err error) { + log.ZDebug(ctx, "UserLocalCache GetUserInfo req", "userID", userID) + defer func() { + if err == nil { + log.ZDebug(ctx, "UserLocalCache GetUserInfo return", "value", val) + } else { + log.ZError(ctx, "UserLocalCache GetUserInfo return", err) + } + }() + return localcache.AnyValue[*sdkws.UserInfo](u.local.Get(ctx, cachekey.GetUserInfoKey(userID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "UserLocalCache GetUserInfo rpc", "userID", userID) + return u.client.GetUserInfo(ctx, userID) + })) +} + +func (u *UserLocalCache) GetUserGlobalMsgRecvOpt(ctx context.Context, userID string) (val int32, err error) { + log.ZDebug(ctx, "UserLocalCache GetUserGlobalMsgRecvOpt req", "userID", userID) + defer func() { + if err == nil { + log.ZDebug(ctx, "UserLocalCache GetUserGlobalMsgRecvOpt return", "value", val) + } else { + log.ZError(ctx, "UserLocalCache GetUserGlobalMsgRecvOpt return", err) + } + }() + return localcache.AnyValue[int32](u.local.Get(ctx, cachekey.GetUserGlobalRecvMsgOptKey(userID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "UserLocalCache GetUserGlobalMsgRecvOpt rpc", "userID", userID) + return u.client.GetUserGlobalMsgRecvOpt(ctx, userID) + })) +} + +func (u *UserLocalCache) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) { + users := make([]*sdkws.UserInfo, 0, len(userIDs)) + for _, userID := range userIDs { + user, err := u.GetUserInfo(ctx, userID) + if err != nil { + if errs.ErrRecordNotFound.Is(err) { + continue + } + return nil, err + } + users = append(users, user) + } + return users, nil +} + +func (u *UserLocalCache) GetUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) { + users := make(map[string]*sdkws.UserInfo, len(userIDs)) + for _, userID := range userIDs { + user, err := u.GetUserInfo(ctx, userID) + if err != nil { + if errs.ErrRecordNotFound.Is(err) { + continue + } + return nil, err + } + users[userID] = user + } + return users, nil +} From 77894b104e8ee8e2ffe43eb68c829939130c19b7 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 17 Jan 2024 17:26:46 +0800 Subject: [PATCH 50/66] feat: msg rpc local cache --- pkg/rpccache/group.go | 2 +- pkg/rpcclient/group.go | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 4b4720539..333177d2d 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -37,7 +37,7 @@ func (g *GroupLocalCache) getGroupMemberIDs(ctx context.Context, groupID string) log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs req", "groupID", groupID) defer func() { if err == nil { - log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs return", "value", val.List) + log.ZDebug(ctx, "GroupLocalCache getGroupMemberIDs return", "value", val) } else { log.ZError(ctx, "GroupLocalCache getGroupMemberIDs return", err) } diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index bf0efe60c..98c8387e5 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -18,8 +18,6 @@ import ( "context" "strings" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/protocol/sdkws" @@ -31,9 +29,7 @@ import ( ) type Group struct { - conn grpc.ClientConnInterface Client group.GroupClient - discov discoveryregistry.SvcDiscoveryRegistry } func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry) *Group { @@ -42,7 +38,7 @@ func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry) *Group { panic(err) } client := group.NewGroupClient(conn) - return &Group{discov: discov, conn: conn, Client: client} + return &Group{Client: client} } type GroupRpcClient Group From e26a582836654b57d6665a2d2358710dea0ecbd8 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 17 Jan 2024 17:33:54 +0800 Subject: [PATCH 51/66] feat: msg rpc local cache --- pkg/rpccache/group.go | 60 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 333177d2d..6a12aa9f0 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -48,20 +48,19 @@ func (g *GroupLocalCache) getGroupMemberIDs(ctx context.Context, groupID string) })) } -func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { - res, err := g.getGroupMemberIDs(ctx, groupID) - if err != nil { - return nil, err - } - return res.List, nil -} - -func (g *GroupLocalCache) GetGroupMemberIDMap(ctx context.Context, groupID string) (map[string]struct{}, error) { - res, err := g.getGroupMemberIDs(ctx, groupID) - if err != nil { - return nil, err - } - return res.Map, nil +func (g *GroupLocalCache) GetGroupMember(ctx context.Context, groupID, userID string) (val *sdkws.GroupMemberFullInfo, err error) { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo req", "groupID", groupID, "userID", userID) + defer func() { + if err == nil { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo return", "value", val) + } else { + log.ZError(ctx, "GroupLocalCache GetGroupInfo return", err) + } + }() + return localcache.AnyValue[*sdkws.GroupMemberFullInfo](g.local.Get(ctx, cachekey.GetGroupMemberInfoKey(groupID, userID), func(ctx context.Context) (any, error) { + log.ZDebug(ctx, "GroupLocalCache GetGroupInfo rpc", "groupID", groupID, "userID", userID) + return g.client.GetGroupMemberCache(ctx, groupID, userID) + })) } func (g *GroupLocalCache) GetGroupInfo(ctx context.Context, groupID string) (val *sdkws.GroupInfo, err error) { @@ -73,12 +72,28 @@ func (g *GroupLocalCache) GetGroupInfo(ctx context.Context, groupID string) (val log.ZError(ctx, "GroupLocalCache GetGroupInfo return", err) } }() - return localcache.AnyValue[*sdkws.GroupInfo](g.local.Get(ctx, cachekey.GetGroupMemberIDsKey(groupID), func(ctx context.Context) (any, error) { + return localcache.AnyValue[*sdkws.GroupInfo](g.local.Get(ctx, cachekey.GetGroupInfoKey(groupID), func(ctx context.Context) (any, error) { log.ZDebug(ctx, "GroupLocalCache GetGroupInfo rpc", "groupID", groupID) return g.client.GetGroupInfoCache(ctx, groupID) })) } +func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) { + res, err := g.getGroupMemberIDs(ctx, groupID) + if err != nil { + return nil, err + } + return res.List, nil +} + +func (g *GroupLocalCache) GetGroupMemberIDMap(ctx context.Context, groupID string) (map[string]struct{}, error) { + res, err := g.getGroupMemberIDs(ctx, groupID) + if err != nil { + return nil, err + } + return res.Map, nil +} + func (g *GroupLocalCache) GetGroupInfos(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) { groupInfos := make([]*sdkws.GroupInfo, 0, len(groupIDs)) for _, groupID := range groupIDs { @@ -94,21 +109,6 @@ func (g *GroupLocalCache) GetGroupInfos(ctx context.Context, groupIDs []string) return groupInfos, nil } -func (g *GroupLocalCache) GetGroupMember(ctx context.Context, groupID, userID string) (val *sdkws.GroupMemberFullInfo, err error) { - log.ZDebug(ctx, "GroupLocalCache GetGroupInfo req", "groupID", groupID, "userID", userID) - defer func() { - if err == nil { - log.ZDebug(ctx, "GroupLocalCache GetGroupInfo return", "value", val) - } else { - log.ZError(ctx, "GroupLocalCache GetGroupInfo return", err) - } - }() - return localcache.AnyValue[*sdkws.GroupMemberFullInfo](g.local.Get(ctx, cachekey.GetGroupMemberInfoKey(groupID, userID), func(ctx context.Context) (any, error) { - log.ZDebug(ctx, "GroupLocalCache GetGroupInfo rpc", "groupID", groupID, "userID", userID) - return g.client.GetGroupMemberCache(ctx, groupID, userID) - })) -} - func (g *GroupLocalCache) GetGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) { members := make([]*sdkws.GroupMemberFullInfo, 0, len(userIDs)) for _, userID := range userIDs { From 899f1f15a4a1eeda57830b650e8776c330c213d9 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Thu, 18 Jan 2024 15:06:07 +0800 Subject: [PATCH 52/66] feat: msg rpc local cache --- deployments/templates/openim.yaml | 25 ++++++++++++++++++++----- pkg/common/config/config.go | 17 ++++++++++++++--- pkg/rpccache/conversation.go | 3 +++ pkg/rpccache/friend.go | 3 +++ pkg/rpccache/group.go | 3 +++ pkg/rpccache/user.go | 3 +++ 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index b7548ffce..8e7e05904 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -528,23 +528,38 @@ prometheus: thirdPrometheusPort: [ ${THIRD_PROM_PORT} ] messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports +###################### LocalCache configuration information ###################### +# topic: redis subscriber channel +# slotNum: number of slots, multiple slots can prevent too many keys from competing for a lock +# slotSize: number of slots, the number of cached keys per slot, the overall cache quantity is slotNum * slotSize +# successExpire: successful cache time seconds +# failedExpire: failed cache time seconds +# disable local caching and annotate topic, slotNum, and slotSize localCache: user: - topic: delete_cache_user + topic: DELETE_CACHE_USER slotNum: 500 slotSize: 20000 + successExpire: 300 + failedExpire: 5 group: - topic: delete_cache_group + topic: DELETE_CACHE_GROUP slotNum: 500 slotSize: 20000 + successExpire: 300 + failedExpire: 5 friend: - topic: delete_cache_friend + topic: DELETE_CACHE_FRIEND slotNum: 500 slotSize: 20000 + successExpire: 300 + failedExpire: 5 conversation: - topic: delete_cache_conversation + topic: DELETE_CACHE_CONVERSATION slotNum: 500 - slotSize: 20000 \ No newline at end of file + slotSize: 20000 + successExpire: 300 + failedExpire: 5 \ No newline at end of file diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 3cc1f4e6e..20eea0464 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -16,6 +16,7 @@ package config import ( "bytes" + "time" "github.com/OpenIMSDK/tools/discoveryregistry" "gopkg.in/yaml.v3" @@ -373,9 +374,19 @@ type notification struct { } type LocalCache struct { - Topic string `yaml:"topic"` - SlotNum int `yaml:"slotNum"` - SlotSize int `yaml:"slotSize"` + Topic string `yaml:"topic"` + SlotNum int `yaml:"slotNum"` + SlotSize int `yaml:"slotSize"` + SuccessExpire int `yaml:"successExpire"` // second + FailedExpire int `yaml:"failedExpire"` // second +} + +func (l LocalCache) Failed() time.Duration { + return time.Second * time.Duration(l.FailedExpire) +} + +func (l LocalCache) Success() time.Duration { + return time.Second * time.Duration(l.SuccessExpire) } func (l LocalCache) Enable() bool { diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 061b05bec..8eadad9d4 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -20,6 +20,9 @@ func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis local: localcache.New[any]( localcache.WithLocalSlotNum(lc.SlotNum), localcache.WithLocalSlotSize(lc.SlotSize), + localcache.WithLinkSlotNum(lc.SlotNum), + localcache.WithLocalSuccessTTL(lc.Success()), + localcache.WithLocalFailedTTL(lc.Failed()), ), } if lc.Enable() { diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go index d9ee377b3..66694f618 100644 --- a/pkg/rpccache/friend.go +++ b/pkg/rpccache/friend.go @@ -18,6 +18,9 @@ func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalCl local: localcache.New[any]( localcache.WithLocalSlotNum(lc.SlotNum), localcache.WithLocalSlotSize(lc.SlotSize), + localcache.WithLinkSlotNum(lc.SlotNum), + localcache.WithLocalSuccessTTL(lc.Success()), + localcache.WithLocalFailedTTL(lc.Failed()), ), } if lc.Enable() { diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go index 6a12aa9f0..b3c666da4 100644 --- a/pkg/rpccache/group.go +++ b/pkg/rpccache/group.go @@ -20,6 +20,9 @@ func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClie local: localcache.New[any]( localcache.WithLocalSlotNum(lc.SlotNum), localcache.WithLocalSlotSize(lc.SlotSize), + localcache.WithLinkSlotNum(lc.SlotNum), + localcache.WithLocalSuccessTTL(lc.Success()), + localcache.WithLocalFailedTTL(lc.Failed()), ), } if lc.Enable() { diff --git a/pkg/rpccache/user.go b/pkg/rpccache/user.go index fd4c24c75..7d6cd5c7e 100644 --- a/pkg/rpccache/user.go +++ b/pkg/rpccache/user.go @@ -20,6 +20,9 @@ func NewUserLocalCache(client rpcclient.UserRpcClient, cli redis.UniversalClient local: localcache.New[any]( localcache.WithLocalSlotNum(lc.SlotNum), localcache.WithLocalSlotSize(lc.SlotSize), + localcache.WithLinkSlotNum(lc.SlotNum), + localcache.WithLocalSuccessTTL(lc.Success()), + localcache.WithLocalFailedTTL(lc.Failed()), ), } if lc.Enable() { From e3ff3043946b2d45b9486e2f4e57bf2110ab7aa3 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Thu, 18 Jan 2024 15:09:27 +0800 Subject: [PATCH 53/66] feat: msg rpc local cache --- deployments/templates/openim.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 8e7e05904..ed3fb261a 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -538,28 +538,28 @@ prometheus: localCache: user: topic: DELETE_CACHE_USER - slotNum: 500 - slotSize: 20000 + slotNum: 100 + slotSize: 2000 successExpire: 300 failedExpire: 5 group: topic: DELETE_CACHE_GROUP - slotNum: 500 - slotSize: 20000 + slotNum: 100 + slotSize: 2000 successExpire: 300 failedExpire: 5 friend: topic: DELETE_CACHE_FRIEND - slotNum: 500 - slotSize: 20000 + slotNum: 100 + slotSize: 2000 successExpire: 300 failedExpire: 5 conversation: topic: DELETE_CACHE_CONVERSATION - slotNum: 500 - slotSize: 20000 + slotNum: 100 + slotSize: 2000 successExpire: 300 failedExpire: 5 \ No newline at end of file From 0ee68e57b90e754eb765e885670e19517f719c35 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Thu, 18 Jan 2024 15:42:57 +0800 Subject: [PATCH 54/66] feat: msg rpc local cache --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 1086560bc..428da6452 100644 --- a/go.mod +++ b/go.mod @@ -160,3 +160,5 @@ require ( ) replace github.com/openimsdk/localcache => ./pkg/localcache + +replace github.com/OpenIMSDK/protocol v0.0.47 => github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94 diff --git a/go.sum b/go.sum index 8f44fbc52..06cdbe07b 100644 --- a/go.sum +++ b/go.sum @@ -15,11 +15,11 @@ cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/o cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= +github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94 h1:o86vkek41ZrQqoBGqyKvS0z6N0uJj64mpzK72OkDZVM= +github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.47 h1:DTJMFSONzqT0i/wa4Q1CtDT/jVATVudIRHcpY1zSWYE= -github.com/OpenIMSDK/protocol v0.0.47/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.23 h1:xozfrGzhbpNPlDTap5DLVPk+JfgZ/ZyIj4Cuu3/bm9w= github.com/OpenIMSDK/tools v0.0.23/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= From 84f97a72f4914b7dd6c27bb203cabc0bf1fa2b32 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 23 Jan 2024 22:07:29 +0000 Subject: [PATCH 55/66] update tools --- go.mod | 2 +- go.sum | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 428da6452..e74fbf5d3 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.47 - github.com/OpenIMSDK/tools v0.0.23 + github.com/OpenIMSDK/tools v0.0.27 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index 06cdbe07b..0531c7383 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= github.com/OpenIMSDK/tools v0.0.23 h1:xozfrGzhbpNPlDTap5DLVPk+JfgZ/ZyIj4Cuu3/bm9w= github.com/OpenIMSDK/tools v0.0.23/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= +github.com/OpenIMSDK/tools v0.0.27 h1:J/kSRqM+y9U4XK/pQ9RkEB31oQ5BTYD1oA5r1PITPRA= +github.com/OpenIMSDK/tools v0.0.27/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= @@ -91,6 +93,7 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -200,8 +203,10 @@ github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -254,6 +259,7 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w= github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -384,6 +390,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -468,6 +475,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 9660556f3528905418f5d1c1ad0bb0bf3fe310aa Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:47:07 +0800 Subject: [PATCH 56/66] refactor: refactor the code of push and optimization. --- go.mod | 2 +- go.sum | 12 +- internal/msggateway/hub_server.go | 26 +- internal/push/callback.go | 12 + internal/push/consumer_init.go | 32 -- internal/push/offlinepush/dummy/push.go | 2 +- internal/push/offlinepush/fcm/push.go | 2 +- internal/push/offlinepush/getui/push.go | 24 +- internal/push/offlinepush/jpush/push.go | 2 +- ...linepush_interface.go => offlinepusher.go} | 27 + internal/push/onlinepusher.go | 211 ++++++++ internal/push/push.go | 51 ++ internal/push/push_handler.go | 285 +++++++++- internal/push/push_rpc_server.go | 109 ---- internal/push/push_to_client.go | 511 ------------------ internal/push/tools.go | 32 -- pkg/rpcclient/conversation.go | 8 + 17 files changed, 610 insertions(+), 738 deletions(-) delete mode 100644 internal/push/consumer_init.go rename internal/push/offlinepush/{offlinepush_interface.go => offlinepusher.go} (54%) create mode 100644 internal/push/onlinepusher.go create mode 100644 internal/push/push.go delete mode 100644 internal/push/push_rpc_server.go delete mode 100644 internal/push/push_to_client.go delete mode 100644 internal/push/tools.go diff --git a/go.mod b/go.mod index e74fbf5d3..bf54752c9 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.47 + github.com/OpenIMSDK/protocol v0.0.49 github.com/OpenIMSDK/tools v0.0.27 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 diff --git a/go.sum b/go.sum index 0531c7383..7c318e02a 100644 --- a/go.sum +++ b/go.sum @@ -15,13 +15,11 @@ cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/o cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= -github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94 h1:o86vkek41ZrQqoBGqyKvS0z6N0uJj64mpzK72OkDZVM= -github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/tools v0.0.23 h1:xozfrGzhbpNPlDTap5DLVPk+JfgZ/ZyIj4Cuu3/bm9w= -github.com/OpenIMSDK/tools v0.0.23/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= +github.com/OpenIMSDK/protocol v0.0.49 h1:wcqJOMBis7f153zNI7V82Fc4WyqA1GanMgXUQgL618k= +github.com/OpenIMSDK/protocol v0.0.49/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.27 h1:J/kSRqM+y9U4XK/pQ9RkEB31oQ5BTYD1oA5r1PITPRA= github.com/OpenIMSDK/tools v0.0.27/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= @@ -93,7 +91,6 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -203,10 +200,8 @@ github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -259,7 +254,6 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w= github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -390,7 +384,6 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -475,7 +468,6 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index 807c4af3b..abf834225 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -25,8 +25,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" @@ -59,7 +57,7 @@ type Server struct { rpcPort int prometheusPort int LongConnServer LongConnServer - pushTerminal []int + pushTerminal map[int]struct{} } func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { @@ -67,12 +65,15 @@ func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { } func NewServer(rpcPort int, proPort int, longConnServer LongConnServer) *Server { - return &Server{ + s := &Server{ rpcPort: rpcPort, prometheusPort: proPort, LongConnServer: longConnServer, - pushTerminal: []int{constant.IOSPlatformID, constant.AndroidPlatformID}, + pushTerminal: make(map[int]struct{}), } + s.pushTerminal[constant.IOSPlatformID] = struct{}{} + s.pushTerminal[constant.AndroidPlatformID] = struct{}{} + return s } func (s *Server) OnlinePushMsg( @@ -126,13 +127,9 @@ func (s *Server) OnlineBatchPushOneMsg( panic("implement me") } -func (s *Server) SuperGroupOnlineBatchPushOneMsg( - ctx context.Context, - req *msggateway.OnlineBatchPushOneMsgReq, +func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq, ) (*msggateway.OnlineBatchPushOneMsgResp, error) { - var singleUserResults []*msggateway.SingleMsgToUserResults - for _, v := range req.PushToUserIDs { var resp []*msggateway.SingleMsgToUserPlatform results := &msggateway.SingleMsgToUserResults{ @@ -153,23 +150,22 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg( } userPlatform := &msggateway.SingleMsgToUserPlatform{ - RecvID: v, - RecvPlatFormID: int32(client.PlatformID), + PlatFormID: int32(client.PlatformID), } if !client.IsBackground || (client.IsBackground && client.PlatformID != constant.IOSPlatformID) { err := client.PushMessage(ctx, req.MsgData) if err != nil { - userPlatform.ResultCode = -2 + userPlatform.ResultCode = int64(errs.ErrPushMsgErr.Code()) resp = append(resp, userPlatform) } else { - if utils.IsContainInt(client.PlatformID, s.pushTerminal) { + if _, ok := s.pushTerminal[client.PlatformID]; ok { results.OnlinePush = true resp = append(resp, userPlatform) } } } else { - userPlatform.ResultCode = -3 + userPlatform.ResultCode = int64(errs.ErrIOSBackgroundPushErr.Code()) resp = append(resp, userPlatform) } } diff --git a/internal/push/callback.go b/internal/push/callback.go index 99a58fb07..90f918fc0 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -16,6 +16,7 @@ package push import ( "context" + "encoding/json" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" @@ -136,3 +137,14 @@ func callbackBeforeSuperGroupOnlinePush( } return nil } +func GetContent(msg *sdkws.MsgData) string { + if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd { + var notification sdkws.NotificationElem + if err := json.Unmarshal(msg.Content, ¬ification); err != nil { + return "" + } + return notification.Detail + } else { + return string(msg.Content) + } +} diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go deleted file mode 100644 index b72c32bb1..000000000 --- a/internal/push/consumer_init.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package push - -type Consumer struct { - pushCh ConsumerHandler - successCount uint64 -} - -func NewConsumer(pusher *Pusher) *Consumer { - return &Consumer{ - pushCh: *NewConsumerHandler(pusher), - } -} - -func (c *Consumer) Start() { - // statistics.NewStatistics(&c.successCount, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to - // msg_gateway count", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) - go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(&c.pushCh) -} diff --git a/internal/push/offlinepush/dummy/push.go b/internal/push/offlinepush/dummy/push.go index f147886d9..16e5fa747 100644 --- a/internal/push/offlinepush/dummy/push.go +++ b/internal/push/offlinepush/dummy/push.go @@ -20,7 +20,7 @@ import ( "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" ) -func NewClient() *Dummy { +func NewDummy() *Dummy { return &Dummy{} } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 8145d4c17..4d88396d1 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -39,7 +39,7 @@ type Fcm struct { cache cache.MsgModel } -func NewClient(cache cache.MsgModel) *Fcm { +func NewFcm(cache cache.MsgModel) *Fcm { projectRoot := config.GetProjectRoot() credentialsFilePath := filepath.Join(projectRoot, "config", config.Config.Push.Fcm.ServiceAccount) opt := option.WithCredentialsFile(credentialsFilePath) diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index b657c9c23..b94deb0eb 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -55,17 +55,17 @@ const ( taskIDTTL = 1000 * 60 * 60 * 24 ) -type Client struct { +type GeTui struct { cache cache.MsgModel tokenExpireTime int64 taskIDTTL int64 } -func NewClient(cache cache.MsgModel) *Client { - return &Client{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL} +func NewGeTui(cache cache.MsgModel) *GeTui { + return &GeTui{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL} } -func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { +func (g *GeTui) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { token, err := g.cache.GetGetuiToken(ctx) if err != nil { if errs.Unwrap(err) == redis.Nil { @@ -111,7 +111,7 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri return err } -func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) { +func (g *GeTui) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) { h := sha256.New() h.Write( []byte(config.Config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.GeTui.MasterSecret), @@ -131,7 +131,7 @@ func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expir return respAuth.Token, int64(expire), err } -func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) (string, error) { +func (g *GeTui) GetTaskID(ctx context.Context, token string, pushReq PushReq) (string, error) { respTask := TaskResp{} ttl := int64(1000 * 60 * 5) pushReq.Settings = &Settings{TTL: &ttl} @@ -143,7 +143,7 @@ func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) ( } // max num is 999. -func (g *Client) batchPush(ctx context.Context, token string, userIDs []string, pushReq PushReq) error { +func (g *GeTui) batchPush(ctx context.Context, token string, userIDs []string, pushReq PushReq) error { taskID, err := g.GetTaskID(ctx, token, pushReq) if err != nil { return err @@ -152,21 +152,21 @@ func (g *Client) batchPush(ctx context.Context, token string, userIDs []string, return g.request(ctx, batchPushURL, pushReq, token, nil) } -func (g *Client) singlePush(ctx context.Context, token, userID string, pushReq PushReq) error { +func (g *GeTui) singlePush(ctx context.Context, token, userID string, pushReq PushReq) error { operationID := mcontext.GetOperationID(ctx) pushReq.RequestID = &operationID pushReq.Audience = &Audience{Alias: []string{userID}} return g.request(ctx, pushURL, pushReq, token, nil) } -func (g *Client) request(ctx context.Context, url string, input any, token string, output any) error { +func (g *GeTui) request(ctx context.Context, url string, input any, token string, output any) error { header := map[string]string{"token": token} resp := &Resp{} resp.Data = output return g.postReturn(ctx, config.Config.Push.GeTui.PushUrl+url, header, input, resp, 3) } -func (g *Client) postReturn( +func (g *GeTui) postReturn( ctx context.Context, url string, header map[string]string, @@ -181,7 +181,7 @@ func (g *Client) postReturn( return output.parseError() } -func (g *Client) getTokenAndSave2Redis(ctx context.Context) (token string, err error) { +func (g *GeTui) getTokenAndSave2Redis(ctx context.Context) (token string, err error) { token, _, err = g.Auth(ctx, time.Now().UnixNano()/1e6) if err != nil { return @@ -193,7 +193,7 @@ func (g *Client) getTokenAndSave2Redis(ctx context.Context) (token string, err e return token, nil } -func (g *Client) GetTaskIDAndSave2Redis(ctx context.Context, token string, pushReq PushReq) (taskID string, err error) { +func (g *GeTui) GetTaskIDAndSave2Redis(ctx context.Context, token string, pushReq PushReq) (taskID string, err error) { pushReq.Settings = &Settings{TTL: &g.taskIDTTL} taskID, err = g.GetTaskID(ctx, token, pushReq) if err != nil { diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index 567269f3c..842d91fcf 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -27,7 +27,7 @@ import ( type JPush struct{} -func NewClient() *JPush { +func NewJPush() *JPush { return &JPush{} } diff --git a/internal/push/offlinepush/offlinepush_interface.go b/internal/push/offlinepush/offlinepusher.go similarity index 54% rename from internal/push/offlinepush/offlinepush_interface.go rename to internal/push/offlinepush/offlinepusher.go index a5d4051f9..23183024a 100644 --- a/internal/push/offlinepush/offlinepush_interface.go +++ b/internal/push/offlinepush/offlinepusher.go @@ -16,6 +16,18 @@ package offlinepush import ( "context" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" +) + +const ( + GETUI = "getui" + FIREBASE = "fcm" + JPUSH = "jpush" ) // OfflinePusher Offline Pusher. @@ -23,6 +35,21 @@ type OfflinePusher interface { Push(ctx context.Context, userIDs []string, title, content string, opts *Opts) error } +func NewOfflinePusher(cache cache.MsgModel) OfflinePusher { + var offlinePusher OfflinePusher + switch config.Config.Push.Enable { + case GETUI: + offlinePusher = getui.NewGeTui(cache) + case FIREBASE: + offlinePusher = fcm.NewFcm(cache) + case JPUSH: + offlinePusher = jpush.NewJPush() + default: + offlinePusher = dummy.NewDummy() + } + return offlinePusher +} + // Opts opts. type Opts struct { Signal *Signal diff --git a/internal/push/onlinepusher.go b/internal/push/onlinepusher.go new file mode 100644 index 000000000..35b9a97b7 --- /dev/null +++ b/internal/push/onlinepusher.go @@ -0,0 +1,211 @@ +package push + +import ( + "context" + "github.com/OpenIMSDK/protocol/msggateway" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + "os" + "sync" +) + +const ( + ENVNAME = "ENVS_DISCOVERY" + KUBERNETES = "k8s" + ZOOKEEPER = "zookeeper" +) + +type OnlinePusher interface { + GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, + pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) + GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults, + pushToUserIDs *[]string) []string +} + +type emptyOnlinePUsher struct{} + +func newEmptyOnlinePUsher() *emptyOnlinePUsher { + return &emptyOnlinePUsher{} +} + +func (emptyOnlinePUsher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, + pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + log.ZWarn(ctx, "emptyOnlinePUsher GetConnsAndOnlinePush", nil) + return nil, nil +} +func (u emptyOnlinePUsher) GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, + wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string { + log.ZWarn(ctx, "emptyOnlinePUsher GetOnlinePushFailedUserIDs", nil) + return nil +} + +func NewOnlinePusher(disCov discoveryregistry.SvcDiscoveryRegistry) OnlinePusher { + var envType string + if value := os.Getenv(ENVNAME); value != "" { + envType = os.Getenv(ENVNAME) + } else { + envType = config.Config.Envs.Discovery + } + switch envType { + case KUBERNETES: + return NewK8sStaticConsistentHash(disCov) + case ZOOKEEPER: + return NewDefaultAllNode(disCov) + default: + return newEmptyOnlinePUsher() + } +} + +type DefaultAllNode struct { + disCov discoveryregistry.SvcDiscoveryRegistry +} + +func NewDefaultAllNode(disCov discoveryregistry.SvcDiscoveryRegistry) *DefaultAllNode { + return &DefaultAllNode{disCov: disCov} +} + +func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, + pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + conns, err := d.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) + log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) + if err != nil { + return nil, err + } + + var ( + mu sync.Mutex + wg = errgroup.Group{} + input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs} + maxWorkers = config.Config.Push.MaxConcurrentWorkers + ) + + if maxWorkers < 3 { + maxWorkers = 3 + } + + wg.SetLimit(maxWorkers) + + // Online push message + for _, conn := range conns { + conn := conn // loop var safe + wg.Go(func() error { + msgClient := msggateway.NewMsgGatewayClient(conn) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) + if err != nil { + return nil + } + + log.ZDebug(ctx, "push result", "reply", reply) + if reply != nil && reply.SinglePushResult != nil { + mu.Lock() + wsResults = append(wsResults, reply.SinglePushResult...) + mu.Unlock() + } + + return nil + }) + } + + _ = wg.Wait() + + // always return nil + return wsResults, nil +} + +func (d *DefaultAllNode) GetOnlinePushFailedUserIDs(_ context.Context, msg *sdkws.MsgData, + wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string { + + onlineSuccessUserIDs := []string{msg.SendID} + for _, v := range wsResults { + //message sender do not need offline push + if msg.SendID == v.UserID { + continue + } + // mobile online push success + if v.OnlinePush { + onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID) + } + + } + + return utils.SliceSub(*pushToUserIDs, onlineSuccessUserIDs) +} + +type K8sStaticConsistentHash struct { + disCov discoveryregistry.SvcDiscoveryRegistry +} + +func NewK8sStaticConsistentHash(disCov discoveryregistry.SvcDiscoveryRegistry) *K8sStaticConsistentHash { + return &K8sStaticConsistentHash{disCov: disCov} +} + +func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, + pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + + var usersHost = make(map[string][]string) + for _, v := range pushToUserIDs { + tHost, err := k.disCov.GetUserIdHashGatewayHost(ctx, v) + if err != nil { + log.ZError(ctx, "get msg gateway hash error", err) + return nil, err + } + tUsers, tbl := usersHost[tHost] + if tbl { + tUsers = append(tUsers, v) + usersHost[tHost] = tUsers + } else { + usersHost[tHost] = []string{v} + } + } + log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) + var usersConns = make(map[*grpc.ClientConn][]string) + for host, userIds := range usersHost { + tconn, _ := k.disCov.GetConn(ctx, host) + usersConns[tconn] = userIds + } + var ( + mu sync.Mutex + wg = errgroup.Group{} + maxWorkers = config.Config.Push.MaxConcurrentWorkers + ) + if maxWorkers < 3 { + maxWorkers = 3 + } + wg.SetLimit(maxWorkers) + for conn, userIds := range usersConns { + tcon := conn + tuserIds := userIds + wg.Go(func() error { + input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds} + msgClient := msggateway.NewMsgGatewayClient(tcon) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) + if err != nil { + return nil + } + log.ZDebug(ctx, "push result", "reply", reply) + if reply != nil && reply.SinglePushResult != nil { + mu.Lock() + wsResults = append(wsResults, reply.SinglePushResult...) + mu.Unlock() + } + return nil + }) + } + _ = wg.Wait() + return wsResults, nil +} +func (k *K8sStaticConsistentHash) GetOnlinePushFailedUserIDs(_ context.Context, _ *sdkws.MsgData, + wsResults []*msggateway.SingleMsgToUserResults, _ *[]string) []string { + var needOfflinePushUserIDs []string + for _, v := range wsResults { + if !v.OnlinePush { + needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID) + } + } + return needOfflinePushUserIDs +} diff --git a/internal/push/push.go b/internal/push/push.go new file mode 100644 index 000000000..90e62ae03 --- /dev/null +++ b/internal/push/push.go @@ -0,0 +1,51 @@ +package push + +import ( + "context" + pbpush "github.com/OpenIMSDK/protocol/push" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "google.golang.org/grpc" +) + +type pushServer struct { + database controller.PushDatabase + disCov discoveryregistry.SvcDiscoveryRegistry + offlinePusher offlinepush.OfflinePusher + pushCh *ConsumerHandler +} + +func (p pushServer) PushMsg(ctx context.Context, req *pbpush.PushMsgReq) (*pbpush.PushMsgResp, error) { + //todo reserved Interface + return nil, nil +} + +func (p pushServer) DelUserPushToken(ctx context.Context, + req *pbpush.DelUserPushTokenReq) (resp *pbpush.DelUserPushTokenResp, err error) { + if err = p.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil { + return nil, err + } + return &pbpush.DelUserPushTokenResp{}, nil +} + +func Start(disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis() + if err != nil { + return err + } + cacheModel := cache.NewMsgCacheModel(rdb) + offlinePusher := offlinepush.NewOfflinePusher(cacheModel) + database := controller.NewPushDatabase(cacheModel) + + consumer := NewConsumerHandler(offlinePusher, rdb, disCov) + pbpush.RegisterPushMsgServiceServer(server, &pushServer{ + database: database, + disCov: disCov, + offlinePusher: offlinePusher, + pushCh: consumer, + }) + go consumer.pushConsumerGroup.RegisterHandleAndConsumer(consumer) + return nil +} diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index c91206ecc..7ccabb7a5 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -16,6 +16,16 @@ package push import ( "context" + "encoding/json" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/mcontext" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "github.com/openimsdk/open-im-server/v3/pkg/rpccache" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" "github.com/IBM/sarama" "google.golang.org/protobuf/proto" @@ -31,18 +41,31 @@ import ( ) type ConsumerHandler struct { - pushConsumerGroup *kfk.MConsumerGroup - pusher *Pusher + pushConsumerGroup *kfk.MConsumerGroup + offlinePusher offlinepush.OfflinePusher + onlinePusher OnlinePusher + groupLocalCache *rpccache.GroupLocalCache + conversationLocalCache *rpccache.ConversationLocalCache + msgRpcClient rpcclient.MessageRpcClient + conversationRpcClient rpcclient.ConversationRpcClient + groupRpcClient rpcclient.GroupRpcClient } -func NewConsumerHandler(pusher *Pusher) *ConsumerHandler { +func NewConsumerHandler(offlinePusher offlinepush.OfflinePusher, + rdb redis.UniversalClient, disCov discoveryregistry.SvcDiscoveryRegistry) *ConsumerHandler { var consumerHandler ConsumerHandler - consumerHandler.pusher = pusher consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, }, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToPush) + consumerHandler.offlinePusher = offlinePusher + consumerHandler.onlinePusher = NewOnlinePusher(disCov) + consumerHandler.groupRpcClient = rpcclient.NewGroupRpcClient(disCov) + consumerHandler.groupLocalCache = rpccache.NewGroupLocalCache(consumerHandler.groupRpcClient, rdb) + consumerHandler.msgRpcClient = rpcclient.NewMessageRpcClient(disCov) + consumerHandler.conversationRpcClient = rpcclient.NewConversationRpcClient(disCov) + consumerHandler.conversationLocalCache = rpccache.NewConversationLocalCache(consumerHandler.conversationRpcClient, rdb) return &consumerHandler } @@ -65,7 +88,7 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) { var err error switch msgFromMQ.MsgData.SessionType { case constant.SuperGroupChatType: - err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) + err = c.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) default: var pushUserIDList []string isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync) @@ -74,18 +97,14 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) { } else { pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID) } - err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData) + err = c.Push2User(ctx, pushUserIDList, pbData.MsgData) } if err != nil { - if err == errNoOfflinePusher { - log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String()) - } else { - log.ZError(ctx, "push failed", err, "msg", pbData.String()) - } + log.ZError(ctx, "push failed", err, "msg", pbData.String()) } } -func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } -func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } +func (*ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } +func (*ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim, ) error { @@ -96,3 +115,243 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, } return nil } + +// Push2User Suitable for two types of conversations, one is SingleChatType and the other is NotificationChatType. +func (c *ConsumerHandler) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { + log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) + if err := callbackOnlinePush(ctx, userIDs, msg); err != nil { + return err + } + + wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, userIDs) + if err != nil { + return err + } + + log.ZDebug(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs) + + if !c.shouldPushOffline(ctx, msg) { + return nil + } + + for _, v := range wsResults { + //message sender do not need offline push + if msg.SendID == v.UserID { + continue + } + //receiver online push success + if v.OnlinePush { + return nil + } + } + offlinePUshUserID := []string{msg.RecvID} + //receiver offline push + if err = callbackOfflinePush(ctx, offlinePUshUserID, msg, nil); err != nil { + return err + } + + err = c.offlinePushMsg(ctx, msg, offlinePUshUserID) + if err != nil { + return err + } + + return nil +} + +func (c *ConsumerHandler) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { + log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) + var pushToUserIDs []string + if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil { + return err + } + + err = c.groupMessagesHandler(ctx, groupID, &pushToUserIDs, msg) + if err != nil { + return err + } + + wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs) + if err != nil { + return err + } + + log.ZDebug(ctx, "group push result", "result", wsResults, "msg", msg) + + if !c.shouldPushOffline(ctx, msg) { + return nil + } + needOfflinePushUserIDs := c.onlinePusher.GetOnlinePushFailedUserIDs(ctx, msg, wsResults, &pushToUserIDs) + + //filter some user, like don not disturb or don't need offline push etc. + needOfflinePushUserIDs, err = c.filterGroupMessageOfflinePush(ctx, groupID, msg, needOfflinePushUserIDs) + if err != nil { + return err + } + // Use offline push messaging + if len(needOfflinePushUserIDs) > 0 { + var offlinePushUserIDs []string + err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) + if err != nil { + return err + } + + if len(offlinePushUserIDs) > 0 { + needOfflinePushUserIDs = offlinePushUserIDs + } + + err = c.offlinePushMsg(ctx, msg, needOfflinePushUserIDs) + if err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) + return err + } + + } + + return nil +} + +func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData, offlinePushUserIDs []string) error { + title, content, opts, err := c.getOfflinePushInfos(msg) + if err != nil { + return err + } + err = c.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts) + if err != nil { + prommetrics.MsgOfflinePushFailedCounter.Inc() + return err + } + return nil +} + +func (c *ConsumerHandler) filterGroupMessageOfflinePush(ctx context.Context, groupID string, msg *sdkws.MsgData, + offlinePushUserIDs []string) (userIDs []string, err error) { + + //todo local cache Obtain the difference set through local comparison. + needOfflinePushUserIDs, err := c.conversationRpcClient.GetConversationOfflinePushUserIDs( + ctx, utils.GenGroupConversationID(groupID), offlinePushUserIDs) + if err != nil { + return nil, err + } + return needOfflinePushUserIDs, nil +} + +func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) { + type AtTextElem struct { + Text string `json:"text,omitempty"` + AtUserList []string `json:"atUserList,omitempty"` + IsAtSelf bool `json:"isAtSelf"` + } + + opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}} + if msg.OfflinePushInfo != nil { + opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount + opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound + opts.Ex = msg.OfflinePushInfo.Ex + } + + if msg.OfflinePushInfo != nil { + title = msg.OfflinePushInfo.Title + content = msg.OfflinePushInfo.Desc + } + if title == "" { + switch msg.ContentType { + case constant.Text: + fallthrough + case constant.Picture: + fallthrough + case constant.Voice: + fallthrough + case constant.Video: + fallthrough + case constant.File: + title = constant.ContentType2PushContent[int64(msg.ContentType)] + case constant.AtText: + ac := AtTextElem{} + _ = utils.JsonStringToStruct(string(msg.Content), &ac) + case constant.SignalingNotification: + title = constant.ContentType2PushContent[constant.SignalMsg] + default: + title = constant.ContentType2PushContent[constant.Common] + } + } + if content == "" { + content = title + } + return +} +func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID string, pushToUserIDs *[]string, msg *sdkws.MsgData) (err error) { + if len(*pushToUserIDs) == 0 { + *pushToUserIDs, err = c.groupLocalCache.GetGroupMemberIDs(ctx, groupID) + if err != nil { + return err + } + switch msg.ContentType { + case constant.MemberQuitNotification: + var tips sdkws.MemberQuitTips + if unmarshalNotificationElem(msg.Content, &tips) != nil { + return err + } + if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, []string{tips.QuitUser.UserID}); err != nil { + log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userID", tips.QuitUser.UserID) + } + *pushToUserIDs = append(*pushToUserIDs, tips.QuitUser.UserID) + case constant.MemberKickedNotification: + var tips sdkws.MemberKickedTips + if unmarshalNotificationElem(msg.Content, &tips) != nil { + return err + } + kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }) + if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, kickedUsers); err != nil { + log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", kickedUsers) + } + + *pushToUserIDs = append(*pushToUserIDs, kickedUsers...) + case constant.GroupDismissedNotification: + if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到 + var tips sdkws.GroupDismissedTips + if unmarshalNotificationElem(msg.Content, &tips) != nil { + return err + } + log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(*pushToUserIDs), "list", pushToUserIDs) + if len(config.Config.Manager.UserID) > 0 { + ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0]) + } + defer func(groupID string) { + if err = c.groupRpcClient.DismissGroup(ctx, groupID); err != nil { + log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID) + } + }(groupID) + } + } + } + return err +} + +func (c *ConsumerHandler) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error { + conversationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID) + maxSeq, err := c.msgRpcClient.GetConversationMaxSeq(ctx, conversationID) + if err != nil { + return err + } + return c.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conversationID, maxSeq) +} + +func unmarshalNotificationElem(bytes []byte, t any) error { + var notification sdkws.NotificationElem + if err := json.Unmarshal(bytes, ¬ification); err != nil { + return err + } + + return json.Unmarshal([]byte(notification.Detail), t) +} + +func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgData) bool { + isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) + if !isOfflinePush { + return false + } + if msg.ContentType == constant.SignalingNotification { + return false + } + return true +} diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go deleted file mode 100644 index 92a6c4b8f..000000000 --- a/internal/push/push_rpc_server.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package push - -import ( - "context" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/rpccache" - "sync" - - "google.golang.org/grpc" - - "github.com/OpenIMSDK/protocol/constant" - pbpush "github.com/OpenIMSDK/protocol/push" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" -) - -type pushServer struct { - pusher *Pusher -} - -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() - if err != nil { - return err - } - cacheModel := cache.NewMsgCacheModel(rdb) - offlinePusher := NewOfflinePusher(cacheModel) - database := controller.NewPushDatabase(cacheModel) - groupRpcClient := rpcclient.NewGroupRpcClient(client) - conversationRpcClient := rpcclient.NewConversationRpcClient(client) - msgRpcClient := rpcclient.NewMessageRpcClient(client) - pusher := NewPusher( - client, - offlinePusher, - database, - rpccache.NewGroupLocalCache(groupRpcClient, rdb), - rpccache.NewConversationLocalCache(conversationRpcClient, rdb), - &conversationRpcClient, - &groupRpcClient, - &msgRpcClient, - ) - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - pbpush.RegisterPushMsgServiceServer(server, &pushServer{ - pusher: pusher, - }) - }() - go func() { - defer wg.Done() - consumer := NewConsumer(pusher) - consumer.Start() - }() - wg.Wait() - return nil -} - -func (r *pushServer) PushMsg(ctx context.Context, pbData *pbpush.PushMsgReq) (resp *pbpush.PushMsgResp, err error) { - switch pbData.MsgData.SessionType { - case constant.SuperGroupChatType: - err = r.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) - default: - var pushUserIDList []string - isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync) - if !isSenderSync { - pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID) - } else { - pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID) - } - err = r.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData) - } - if err != nil { - if err != errNoOfflinePusher { - return nil, err - } else { - log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String()) - } - } - return &pbpush.PushMsgResp{}, nil -} - -func (r *pushServer) DelUserPushToken( - ctx context.Context, - req *pbpush.DelUserPushTokenReq, -) (resp *pbpush.DelUserPushTokenResp, err error) { - if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil { - return nil, err - } - return &pbpush.DelUserPushTokenResp{}, nil -} diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go deleted file mode 100644 index 2cf1a34b5..000000000 --- a/internal/push/push_to_client.go +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package push - -import ( - "context" - "encoding/json" - "errors" - "github.com/openimsdk/open-im-server/v3/pkg/rpccache" - "google.golang.org/grpc" - "sync" - - "golang.org/x/sync/errgroup" - - "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/protocol/conversation" - "github.com/OpenIMSDK/protocol/msggateway" - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/mcontext" - "github.com/OpenIMSDK/tools/utils" - - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" -) - -type Pusher struct { - database controller.PushDatabase - discov discoveryregistry.SvcDiscoveryRegistry - offlinePusher offlinepush.OfflinePusher - groupLocalCache *rpccache.GroupLocalCache - conversationLocalCache *rpccache.ConversationLocalCache - msgRpcClient *rpcclient.MessageRpcClient - conversationRpcClient *rpcclient.ConversationRpcClient - groupRpcClient *rpcclient.GroupRpcClient -} - -var errNoOfflinePusher = errors.New("no offlinePusher is configured") - -func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, - groupLocalCache *rpccache.GroupLocalCache, conversationLocalCache *rpccache.ConversationLocalCache, - conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient, -) *Pusher { - return &Pusher{ - discov: discov, - database: database, - offlinePusher: offlinePusher, - groupLocalCache: groupLocalCache, - conversationLocalCache: conversationLocalCache, - msgRpcClient: msgRpcClient, - conversationRpcClient: conversationRpcClient, - groupRpcClient: groupRpcClient, - } -} - -func NewOfflinePusher(cache cache.MsgModel) offlinepush.OfflinePusher { - var offlinePusher offlinepush.OfflinePusher - switch config.Config.Push.Enable { - case "getui": - offlinePusher = getui.NewClient(cache) - case "fcm": - offlinePusher = fcm.NewClient(cache) - case "jpush": - offlinePusher = jpush.NewClient() - default: - offlinePusher = dummy.NewClient() - } - return offlinePusher -} - -func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error { - conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID) - maxSeq, err := p.msgRpcClient.GetConversationMaxSeq(ctx, conevrsationID) - if err != nil { - return err - } - return p.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq) -} - -func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { - log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) - if err := callbackOnlinePush(ctx, userIDs, msg); err != nil { - return err - } - // push - wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs) - if err != nil { - return err - } - - isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs) - - if !isOfflinePush { - return nil - } - - for _, v := range wsResults { - if !v.OnlinePush && msg.SendID == v.UserID { - if err = callbackOfflinePush(ctx, userIDs, msg, &[]string{}); err != nil { - return err - } - - err = p.offlinePushMsg(ctx, msg.SendID, msg, []string{v.UserID}) - if err != nil { - return err - } - } - - } - return nil -} - -func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error { - var notification sdkws.NotificationElem - if err := json.Unmarshal(bytes, ¬ification); err != nil { - return err - } - - return json.Unmarshal([]byte(notification.Detail), t) -} - -/* -k8s deployment,offline push group messages function -*/ -func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error { - - var needOfflinePushUserIDs []string - for _, v := range wsResults { - if !v.OnlinePush { - needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID) - } - } - if len(needOfflinePushUserIDs) > 0 { - var offlinePushUserIDs []string - err := callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) - if err != nil { - return err - } - - if len(offlinePushUserIDs) > 0 { - needOfflinePushUserIDs = offlinePushUserIDs - } - if msg.ContentType != constant.SignalingNotification { - resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( - ctx, - &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, - ) - if err != nil { - return err - } - if len(resp.UserIDs) > 0 { - err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) - if err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) - return err - } - } - } - - } - return nil -} -func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { - log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) - var pushToUserIDs []string - if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil { - return err - } - - if len(pushToUserIDs) == 0 { - pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID) - if err != nil { - return err - } - - switch msg.ContentType { - case constant.MemberQuitNotification: - var tips sdkws.MemberQuitTips - if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { - return err - } - defer func(groupID string, userIDs []string) { - if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { - log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) - } - }(groupID, []string{tips.QuitUser.UserID}) - pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID) - case constant.MemberKickedNotification: - var tips sdkws.MemberKickedTips - if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { - return err - } - kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }) - defer func(groupID string, userIDs []string) { - if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { - log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) - } - }(groupID, kickedUsers) - pushToUserIDs = append(pushToUserIDs, kickedUsers...) - case constant.GroupDismissedNotification: - if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到 - var tips sdkws.GroupDismissedTips - if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { - return err - } - log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs) - if len(config.Config.Manager.UserID) > 0 { - ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0]) - } - defer func(groupID string) { - if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil { - log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID) - } - }(groupID) - } - } - } - - wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs) - if err != nil { - return err - } - - log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg) - isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - if isOfflinePush && config.Config.Envs.Discovery == "k8s" { - return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults) - } - if isOfflinePush && config.Config.Envs.Discovery == "zookeeper" { - var ( - onlineSuccessUserIDs = []string{msg.SendID} - webAndPcBackgroundUserIDs []string - ) - - for _, v := range wsResults { - if v.OnlinePush && v.UserID != msg.SendID { - onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID) - } - - if v.OnlinePush { - continue - } - - if len(v.Resp) == 0 { - continue - } - - for _, singleResult := range v.Resp { - if singleResult.ResultCode != -2 { - continue - } - - isPC := constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC - isWebID := singleResult.RecvPlatFormID == constant.WebPlatformID - - if isPC || isWebID { - webAndPcBackgroundUserIDs = append(webAndPcBackgroundUserIDs, v.UserID) - } - } - } - - needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs) - - // Use offline push messaging - if len(needOfflinePushUserIDs) > 0 { - var offlinePushUserIDs []string - err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) - if err != nil { - return err - } - - if len(offlinePushUserIDs) > 0 { - needOfflinePushUserIDs = offlinePushUserIDs - } - if msg.ContentType != constant.SignalingNotification { - resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( - ctx, - &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, - ) - if err != nil { - return err - } - if len(resp.UserIDs) > 0 { - err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) - if err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) - return err - } - if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs)) - return err - } - } - } - - } - } - return nil -} - -func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { - var usersHost = make(map[string][]string) - for _, v := range pushToUserIDs { - tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v) - if err != nil { - log.ZError(ctx, "get msggateway hash error", err) - return nil, err - } - tUsers, tbl := usersHost[tHost] - if tbl { - tUsers = append(tUsers, v) - usersHost[tHost] = tUsers - } else { - usersHost[tHost] = []string{v} - } - } - log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) - var usersConns = make(map[*grpc.ClientConn][]string) - for host, userIds := range usersHost { - tconn, _ := p.discov.GetConn(ctx, host) - usersConns[tconn] = userIds - } - var ( - mu sync.Mutex - wg = errgroup.Group{} - maxWorkers = config.Config.Push.MaxConcurrentWorkers - ) - if maxWorkers < 3 { - maxWorkers = 3 - } - wg.SetLimit(maxWorkers) - for conn, userIds := range usersConns { - tcon := conn - tuserIds := userIds - wg.Go(func() error { - input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds} - msgClient := msggateway.NewMsgGatewayClient(tcon) - reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) - if err != nil { - return nil - } - log.ZDebug(ctx, "push result", "reply", reply) - if reply != nil && reply.SinglePushResult != nil { - mu.Lock() - wsResults = append(wsResults, reply.SinglePushResult...) - mu.Unlock() - } - return nil - }) - } - _ = wg.Wait() - return wsResults, nil -} -func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { - if config.Config.Envs.Discovery == "k8s" { - return p.k8sOnlinePush(ctx, msg, pushToUserIDs) - } - conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) - log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) - if err != nil { - return nil, err - } - - var ( - mu sync.Mutex - wg = errgroup.Group{} - input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs} - maxWorkers = config.Config.Push.MaxConcurrentWorkers - ) - - if maxWorkers < 3 { - maxWorkers = 3 - } - - wg.SetLimit(maxWorkers) - - // Online push message - for _, conn := range conns { - conn := conn // loop var safe - wg.Go(func() error { - msgClient := msggateway.NewMsgGatewayClient(conn) - reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) - if err != nil { - return nil - } - - log.ZDebug(ctx, "push result", "reply", reply) - if reply != nil && reply.SinglePushResult != nil { - mu.Lock() - wsResults = append(wsResults, reply.SinglePushResult...) - mu.Unlock() - } - - return nil - }) - } - - _ = wg.Wait() - - // always return nil - return wsResults, nil -} - -func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error { - title, content, opts, err := p.getOfflinePushInfos(conversationID, msg) - if err != nil { - return err - } - err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts) - if err != nil { - prommetrics.MsgOfflinePushFailedCounter.Inc() - return err - } - return nil -} - -func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, err error) { - opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}} - // if msg.ContentType > constant.SignalingNotificationBegin && msg.ContentType < constant.SignalingNotificationEnd { - // req := &sdkws.SignalReq{} - // if err := proto.Unmarshal(msg.Content, req); err != nil { - // return nil, utils.Wrap(err, "") - // } - // switch req.Payload.(type) { - // case *sdkws.SignalReq_Invite, *sdkws.SignalReq_InviteInGroup: - // opts.Signal = &offlinepush.Signal{ClientMsgID: msg.ClientMsgID} - // } - // } - if msg.OfflinePushInfo != nil { - opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount - opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound - opts.Ex = msg.OfflinePushInfo.Ex - } - return opts, nil -} - -func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) { - if p.offlinePusher == nil { - err = errNoOfflinePusher - return - } - - type atContent struct { - Text string `json:"text"` - AtUserList []string `json:"atUserList"` - IsAtSelf bool `json:"isAtSelf"` - } - - opts, err = p.GetOfflinePushOpts(msg) - if err != nil { - return - } - - if msg.OfflinePushInfo != nil { - title = msg.OfflinePushInfo.Title - content = msg.OfflinePushInfo.Desc - } - if title == "" { - switch msg.ContentType { - case constant.Text: - fallthrough - case constant.Picture: - fallthrough - case constant.Voice: - fallthrough - case constant.Video: - fallthrough - case constant.File: - title = constant.ContentType2PushContent[int64(msg.ContentType)] - case constant.AtText: - ac := atContent{} - _ = utils.JsonStringToStruct(string(msg.Content), &ac) - if utils.IsContain(conversationID, ac.AtUserList) { - title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common] - } else { - title = constant.ContentType2PushContent[constant.GroupMsg] - } - case constant.SignalingNotification: - title = constant.ContentType2PushContent[constant.SignalMsg] - default: - title = constant.ContentType2PushContent[constant.Common] - } - } - if content == "" { - content = title - } - return -} diff --git a/internal/push/tools.go b/internal/push/tools.go deleted file mode 100644 index 3242767b1..000000000 --- a/internal/push/tools.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package push - -import ( - "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/protocol/sdkws" - "google.golang.org/protobuf/proto" -) - -func GetContent(msg *sdkws.MsgData) string { - if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd { - var tips sdkws.TipsComm - _ = proto.Unmarshal(msg.Content, &tips) - content := tips.JsonDetail - return content - } else { - return string(msg.Content) - } -} diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 3ba8dd8c0..53332beac 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -114,6 +114,14 @@ func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Con return resp.Conversations, nil } +func (c *ConversationRpcClient) GetConversationOfflinePushUserIDs(ctx context.Context, conversationID string, userIDs []string) ([]string, error) { + resp, err := c.Client.GetConversationOfflinePushUserIDs(ctx, &pbconversation.GetConversationOfflinePushUserIDsReq{ConversationID: conversationID, UserIDs: userIDs}) + if err != nil { + return nil, err + } + return resp.UserIDs, nil +} + func (c *ConversationRpcClient) GetConversations( ctx context.Context, ownerUserID string, From 4266fed44f9f8dc9002634cb6cc6860a7d9f7434 Mon Sep 17 00:00:00 2001 From: withchao Date: Mon, 22 Jan 2024 03:13:27 +0000 Subject: [PATCH 57/66] cicd: robot automated Change --- go.mod | 2 +- go.sum | 4 ++-- internal/msggateway/n_ws_server.go | 3 ++- internal/push/push_rpc_server.go | 3 ++- internal/push/push_to_client.go | 3 ++- internal/rpc/conversation/conversaion.go | 3 ++- internal/rpc/msg/as_read.go | 1 + internal/rpc/msg/seq.go | 1 + internal/rpc/third/s3.go | 6 ++++-- internal/rpc/user/callback.go | 1 + internal/rpc/user/user.go | 6 ++++-- pkg/common/convert/friend.go | 1 + pkg/common/db/controller/user.go | 9 ++++++++- pkg/common/db/mgo/friend.go | 1 + pkg/common/db/mgo/user.go | 13 +++++++++++-- pkg/common/db/table/relation/user.go | 3 ++- .../discoveryregister/kubernetes/kubernetes.go | 4 +++- pkg/rpcclient/msg.go | 1 + 18 files changed, 49 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 4332e389c..739cf6b8e 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/go-redis/redis v6.15.9+incompatible github.com/redis/go-redis/v9 v9.2.1 + github.com/spf13/pflag v1.0.5 github.com/stathat/consistent v1.0.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 go.uber.org/automaxprocs v1.5.3 @@ -116,7 +117,6 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/xid v1.5.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/src-d/gcfg v1.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index d32a689cb..659ff3036 100644 --- a/go.sum +++ b/go.sum @@ -15,11 +15,11 @@ cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/o cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= -github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94 h1:o86vkek41ZrQqoBGqyKvS0z6N0uJj64mpzK72OkDZVM= -github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= +github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw= +github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.23 h1:xozfrGzhbpNPlDTap5DLVPk+JfgZ/ZyIj4Cuu3/bm9w= github.com/OpenIMSDK/tools v0.0.23/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index 7e8129105..01d92b92a 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -19,7 +19,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/OpenIMSDK/tools/apiresp" "net/http" "os" "os/signal" @@ -29,6 +28,8 @@ import ( "syscall" "time" + "github.com/OpenIMSDK/tools/apiresp" + "github.com/go-playground/validator/v10" "github.com/redis/go-redis/v9" "golang.org/x/sync/errgroup" diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 9e66f8f73..188ddc0e1 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -16,9 +16,10 @@ package push import ( "context" - "github.com/OpenIMSDK/tools/utils" "sync" + "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" "github.com/OpenIMSDK/protocol/constant" diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 7cee7b99d..ca9004605 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -18,9 +18,10 @@ import ( "context" "encoding/json" "errors" - "google.golang.org/grpc" "sync" + "google.golang.org/grpc" + "golang.org/x/sync/errgroup" "github.com/OpenIMSDK/protocol/constant" diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index d0d59547c..40803089c 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -17,9 +17,10 @@ package conversation import ( "context" "errors" - "github.com/OpenIMSDK/protocol/sdkws" "sort" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/tx" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index e91e2cf34..cb292421e 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -18,6 +18,7 @@ import ( "context" utils2 "github.com/OpenIMSDK/tools/utils" + cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/redis/go-redis/v9" diff --git a/internal/rpc/msg/seq.go b/internal/rpc/msg/seq.go index c12f258b7..dfc2ad0b1 100644 --- a/internal/rpc/msg/seq.go +++ b/internal/rpc/msg/seq.go @@ -16,6 +16,7 @@ package msg import ( "context" + pbmsg "github.com/OpenIMSDK/protocol/msg" ) diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 2c230f258..3b501d4ad 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -19,12 +19,14 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" - "github.com/google/uuid" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "path" "strconv" "time" + "github.com/google/uuid" + + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/OpenIMSDK/protocol/third" diff --git a/internal/rpc/user/callback.go b/internal/rpc/user/callback.go index 092a66a07..5276946a4 100644 --- a/internal/rpc/user/callback.go +++ b/internal/rpc/user/callback.go @@ -16,6 +16,7 @@ package user import ( "context" + pbuser "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/utils" diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 240bea153..e09c3299a 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -17,12 +17,14 @@ package user import ( "context" "errors" - "github.com/OpenIMSDK/tools/pagination" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "math/rand" "strings" "time" + "github.com/OpenIMSDK/tools/pagination" + + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/OpenIMSDK/tools/tx" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go index 62ce6f95b..27bd595ad 100644 --- a/pkg/common/convert/friend.go +++ b/pkg/common/convert/friend.go @@ -17,6 +17,7 @@ package convert import ( "context" "fmt" + "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/utils" diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index 1a3651076..78ac5a701 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -190,7 +190,14 @@ func (u *userDatabase) Page(ctx context.Context, pagination pagination.Paginatio func (u *userDatabase) PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) { return u.userDB.PageFindUser(ctx, level1, level2, pagination) } -func (u *userDatabase) PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID, nickName string, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) { + +func (u *userDatabase) PageFindUserWithKeyword( + ctx context.Context, + level1 int64, + level2 int64, + userID, nickName string, + pagination pagination.Pagination, +) (count int64, users []*relation.UserModel, err error) { return u.userDB.PageFindUserWithKeyword(ctx, level1, level2, userID, nickName, pagination) } diff --git a/pkg/common/db/mgo/friend.go b/pkg/common/db/mgo/friend.go index b4172d0fb..851db6157 100644 --- a/pkg/common/db/mgo/friend.go +++ b/pkg/common/db/mgo/friend.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/mongo/options" diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index 7eed32634..34a25ed08 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -16,10 +16,11 @@ package mgo import ( "context" + "time" + "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/errs" "go.mongodb.org/mongo-driver/bson/primitive" - "time" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" @@ -89,7 +90,15 @@ func (u *UserMgo) PageFindUser(ctx context.Context, level1 int64, level2 int64, return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination) } -func (u *UserMgo) PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID string, nickName string, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) { + +func (u *UserMgo) PageFindUserWithKeyword( + ctx context.Context, + level1 int64, + level2 int64, + userID string, + nickName string, + pagination pagination.Pagination, +) (count int64, users []*relation.UserModel, err error) { // Initialize the base query with level conditions query := bson.M{ "$and": []bson.M{ diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index 4039257f1..dbb2ff464 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -16,9 +16,10 @@ package relation import ( "context" - "github.com/OpenIMSDK/protocol/user" "time" + "github.com/OpenIMSDK/protocol/user" + "github.com/OpenIMSDK/tools/pagination" ) diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index c10518056..06c58d961 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -18,15 +18,17 @@ import ( "context" "errors" "fmt" - "github.com/stathat/consistent" "os" "strconv" "strings" + "github.com/stathat/consistent" + "google.golang.org/grpc" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index abad0075a..56167d7f4 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -17,6 +17,7 @@ package rpcclient import ( "context" "encoding/json" + "google.golang.org/grpc" "google.golang.org/protobuf/proto" From 119a2c2247a7420b6ecd229835eac0978841e073 Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:14:21 +0800 Subject: [PATCH 58/66] refactor: rename cache. --- pkg/common/redispubsub/redissubscriber.go | 13 +++++++--- pkg/localcache/cache.go | 7 +++--- pkg/localcache/cache_test.go | 2 +- .../{lru_actively.go => lru_expiration.go} | 24 +++++++++---------- .../lru/{lru_inertia.go => lru_lazy.go} | 24 +++++++++---------- .../{lru_inertia_test.go => lru_lazy_test.go} | 2 +- pkg/localcache/option.go | 20 +++++++++------- 7 files changed, 51 insertions(+), 41 deletions(-) rename pkg/localcache/lru/{lru_actively.go => lru_expiration.go} (57%) rename pkg/localcache/lru/{lru_inertia.go => lru_lazy.go} (65%) rename pkg/localcache/lru/{lru_inertia_test.go => lru_lazy_test.go} (96%) diff --git a/pkg/common/redispubsub/redissubscriber.go b/pkg/common/redispubsub/redissubscriber.go index 69cfd8a69..a7029a993 100644 --- a/pkg/common/redispubsub/redissubscriber.go +++ b/pkg/common/redispubsub/redissubscriber.go @@ -16,12 +16,19 @@ func NewSubscriber(client redis.UniversalClient, channel string) *Subscriber { return &Subscriber{client: client, channel: channel} } -func (s *Subscriber) OnMessage(callback func(string)) error { +func (s *Subscriber) OnMessage(ctx context.Context, callback func(string)) error { messageChannel := s.client.Subscribe(ctx, s.channel).Channel() + go func() { - for msg := range messageChannel { - callback(msg.Payload) + for { + select { + case <-ctx.Done(): + return + case msg := <-messageChannel: + callback(msg.Payload) + } } }() + return nil } diff --git a/pkg/localcache/cache.go b/pkg/localcache/cache.go index ed0e16419..4b405b46a 100644 --- a/pkg/localcache/cache.go +++ b/pkg/localcache/cache.go @@ -21,13 +21,14 @@ func New[V any](opts ...Option) Cache[V] { for _, o := range opts { o(opt) } + c := cache[V]{opt: opt} if opt.localSlotNum > 0 && opt.localSlotSize > 0 { createSimpleLRU := func() lru.LRU[string, V] { - if opt.actively { - return lru.NewActivelyLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + if opt.expirationEvict { + return lru.NewExpirationLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) } else { - return lru.NewInertiaLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) + return lru.NewLayLRU[string, V](opt.localSlotSize, opt.localSuccessTTL, opt.localFailedTTL, opt.target, c.onEvict) } } if opt.localSlotNum == 1 { diff --git a/pkg/localcache/cache_test.go b/pkg/localcache/cache_test.go index 90413fd20..c497b7b4a 100644 --- a/pkg/localcache/cache_test.go +++ b/pkg/localcache/cache_test.go @@ -11,7 +11,7 @@ import ( ) func TestName(t *testing.T) { - c := New[string](WithActively()) + c := New[string](WithExpirationEvict()) //c := New[string]() ctx := context.Background() diff --git a/pkg/localcache/lru/lru_actively.go b/pkg/localcache/lru/lru_expiration.go similarity index 57% rename from pkg/localcache/lru/lru_actively.go rename to pkg/localcache/lru/lru_expiration.go index 55fa4d0d1..3cf61f061 100644 --- a/pkg/localcache/lru/lru_actively.go +++ b/pkg/localcache/lru/lru_expiration.go @@ -6,15 +6,15 @@ import ( "time" ) -func NewActivelyLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) LRU[K, V] { - var cb expirable.EvictCallback[K, *activelyLruItem[V]] +func NewExpirationLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) LRU[K, V] { + var cb expirable.EvictCallback[K, *expirationLruItem[V]] if onEvict != nil { - cb = func(key K, value *activelyLruItem[V]) { + cb = func(key K, value *expirationLruItem[V]) { onEvict(key, value.value) } } - core := expirable.NewLRU[K, *activelyLruItem[V]](size, cb, successTTL) - return &activelyLRU[K, V]{ + core := expirable.NewLRU[K, *expirationLruItem[V]](size, cb, successTTL) + return &ExpirationLRU[K, V]{ core: core, successTTL: successTTL, failedTTL: failedTTL, @@ -22,21 +22,21 @@ func NewActivelyLRU[K comparable, V any](size int, successTTL, failedTTL time.Du } } -type activelyLruItem[V any] struct { +type expirationLruItem[V any] struct { lock sync.RWMutex err error value V } -type activelyLRU[K comparable, V any] struct { +type ExpirationLRU[K comparable, V any] struct { lock sync.Mutex - core *expirable.LRU[K, *activelyLruItem[V]] + core *expirable.LRU[K, *expirationLruItem[V]] successTTL time.Duration failedTTL time.Duration target Target } -func (x *activelyLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { +func (x *ExpirationLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { x.lock.Lock() v, ok := x.core.Get(key) if ok { @@ -46,7 +46,7 @@ func (x *activelyLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { defer v.lock.RUnlock() return v.value, v.err } else { - v = &activelyLruItem[V]{} + v = &expirationLruItem[V]{} x.core.Add(key, v) v.lock.Lock() x.lock.Unlock() @@ -62,7 +62,7 @@ func (x *activelyLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { } } -func (x *activelyLRU[K, V]) Del(key K) bool { +func (x *ExpirationLRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() @@ -74,5 +74,5 @@ func (x *activelyLRU[K, V]) Del(key K) bool { return ok } -func (x *activelyLRU[K, V]) Stop() { +func (x *ExpirationLRU[K, V]) Stop() { } diff --git a/pkg/localcache/lru/lru_inertia.go b/pkg/localcache/lru/lru_lazy.go similarity index 65% rename from pkg/localcache/lru/lru_inertia.go rename to pkg/localcache/lru/lru_lazy.go index b1f9f24af..a9270ea4a 100644 --- a/pkg/localcache/lru/lru_inertia.go +++ b/pkg/localcache/lru/lru_lazy.go @@ -6,25 +6,25 @@ import ( "time" ) -type inertiaLruItem[V any] struct { +type layLruItem[V any] struct { lock sync.Mutex expires int64 err error value V } -func NewInertiaLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *InertiaLRU[K, V] { - var cb simplelru.EvictCallback[K, *inertiaLruItem[V]] +func NewLayLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *LayLRU[K, V] { + var cb simplelru.EvictCallback[K, *layLruItem[V]] if onEvict != nil { - cb = func(key K, value *inertiaLruItem[V]) { + cb = func(key K, value *layLruItem[V]) { onEvict(key, value.value) } } - core, err := simplelru.NewLRU[K, *inertiaLruItem[V]](size, cb) + core, err := simplelru.NewLRU[K, *layLruItem[V]](size, cb) if err != nil { panic(err) } - return &InertiaLRU[K, V]{ + return &LayLRU[K, V]{ core: core, successTTL: successTTL, failedTTL: failedTTL, @@ -32,15 +32,15 @@ func NewInertiaLRU[K comparable, V any](size int, successTTL, failedTTL time.Dur } } -type InertiaLRU[K comparable, V any] struct { +type LayLRU[K comparable, V any] struct { lock sync.Mutex - core *simplelru.LRU[K, *inertiaLruItem[V]] + core *simplelru.LRU[K, *layLruItem[V]] successTTL time.Duration failedTTL time.Duration target Target } -func (x *InertiaLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { +func (x *LayLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { x.lock.Lock() v, ok := x.core.Get(key) if ok { @@ -53,7 +53,7 @@ func (x *InertiaLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { return value, err } } else { - v = &inertiaLruItem[V]{} + v = &layLruItem[V]{} x.core.Add(key, v) v.lock.Lock() x.lock.Unlock() @@ -73,7 +73,7 @@ func (x *InertiaLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { return v.value, v.err } -func (x *InertiaLRU[K, V]) Del(key K) bool { +func (x *LayLRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() @@ -85,6 +85,6 @@ func (x *InertiaLRU[K, V]) Del(key K) bool { return ok } -func (x *InertiaLRU[K, V]) Stop() { +func (x *LayLRU[K, V]) Stop() { } diff --git a/pkg/localcache/lru/lru_inertia_test.go b/pkg/localcache/lru/lru_lazy_test.go similarity index 96% rename from pkg/localcache/lru/lru_inertia_test.go rename to pkg/localcache/lru/lru_lazy_test.go index df4919d2d..09fd04cd3 100644 --- a/pkg/localcache/lru/lru_inertia_test.go +++ b/pkg/localcache/lru/lru_lazy_test.go @@ -49,7 +49,7 @@ func TestName(t *testing.T) { h.Write(*(*[]byte)(unsafe.Pointer(&k))) return h.Sum64() }, func() LRU[string, string] { - return NewActivelyLRU[string, string](100, time.Second*60, time.Second, target, nil) + return NewExpirationLRU[string, string](100, time.Second*60, time.Second, target, nil) }) //l := NewInertiaLRU[string, string](1000, time.Second*20, time.Second*5, target) diff --git a/pkg/localcache/option.go b/pkg/localcache/option.go index 17780161a..ecb5da0e6 100644 --- a/pkg/localcache/option.go +++ b/pkg/localcache/option.go @@ -11,7 +11,7 @@ func defaultOption() *option { localSlotNum: 500, localSlotSize: 20000, linkSlotNum: 500, - actively: false, + expirationEvict: false, localSuccessTTL: time.Minute, localFailedTTL: time.Second * 5, delFn: make([]func(ctx context.Context, key ...string), 0, 2), @@ -20,10 +20,12 @@ func defaultOption() *option { } type option struct { - localSlotNum int - localSlotSize int - linkSlotNum int - actively bool + localSlotNum int + localSlotSize int + linkSlotNum int + // expirationEvict: true means that the cache will be actively cleared when the timer expires, + // false means that the cache will be lazily deleted. + expirationEvict bool localSuccessTTL time.Duration localFailedTTL time.Duration delFn []func(ctx context.Context, key ...string) @@ -32,15 +34,15 @@ type option struct { type Option func(o *option) -func WithActively() Option { +func WithExpirationEvict() Option { return func(o *option) { - o.actively = true + o.expirationEvict = true } } -func WithInertia() Option { +func WithLazy() Option { return func(o *option) { - o.actively = false + o.expirationEvict = false } } From b940bcaafeef6709852471e273642456b887bdb7 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Tue, 23 Jan 2024 16:03:22 +0800 Subject: [PATCH 59/66] merge --- internal/push/push_rpc_server.go | 0 internal/push/push_to_client.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 internal/push/push_rpc_server.go delete mode 100644 internal/push/push_to_client.go diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go deleted file mode 100644 index e69de29bb..000000000 From 38e11dc678bce165b4fe0a1baa281dda1efcf752 Mon Sep 17 00:00:00 2001 From: Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:18:00 +0800 Subject: [PATCH 60/66] fix: refactor project dir avoid import cycle. --- internal/push/offlinepush/dummy/push.go | 5 ++--- internal/push/offlinepush/fcm/push.go | 4 ++-- internal/push/offlinepush/getui/push.go | 4 ++-- internal/push/offlinepush/jpush/push.go | 4 ++-- internal/push/offlinepush/offlinepusher.go | 16 ++-------------- internal/push/offlinepush/options/options.go | 14 ++++++++++++++ internal/push/push_handler.go | 5 +++-- 7 files changed, 27 insertions(+), 25 deletions(-) create mode 100644 internal/push/offlinepush/options/options.go diff --git a/internal/push/offlinepush/dummy/push.go b/internal/push/offlinepush/dummy/push.go index 16e5fa747..395c2f45e 100644 --- a/internal/push/offlinepush/dummy/push.go +++ b/internal/push/offlinepush/dummy/push.go @@ -16,8 +16,7 @@ package dummy import ( "context" - - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" ) func NewDummy() *Dummy { @@ -27,6 +26,6 @@ func NewDummy() *Dummy { type Dummy struct { } -func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { +func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { return nil } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 4d88396d1..508c70f55 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -16,6 +16,7 @@ package fcm import ( "context" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "path/filepath" firebase "firebase.google.com/go" @@ -25,7 +26,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) @@ -56,7 +56,7 @@ func NewFcm(cache cache.MsgModel) *Fcm { return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache} } -func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { +func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { // accounts->registrationToken allTokens := make(map[string][]string, 0) for _, account := range userIDs { diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index b94deb0eb..50308f6ab 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -19,6 +19,7 @@ import ( "crypto/sha256" "encoding/hex" "errors" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "strconv" "sync" "time" @@ -30,7 +31,6 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils/splitter" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" @@ -65,7 +65,7 @@ func NewGeTui(cache cache.MsgModel) *GeTui { return &GeTui{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL} } -func (g *GeTui) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { +func (g *GeTui) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { token, err := g.cache.GetGetuiToken(ctx) if err != nil { if errs.Unwrap(err) == redis.Nil { diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index 842d91fcf..f25ff6f4c 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -18,8 +18,8 @@ import ( "context" "encoding/base64" "fmt" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush/body" "github.com/openimsdk/open-im-server/v3/pkg/common/config" http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" @@ -46,7 +46,7 @@ func (j *JPush) getAuthorization(appKey string, masterSecret string) string { return Authorization } -func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { +func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { var pf body.Platform pf.SetAll() var au body.Audience diff --git a/internal/push/offlinepush/offlinepusher.go b/internal/push/offlinepush/offlinepusher.go index 23183024a..83bf8e66e 100644 --- a/internal/push/offlinepush/offlinepusher.go +++ b/internal/push/offlinepush/offlinepusher.go @@ -20,6 +20,7 @@ import ( "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) @@ -32,7 +33,7 @@ const ( // OfflinePusher Offline Pusher. type OfflinePusher interface { - Push(ctx context.Context, userIDs []string, title, content string, opts *Opts) error + Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error } func NewOfflinePusher(cache cache.MsgModel) OfflinePusher { @@ -49,16 +50,3 @@ func NewOfflinePusher(cache cache.MsgModel) OfflinePusher { } return offlinePusher } - -// Opts opts. -type Opts struct { - Signal *Signal - IOSPushSound string - IOSBadgeCount bool - Ex string -} - -// Signal message id. -type Signal struct { - ClientMsgID string -} diff --git a/internal/push/offlinepush/options/options.go b/internal/push/offlinepush/options/options.go new file mode 100644 index 000000000..056f6b711 --- /dev/null +++ b/internal/push/offlinepush/options/options.go @@ -0,0 +1,14 @@ +package options + +// Opts opts. +type Opts struct { + Signal *Signal + IOSPushSound string + IOSBadgeCount bool + Ex string +} + +// Signal message id. +type Signal struct { + ClientMsgID string +} diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index 7ccabb7a5..82de8f250 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -21,6 +21,7 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/mcontext" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/rpccache" @@ -235,14 +236,14 @@ func (c *ConsumerHandler) filterGroupMessageOfflinePush(ctx context.Context, gro return needOfflinePushUserIDs, nil } -func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) { +func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *options.Opts, err error) { type AtTextElem struct { Text string `json:"text,omitempty"` AtUserList []string `json:"atUserList,omitempty"` IsAtSelf bool `json:"isAtSelf"` } - opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}} + opts = &options.Opts{Signal: &options.Signal{}} if msg.OfflinePushInfo != nil { opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound From 24a04797615ceb391c1f7ad646acb4857f397a3b Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Fri, 26 Jan 2024 15:21:28 +0800 Subject: [PATCH 61/66] merge --- internal/push/push_to_client.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 internal/push/push_to_client.go diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go deleted file mode 100644 index e69de29bb..000000000 From 16a9dfba271e8e4ec5bc466e9dd2a8fb2852be2b Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Sun, 28 Jan 2024 16:33:09 +0800 Subject: [PATCH 62/66] feat: conversation FindRecvMsgNotNotifyUserIDs --- pkg/common/db/mgo/conversation.go | 4 ++-- pkg/common/db/table/relation/conversation.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go index bf5d1a145..37c222836 100644 --- a/pkg/common/db/mgo/conversation.go +++ b/pkg/common/db/mgo/conversation.go @@ -96,8 +96,8 @@ func (c *ConversationMgo) FindUserIDAllConversations(ctx context.Context, userID return mgoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": userID}) } -func (c *ConversationMgo) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) { - return mgoutil.Find[string](ctx, c.coll, bson.M{"group_id": groupID, "recv_msg_opt": constant.ReceiveNotNotifyMessage}, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1})) +func (c *ConversationMgo) FindRecvMsgNotNotifyUserIDs(ctx context.Context, conversationID string) ([]string, error) { + return mgoutil.Find[string](ctx, c.coll, bson.M{"conversation_id": conversationID, "recv_msg_opt": constant.ReceiveNotNotifyMessage}, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1})) } func (c *ConversationMgo) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) { diff --git a/pkg/common/db/table/relation/conversation.go b/pkg/common/db/table/relation/conversation.go index e0a5268ca..0fba736f9 100644 --- a/pkg/common/db/table/relation/conversation.go +++ b/pkg/common/db/table/relation/conversation.go @@ -53,7 +53,7 @@ type ConversationModelInterface interface { Take(ctx context.Context, userID, conversationID string) (conversation *ConversationModel, err error) FindConversationID(ctx context.Context, userID string, conversationIDs []string) (existConversationID []string, err error) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*ConversationModel, err error) - FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) + FindRecvMsgNotNotifyUserIDs(ctx context.Context, conversationID string) ([]string, error) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) GetAllConversationIDs(ctx context.Context) ([]string, error) GetAllConversationIDsNumber(ctx context.Context) (int64, error) From 31f28a54abb0c7915ef7bb4943ddcf26e5bea1f2 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Sun, 28 Jan 2024 17:29:46 +0800 Subject: [PATCH 63/66] feat: conversation FindRecvMsgNotNotifyUserIDs --- go.mod | 2 +- go.sum | 6 +++-- internal/push/push_to_client.go | 0 internal/rpc/conversation/conversaion.go | 26 +++++++++++++------- pkg/common/db/cache/config.go | 2 +- pkg/common/db/mgo/conversation.go | 10 ++++++-- pkg/common/db/table/relation/conversation.go | 2 +- pkg/rpccache/conversation.go | 22 +++++++++++++++++ pkg/rpcclient/conversation.go | 8 ++++++ 9 files changed, 62 insertions(+), 16 deletions(-) delete mode 100644 internal/push/push_to_client.go diff --git a/go.mod b/go.mod index 739733d67..d496aac12 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.49 + github.com/OpenIMSDK/protocol v0.0.53 github.com/OpenIMSDK/tools v0.0.29 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 diff --git a/go.sum b/go.sum index b7b40632f..0ce3d0320 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw= -github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/protocol v0.0.49 h1:wcqJOMBis7f153zNI7V82Fc4WyqA1GanMgXUQgL618k= +github.com/OpenIMSDK/protocol v0.0.49/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.29 h1:NS4PEwYl9sX3SWsMjDOLVxMo3LcTWREMr+2cjzWjcqc= github.com/OpenIMSDK/tools v0.0.29/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= @@ -171,6 +171,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 40803089c..2288113c5 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -90,11 +90,11 @@ func (c *conversationServer) GetConversation(ctx context.Context, req *pbconvers return resp, nil } -func (m *conversationServer) GetSortedConversationList(ctx context.Context, req *pbconversation.GetSortedConversationListReq) (resp *pbconversation.GetSortedConversationListResp, err error) { +func (c *conversationServer) GetSortedConversationList(ctx context.Context, req *pbconversation.GetSortedConversationListReq) (resp *pbconversation.GetSortedConversationListResp, err error) { log.ZDebug(ctx, "GetSortedConversationList", "seqs", req, "userID", req.UserID) var conversationIDs []string if len(req.ConversationIDs) == 0 { - conversationIDs, err = m.conversationDatabase.GetConversationIDs(ctx, req.UserID) + conversationIDs, err = c.conversationDatabase.GetConversationIDs(ctx, req.UserID) if err != nil { return nil, err } @@ -102,7 +102,7 @@ func (m *conversationServer) GetSortedConversationList(ctx context.Context, req conversationIDs = req.ConversationIDs } - conversations, err := m.conversationDatabase.FindConversations(ctx, req.UserID, conversationIDs) + conversations, err := c.conversationDatabase.FindConversations(ctx, req.UserID, conversationIDs) if err != nil { return nil, err } @@ -110,22 +110,22 @@ func (m *conversationServer) GetSortedConversationList(ctx context.Context, req return nil, errs.ErrRecordNotFound.Wrap() } - maxSeqs, err := m.msgRpcClient.GetMaxSeqs(ctx, conversationIDs) + maxSeqs, err := c.msgRpcClient.GetMaxSeqs(ctx, conversationIDs) if err != nil { return nil, err } - chatLogs, err := m.msgRpcClient.GetMsgByConversationIDs(ctx, conversationIDs, maxSeqs) + chatLogs, err := c.msgRpcClient.GetMsgByConversationIDs(ctx, conversationIDs, maxSeqs) if err != nil { return nil, err } - conversationMsg, err := m.getConversationInfo(ctx, chatLogs, req.UserID) + conversationMsg, err := c.getConversationInfo(ctx, chatLogs, req.UserID) if err != nil { return nil, err } - hasReadSeqs, err := m.msgRpcClient.GetHasReadSeqs(ctx, req.UserID, conversationIDs) + hasReadSeqs, err := c.msgRpcClient.GetHasReadSeqs(ctx, req.UserID, conversationIDs) if err != nil { return nil, err } @@ -157,8 +157,8 @@ func (m *conversationServer) GetSortedConversationList(ctx context.Context, req UnreadTotal: unreadTotal, } - m.conversationSort(conversation_isPinTime, resp, conversation_unreadCount, conversationMsg) - m.conversationSort(conversation_notPinTime, resp, conversation_unreadCount, conversationMsg) + c.conversationSort(conversation_isPinTime, resp, conversation_unreadCount, conversationMsg) + c.conversationSort(conversation_notPinTime, resp, conversation_unreadCount, conversationMsg) resp.ConversationElems = utils.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber())) return resp, nil @@ -529,3 +529,11 @@ func (c *conversationServer) getConversationInfo( } return conversationMsg, nil } + +func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) { + userIDs, err := c.conversationDatabase.GetConversationNotReceiveMessageUserIDs(ctx, req.ConversationID) + if err != nil { + return nil, err + } + return &pbconversation.GetConversationNotReceiveMessageUserIDsResp{UserIDs: userIDs}, nil +} diff --git a/pkg/common/db/cache/config.go b/pkg/common/db/cache/config.go index 52ece95f7..7599d8a11 100644 --- a/pkg/common/db/cache/config.go +++ b/pkg/common/db/cache/config.go @@ -35,7 +35,7 @@ func getPublishKey(topic string, key []string) []string { }, { Local: config.Config.LocalCache.Conversation, - Keys: []string{cachekey.ConversationIDsKey, cachekey.ConversationKey}, + Keys: []string{cachekey.ConversationKey, cachekey.ConversationIDsKey, cachekey.ConversationNotReceiveMessageUserIDsKey}, }, } subscribe = make(map[string][]string) diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go index 849a78b3d..640c7a3d5 100644 --- a/pkg/common/db/mgo/conversation.go +++ b/pkg/common/db/mgo/conversation.go @@ -96,8 +96,14 @@ func (c *ConversationMgo) FindUserIDAllConversations(ctx context.Context, userID return mgoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": userID}) } -func (c *ConversationMgo) FindRecvMsgNotNotifyUserIDs(ctx context.Context, conversationID string) ([]string, error) { - return mgoutil.Find[string](ctx, c.coll, bson.M{"conversation_id": conversationID, "recv_msg_opt": constant.ReceiveNotNotifyMessage}, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1})) +func (c *ConversationMgo) FindRecvMsgUserIDs(ctx context.Context, conversationID string, recvOpts []int) ([]string, error) { + var filter any + if len(recvOpts) == 0 { + filter = bson.M{"conversation_id": conversationID} + } else { + filter = bson.M{"conversation_id": conversationID, "recv_msg_opt": bson.M{"$in": recvOpts}} + } + return mgoutil.Find[string](ctx, c.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1})) } func (c *ConversationMgo) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) { diff --git a/pkg/common/db/table/relation/conversation.go b/pkg/common/db/table/relation/conversation.go index 0fba736f9..583e41c0f 100644 --- a/pkg/common/db/table/relation/conversation.go +++ b/pkg/common/db/table/relation/conversation.go @@ -53,7 +53,7 @@ type ConversationModelInterface interface { Take(ctx context.Context, userID, conversationID string) (conversation *ConversationModel, err error) FindConversationID(ctx context.Context, userID string, conversationIDs []string) (existConversationID []string, err error) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*ConversationModel, err error) - FindRecvMsgNotNotifyUserIDs(ctx context.Context, conversationID string) ([]string, error) + FindRecvMsgUserIDs(ctx context.Context, conversationID string, recvOpts []int) ([]string, error) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) GetAllConversationIDs(ctx context.Context) ([]string, error) GetAllConversationIDsNumber(ctx context.Context) (int64, error) diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go index 8eadad9d4..ae77b29b7 100644 --- a/pkg/rpccache/conversation.go +++ b/pkg/rpccache/conversation.go @@ -88,3 +88,25 @@ func (c *ConversationLocalCache) GetConversations(ctx context.Context, ownerUser } return conversations, nil } + +func (c *ConversationLocalCache) getConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) (*listMap[string], error) { + return localcache.AnyValue[*listMap[string]](c.local.Get(ctx, cachekey.GetConversationNotReceiveMessageUserIDsKey(conversationID), func(ctx context.Context) (any, error) { + return newListMap(c.client.GetConversationNotReceiveMessageUserIDs(ctx, conversationID)) + })) +} + +func (c *ConversationLocalCache) GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error) { + res, err := c.getConversationNotReceiveMessageUserIDs(ctx, conversationID) + if err != nil { + return nil, err + } + return res.List, nil +} + +func (c *ConversationLocalCache) GetConversationNotReceiveMessageUserIDMap(ctx context.Context, conversationID string) (map[string]struct{}, error) { + res, err := c.getConversationNotReceiveMessageUserIDs(ctx, conversationID) + if err != nil { + return nil, err + } + return res.Map, nil +} diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 53332beac..80053e870 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -139,3 +139,11 @@ func (c *ConversationRpcClient) GetConversations( } return resp.Conversations, nil } + +func (c *ConversationRpcClient) GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error) { + resp, err := c.Client.GetConversationNotReceiveMessageUserIDs(ctx, &pbconversation.GetConversationNotReceiveMessageUserIDsReq{ConversationID: conversationID}) + if err != nil { + return nil, err + } + return resp.UserIDs, nil +} From c40de0b12e0baa614a74e8757468c206aaae9921 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Sun, 28 Jan 2024 17:34:51 +0800 Subject: [PATCH 64/66] feat: conversation FindRecvMsgNotNotifyUserIDs --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 0ce3d0320..81604661d 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.49 h1:wcqJOMBis7f153zNI7V82Fc4WyqA1GanMgXUQgL618k= -github.com/OpenIMSDK/protocol v0.0.49/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/protocol v0.0.53 h1:PtePLTqMYRHjWSf8XIL3x5JRC3YoySTMA6tRKfbUjQY= +github.com/OpenIMSDK/protocol v0.0.53/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.29 h1:NS4PEwYl9sX3SWsMjDOLVxMo3LcTWREMr+2cjzWjcqc= github.com/OpenIMSDK/tools v0.0.29/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= From 90370557e3c84848f595d09bf0cce26d118c2478 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 31 Jan 2024 16:43:41 +0800 Subject: [PATCH 65/66] merge --- internal/push/push_to_client.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 internal/push/push_to_client.go diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go deleted file mode 100644 index e69de29bb..000000000 From d4ed448d7d0c160899e8575e6fb1a932dee3dc16 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Thu, 7 Mar 2024 18:00:58 +0800 Subject: [PATCH 66/66] merge the latest main --- internal/msggateway/hub_server.go | 9 +- internal/push/callback.go | 12 - internal/push/consumer_init.go | 41 ++ internal/push/offlinepush/dummy/push.go | 7 +- internal/push/offlinepush/fcm/push.go | 4 +- internal/push/offlinepush/getui/push.go | 4 +- internal/push/offlinepush/jpush/push.go | 4 +- .../push/offlinepush/offlinepush_interface.go | 37 ++ internal/push/offlinepush/offlinepusher.go | 52 -- internal/push/offlinepush/options/options.go | 14 - internal/push/onlinepusher.go | 211 ------- internal/push/push.go | 51 -- internal/push/push_handler.go | 275 +-------- internal/push/push_rpc_server.go | 108 ++++ internal/push/push_to_client.go | 522 ++++++++++++++++++ internal/push/tools.go | 32 ++ internal/rpc/conversation/conversaion.go | 8 - internal/rpc/friend/friend.go | 7 - internal/rpc/msg/server.go | 3 - internal/rpc/msg/verify.go | 2 +- pkg/common/db/cache/conversation.go | 1 - pkg/common/db/cache/msg.go | 1 - pkg/common/db/localcache/conversation.go | 0 pkg/common/db/localcache/group.go | 0 pkg/rpcclient/group.go | 3 +- 25 files changed, 764 insertions(+), 644 deletions(-) create mode 100644 internal/push/offlinepush/offlinepush_interface.go delete mode 100644 internal/push/offlinepush/offlinepusher.go delete mode 100644 internal/push/offlinepush/options/options.go delete mode 100644 internal/push/onlinepusher.go delete mode 100644 internal/push/push.go create mode 100644 internal/push/tools.go delete mode 100644 pkg/common/db/localcache/conversation.go delete mode 100644 pkg/common/db/localcache/group.go diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index c0d5a5999..146565561 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" - "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" @@ -66,15 +65,13 @@ func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { s.LongConnServer = LongConnServer } -func NewServer(rpcPort int, proPort int, longConnServer LongConnServer) *Server { +func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, conf *config.GlobalConfig) *Server { s := &Server{ -func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, config *config.GlobalConfig) *Server { - return &Server{ rpcPort: rpcPort, prometheusPort: proPort, LongConnServer: longConnServer, pushTerminal: make(map[int]struct{}), - config: config, + config: conf, } s.pushTerminal[constant.IOSPlatformID] = struct{}{} s.pushTerminal[constant.AndroidPlatformID] = struct{}{} @@ -155,7 +152,7 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga } userPlatform := &msggateway.SingleMsgToUserPlatform{ - PlatFormID: int32(client.PlatformID), + RecvPlatFormID: int32(client.PlatformID), } if !client.IsBackground || (client.IsBackground && client.PlatformID != constant.IOSPlatformID) { diff --git a/internal/push/callback.go b/internal/push/callback.go index b8830cf9f..6415d63d6 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -16,7 +16,6 @@ package push import ( "context" - "encoding/json" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" @@ -136,14 +135,3 @@ func callbackBeforeSuperGroupOnlinePush( } return nil } -func GetContent(msg *sdkws.MsgData) string { - if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd { - var notification sdkws.NotificationElem - if err := json.Unmarshal(msg.Content, ¬ification); err != nil { - return "" - } - return notification.Detail - } else { - return string(msg.Content) - } -} diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index e69de29bb..351b63f46 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -0,0 +1,41 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package push + +import ( + "context" + + "github.com/openimsdk/open-im-server/v3/pkg/common/config" +) + +type Consumer struct { + pushCh ConsumerHandler + // successCount is unused + // successCount uint64 +} + +func NewConsumer(config *config.GlobalConfig, pusher *Pusher) (*Consumer, error) { + c, err := NewConsumerHandler(config, pusher) + if err != nil { + return nil, err + } + return &Consumer{ + pushCh: *c, + }, nil +} + +func (c *Consumer) Start() { + go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh) +} diff --git a/internal/push/offlinepush/dummy/push.go b/internal/push/offlinepush/dummy/push.go index 395c2f45e..f147886d9 100644 --- a/internal/push/offlinepush/dummy/push.go +++ b/internal/push/offlinepush/dummy/push.go @@ -16,16 +16,17 @@ package dummy import ( "context" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" + + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" ) -func NewDummy() *Dummy { +func NewClient() *Dummy { return &Dummy{} } type Dummy struct { } -func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { +func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { return nil } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 6882a37c4..ed65a5af6 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -16,14 +16,12 @@ package fcm import ( "context" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "path/filepath" firebase "firebase.google.com/go" "firebase.google.com/go/messaging" "github.com/OpenIMSDK/protocol/constant" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/redis/go-redis/v9" @@ -58,7 +56,7 @@ func NewClient(globalConfig *config.GlobalConfig, cache cache.MsgModel) *Fcm { return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache} } -func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { +func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { // accounts->registrationToken allTokens := make(map[string][]string, 0) for _, account := range userIDs { diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index ba80038f7..67f6292db 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -19,7 +19,6 @@ import ( "crypto/sha256" "encoding/hex" "errors" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "strconv" "sync" "time" @@ -29,7 +28,6 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils/splitter" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" @@ -53,7 +51,7 @@ const ( taskIDTTL = 1000 * 60 * 60 * 24 ) -type GeTui struct { +type Client struct { cache cache.MsgModel tokenExpireTime int64 taskIDTTL int64 diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index 55c4aea39..2ced4bfd3 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -18,8 +18,8 @@ import ( "context" "encoding/base64" "fmt" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush/body" "github.com/openimsdk/open-im-server/v3/pkg/common/config" http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" @@ -48,7 +48,7 @@ func (j *JPush) getAuthorization(appKey string, masterSecret string) string { return Authorization } -func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { +func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { var pf body.Platform pf.SetAll() var au body.Audience diff --git a/internal/push/offlinepush/offlinepush_interface.go b/internal/push/offlinepush/offlinepush_interface.go new file mode 100644 index 000000000..a5d4051f9 --- /dev/null +++ b/internal/push/offlinepush/offlinepush_interface.go @@ -0,0 +1,37 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package offlinepush + +import ( + "context" +) + +// OfflinePusher Offline Pusher. +type OfflinePusher interface { + Push(ctx context.Context, userIDs []string, title, content string, opts *Opts) error +} + +// Opts opts. +type Opts struct { + Signal *Signal + IOSPushSound string + IOSBadgeCount bool + Ex string +} + +// Signal message id. +type Signal struct { + ClientMsgID string +} diff --git a/internal/push/offlinepush/offlinepusher.go b/internal/push/offlinepush/offlinepusher.go deleted file mode 100644 index 83bf8e66e..000000000 --- a/internal/push/offlinepush/offlinepusher.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package offlinepush - -import ( - "context" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" -) - -const ( - GETUI = "getui" - FIREBASE = "fcm" - JPUSH = "jpush" -) - -// OfflinePusher Offline Pusher. -type OfflinePusher interface { - Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error -} - -func NewOfflinePusher(cache cache.MsgModel) OfflinePusher { - var offlinePusher OfflinePusher - switch config.Config.Push.Enable { - case GETUI: - offlinePusher = getui.NewGeTui(cache) - case FIREBASE: - offlinePusher = fcm.NewFcm(cache) - case JPUSH: - offlinePusher = jpush.NewJPush() - default: - offlinePusher = dummy.NewDummy() - } - return offlinePusher -} diff --git a/internal/push/offlinepush/options/options.go b/internal/push/offlinepush/options/options.go deleted file mode 100644 index 056f6b711..000000000 --- a/internal/push/offlinepush/options/options.go +++ /dev/null @@ -1,14 +0,0 @@ -package options - -// Opts opts. -type Opts struct { - Signal *Signal - IOSPushSound string - IOSBadgeCount bool - Ex string -} - -// Signal message id. -type Signal struct { - ClientMsgID string -} diff --git a/internal/push/onlinepusher.go b/internal/push/onlinepusher.go deleted file mode 100644 index 35b9a97b7..000000000 --- a/internal/push/onlinepusher.go +++ /dev/null @@ -1,211 +0,0 @@ -package push - -import ( - "context" - "github.com/OpenIMSDK/protocol/msggateway" - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc" - "os" - "sync" -) - -const ( - ENVNAME = "ENVS_DISCOVERY" - KUBERNETES = "k8s" - ZOOKEEPER = "zookeeper" -) - -type OnlinePusher interface { - GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, - pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) - GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults, - pushToUserIDs *[]string) []string -} - -type emptyOnlinePUsher struct{} - -func newEmptyOnlinePUsher() *emptyOnlinePUsher { - return &emptyOnlinePUsher{} -} - -func (emptyOnlinePUsher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, - pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { - log.ZWarn(ctx, "emptyOnlinePUsher GetConnsAndOnlinePush", nil) - return nil, nil -} -func (u emptyOnlinePUsher) GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, - wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string { - log.ZWarn(ctx, "emptyOnlinePUsher GetOnlinePushFailedUserIDs", nil) - return nil -} - -func NewOnlinePusher(disCov discoveryregistry.SvcDiscoveryRegistry) OnlinePusher { - var envType string - if value := os.Getenv(ENVNAME); value != "" { - envType = os.Getenv(ENVNAME) - } else { - envType = config.Config.Envs.Discovery - } - switch envType { - case KUBERNETES: - return NewK8sStaticConsistentHash(disCov) - case ZOOKEEPER: - return NewDefaultAllNode(disCov) - default: - return newEmptyOnlinePUsher() - } -} - -type DefaultAllNode struct { - disCov discoveryregistry.SvcDiscoveryRegistry -} - -func NewDefaultAllNode(disCov discoveryregistry.SvcDiscoveryRegistry) *DefaultAllNode { - return &DefaultAllNode{disCov: disCov} -} - -func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, - pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { - conns, err := d.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) - log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) - if err != nil { - return nil, err - } - - var ( - mu sync.Mutex - wg = errgroup.Group{} - input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs} - maxWorkers = config.Config.Push.MaxConcurrentWorkers - ) - - if maxWorkers < 3 { - maxWorkers = 3 - } - - wg.SetLimit(maxWorkers) - - // Online push message - for _, conn := range conns { - conn := conn // loop var safe - wg.Go(func() error { - msgClient := msggateway.NewMsgGatewayClient(conn) - reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) - if err != nil { - return nil - } - - log.ZDebug(ctx, "push result", "reply", reply) - if reply != nil && reply.SinglePushResult != nil { - mu.Lock() - wsResults = append(wsResults, reply.SinglePushResult...) - mu.Unlock() - } - - return nil - }) - } - - _ = wg.Wait() - - // always return nil - return wsResults, nil -} - -func (d *DefaultAllNode) GetOnlinePushFailedUserIDs(_ context.Context, msg *sdkws.MsgData, - wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string { - - onlineSuccessUserIDs := []string{msg.SendID} - for _, v := range wsResults { - //message sender do not need offline push - if msg.SendID == v.UserID { - continue - } - // mobile online push success - if v.OnlinePush { - onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID) - } - - } - - return utils.SliceSub(*pushToUserIDs, onlineSuccessUserIDs) -} - -type K8sStaticConsistentHash struct { - disCov discoveryregistry.SvcDiscoveryRegistry -} - -func NewK8sStaticConsistentHash(disCov discoveryregistry.SvcDiscoveryRegistry) *K8sStaticConsistentHash { - return &K8sStaticConsistentHash{disCov: disCov} -} - -func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, - pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { - - var usersHost = make(map[string][]string) - for _, v := range pushToUserIDs { - tHost, err := k.disCov.GetUserIdHashGatewayHost(ctx, v) - if err != nil { - log.ZError(ctx, "get msg gateway hash error", err) - return nil, err - } - tUsers, tbl := usersHost[tHost] - if tbl { - tUsers = append(tUsers, v) - usersHost[tHost] = tUsers - } else { - usersHost[tHost] = []string{v} - } - } - log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) - var usersConns = make(map[*grpc.ClientConn][]string) - for host, userIds := range usersHost { - tconn, _ := k.disCov.GetConn(ctx, host) - usersConns[tconn] = userIds - } - var ( - mu sync.Mutex - wg = errgroup.Group{} - maxWorkers = config.Config.Push.MaxConcurrentWorkers - ) - if maxWorkers < 3 { - maxWorkers = 3 - } - wg.SetLimit(maxWorkers) - for conn, userIds := range usersConns { - tcon := conn - tuserIds := userIds - wg.Go(func() error { - input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds} - msgClient := msggateway.NewMsgGatewayClient(tcon) - reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) - if err != nil { - return nil - } - log.ZDebug(ctx, "push result", "reply", reply) - if reply != nil && reply.SinglePushResult != nil { - mu.Lock() - wsResults = append(wsResults, reply.SinglePushResult...) - mu.Unlock() - } - return nil - }) - } - _ = wg.Wait() - return wsResults, nil -} -func (k *K8sStaticConsistentHash) GetOnlinePushFailedUserIDs(_ context.Context, _ *sdkws.MsgData, - wsResults []*msggateway.SingleMsgToUserResults, _ *[]string) []string { - var needOfflinePushUserIDs []string - for _, v := range wsResults { - if !v.OnlinePush { - needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID) - } - } - return needOfflinePushUserIDs -} diff --git a/internal/push/push.go b/internal/push/push.go deleted file mode 100644 index 90e62ae03..000000000 --- a/internal/push/push.go +++ /dev/null @@ -1,51 +0,0 @@ -package push - -import ( - "context" - pbpush "github.com/OpenIMSDK/protocol/push" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "google.golang.org/grpc" -) - -type pushServer struct { - database controller.PushDatabase - disCov discoveryregistry.SvcDiscoveryRegistry - offlinePusher offlinepush.OfflinePusher - pushCh *ConsumerHandler -} - -func (p pushServer) PushMsg(ctx context.Context, req *pbpush.PushMsgReq) (*pbpush.PushMsgResp, error) { - //todo reserved Interface - return nil, nil -} - -func (p pushServer) DelUserPushToken(ctx context.Context, - req *pbpush.DelUserPushTokenReq) (resp *pbpush.DelUserPushTokenResp, err error) { - if err = p.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil { - return nil, err - } - return &pbpush.DelUserPushTokenResp{}, nil -} - -func Start(disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() - if err != nil { - return err - } - cacheModel := cache.NewMsgCacheModel(rdb) - offlinePusher := offlinepush.NewOfflinePusher(cacheModel) - database := controller.NewPushDatabase(cacheModel) - - consumer := NewConsumerHandler(offlinePusher, rdb, disCov) - pbpush.RegisterPushMsgServiceServer(server, &pushServer{ - database: database, - disCov: disCov, - offlinePusher: offlinePusher, - pushCh: consumer, - }) - go consumer.pushConsumerGroup.RegisterHandleAndConsumer(consumer) - return nil -} diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index 61106e1f1..0e68e76b3 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -16,17 +16,6 @@ package push import ( "context" - "encoding/json" - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/openimsdk/open-im-server/v3/pkg/rpccache" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - "github.com/redis/go-redis/v9" "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" @@ -40,14 +29,8 @@ import ( ) type ConsumerHandler struct { - pushConsumerGroup *kfk.MConsumerGroup - offlinePusher offlinepush.OfflinePusher - onlinePusher OnlinePusher - groupLocalCache *rpccache.GroupLocalCache - conversationLocalCache *rpccache.ConversationLocalCache - msgRpcClient rpcclient.MessageRpcClient - conversationRpcClient rpcclient.ConversationRpcClient - groupRpcClient rpcclient.GroupRpcClient + pushConsumerGroup *kfk.MConsumerGroup + pusher *Pusher } func NewConsumerHandler(config *config.GlobalConfig, pusher *Pusher) (*ConsumerHandler, error) { @@ -98,7 +81,7 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) { var err error switch msgFromMQ.MsgData.SessionType { case constant.SuperGroupChatType: - err = c.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) + err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) default: var pushUserIDList []string isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync) @@ -107,14 +90,18 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) { } else { pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID) } - err = c.Push2User(ctx, pushUserIDList, pbData.MsgData) + err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData) } if err != nil { - log.ZError(ctx, "push failed", err, "msg", pbData.String()) + if err == errNoOfflinePusher { + log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String()) + } else { + log.ZError(ctx, "push failed", err, "msg", pbData.String()) + } } } -func (*ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } -func (*ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } +func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } +func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim, ) error { @@ -125,243 +112,3 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, } return nil } - -// Push2User Suitable for two types of conversations, one is SingleChatType and the other is NotificationChatType. -func (c *ConsumerHandler) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { - log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) - if err := callbackOnlinePush(ctx, userIDs, msg); err != nil { - return err - } - - wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, userIDs) - if err != nil { - return err - } - - log.ZDebug(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs) - - if !c.shouldPushOffline(ctx, msg) { - return nil - } - - for _, v := range wsResults { - //message sender do not need offline push - if msg.SendID == v.UserID { - continue - } - //receiver online push success - if v.OnlinePush { - return nil - } - } - offlinePUshUserID := []string{msg.RecvID} - //receiver offline push - if err = callbackOfflinePush(ctx, offlinePUshUserID, msg, nil); err != nil { - return err - } - - err = c.offlinePushMsg(ctx, msg, offlinePUshUserID) - if err != nil { - return err - } - - return nil -} - -func (c *ConsumerHandler) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { - log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) - var pushToUserIDs []string - if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil { - return err - } - - err = c.groupMessagesHandler(ctx, groupID, &pushToUserIDs, msg) - if err != nil { - return err - } - - wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs) - if err != nil { - return err - } - - log.ZDebug(ctx, "group push result", "result", wsResults, "msg", msg) - - if !c.shouldPushOffline(ctx, msg) { - return nil - } - needOfflinePushUserIDs := c.onlinePusher.GetOnlinePushFailedUserIDs(ctx, msg, wsResults, &pushToUserIDs) - - //filter some user, like don not disturb or don't need offline push etc. - needOfflinePushUserIDs, err = c.filterGroupMessageOfflinePush(ctx, groupID, msg, needOfflinePushUserIDs) - if err != nil { - return err - } - // Use offline push messaging - if len(needOfflinePushUserIDs) > 0 { - var offlinePushUserIDs []string - err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) - if err != nil { - return err - } - - if len(offlinePushUserIDs) > 0 { - needOfflinePushUserIDs = offlinePushUserIDs - } - - err = c.offlinePushMsg(ctx, msg, needOfflinePushUserIDs) - if err != nil { - log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) - return err - } - - } - - return nil -} - -func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData, offlinePushUserIDs []string) error { - title, content, opts, err := c.getOfflinePushInfos(msg) - if err != nil { - return err - } - err = c.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts) - if err != nil { - prommetrics.MsgOfflinePushFailedCounter.Inc() - return err - } - return nil -} - -func (c *ConsumerHandler) filterGroupMessageOfflinePush(ctx context.Context, groupID string, msg *sdkws.MsgData, - offlinePushUserIDs []string) (userIDs []string, err error) { - - //todo local cache Obtain the difference set through local comparison. - needOfflinePushUserIDs, err := c.conversationRpcClient.GetConversationOfflinePushUserIDs( - ctx, utils.GenGroupConversationID(groupID), offlinePushUserIDs) - if err != nil { - return nil, err - } - return needOfflinePushUserIDs, nil -} - -func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *options.Opts, err error) { - type AtTextElem struct { - Text string `json:"text,omitempty"` - AtUserList []string `json:"atUserList,omitempty"` - IsAtSelf bool `json:"isAtSelf"` - } - - opts = &options.Opts{Signal: &options.Signal{}} - if msg.OfflinePushInfo != nil { - opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount - opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound - opts.Ex = msg.OfflinePushInfo.Ex - } - - if msg.OfflinePushInfo != nil { - title = msg.OfflinePushInfo.Title - content = msg.OfflinePushInfo.Desc - } - if title == "" { - switch msg.ContentType { - case constant.Text: - fallthrough - case constant.Picture: - fallthrough - case constant.Voice: - fallthrough - case constant.Video: - fallthrough - case constant.File: - title = constant.ContentType2PushContent[int64(msg.ContentType)] - case constant.AtText: - ac := AtTextElem{} - _ = utils.JsonStringToStruct(string(msg.Content), &ac) - case constant.SignalingNotification: - title = constant.ContentType2PushContent[constant.SignalMsg] - default: - title = constant.ContentType2PushContent[constant.Common] - } - } - if content == "" { - content = title - } - return -} -func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID string, pushToUserIDs *[]string, msg *sdkws.MsgData) (err error) { - if len(*pushToUserIDs) == 0 { - *pushToUserIDs, err = c.groupLocalCache.GetGroupMemberIDs(ctx, groupID) - if err != nil { - return err - } - switch msg.ContentType { - case constant.MemberQuitNotification: - var tips sdkws.MemberQuitTips - if unmarshalNotificationElem(msg.Content, &tips) != nil { - return err - } - if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, []string{tips.QuitUser.UserID}); err != nil { - log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userID", tips.QuitUser.UserID) - } - *pushToUserIDs = append(*pushToUserIDs, tips.QuitUser.UserID) - case constant.MemberKickedNotification: - var tips sdkws.MemberKickedTips - if unmarshalNotificationElem(msg.Content, &tips) != nil { - return err - } - kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }) - if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, kickedUsers); err != nil { - log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", kickedUsers) - } - - *pushToUserIDs = append(*pushToUserIDs, kickedUsers...) - case constant.GroupDismissedNotification: - if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到 - var tips sdkws.GroupDismissedTips - if unmarshalNotificationElem(msg.Content, &tips) != nil { - return err - } - log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(*pushToUserIDs), "list", pushToUserIDs) - if len(config.Config.Manager.UserID) > 0 { - ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0]) - } - defer func(groupID string) { - if err = c.groupRpcClient.DismissGroup(ctx, groupID); err != nil { - log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID) - } - }(groupID) - } - } - } - return err -} - -func (c *ConsumerHandler) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error { - conversationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID) - maxSeq, err := c.msgRpcClient.GetConversationMaxSeq(ctx, conversationID) - if err != nil { - return err - } - return c.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conversationID, maxSeq) -} - -func unmarshalNotificationElem(bytes []byte, t any) error { - var notification sdkws.NotificationElem - if err := json.Unmarshal(bytes, ¬ification); err != nil { - return err - } - - return json.Unmarshal([]byte(notification.Detail), t) -} - -func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgData) bool { - isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - if !isOfflinePush { - return false - } - if msg.ContentType == constant.SignalingNotification { - return false - } - return true -} diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index e69de29bb..d5d4c9242 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -0,0 +1,108 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package push + +import ( + "context" + "github.com/openimsdk/open-im-server/v3/pkg/rpccache" + + "github.com/OpenIMSDK/protocol/constant" + pbpush "github.com/OpenIMSDK/protocol/push" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" +) + +type pushServer struct { + pusher *Pusher + config *config.GlobalConfig +} + +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) + if err != nil { + return err + } + cacheModel := cache.NewMsgCacheModel(rdb, config) + offlinePusher := NewOfflinePusher(config, cacheModel) + database := controller.NewPushDatabase(cacheModel) + groupRpcClient := rpcclient.NewGroupRpcClient(client, config) + conversationRpcClient := rpcclient.NewConversationRpcClient(client, config) + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) + pusher := NewPusher( + config, + client, + offlinePusher, + database, + rpccache.NewGroupLocalCache(groupRpcClient, rdb), + rpccache.NewConversationLocalCache(conversationRpcClient, rdb), + &conversationRpcClient, + &groupRpcClient, + &msgRpcClient, + ) + + pbpush.RegisterPushMsgServiceServer(server, &pushServer{ + pusher: pusher, + config: config, + }) + + consumer, err := NewConsumer(config, pusher) + if err != nil { + return err + } + + consumer.Start() + + return nil +} + +func (r *pushServer) PushMsg(ctx context.Context, pbData *pbpush.PushMsgReq) (resp *pbpush.PushMsgResp, err error) { + switch pbData.MsgData.SessionType { + case constant.SuperGroupChatType: + err = r.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData) + default: + var pushUserIDList []string + isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync) + if !isSenderSync { + pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID) + } else { + pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID) + } + err = r.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData) + } + if err != nil { + if err != errNoOfflinePusher { + return nil, err + } else { + log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String()) + } + } + return &pbpush.PushMsgResp{}, nil +} + +func (r *pushServer) DelUserPushToken( + ctx context.Context, + req *pbpush.DelUserPushTokenReq, +) (resp *pbpush.DelUserPushTokenResp, err error) { + if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil { + return nil, err + } + return &pbpush.DelUserPushTokenResp{}, nil +} diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index e69de29bb..2bf17eaf9 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -0,0 +1,522 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package push + +import ( + "context" + "encoding/json" + "errors" + "github.com/openimsdk/open-im-server/v3/pkg/rpccache" + "sync" + + "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/protocol/conversation" + "github.com/OpenIMSDK/protocol/msggateway" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/mcontext" + "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" +) + +type Pusher struct { + config *config.GlobalConfig + database controller.PushDatabase + discov discoveryregistry.SvcDiscoveryRegistry + offlinePusher offlinepush.OfflinePusher + groupLocalCache *rpccache.GroupLocalCache + conversationLocalCache *rpccache.ConversationLocalCache + msgRpcClient *rpcclient.MessageRpcClient + conversationRpcClient *rpcclient.ConversationRpcClient + groupRpcClient *rpcclient.GroupRpcClient +} + +var errNoOfflinePusher = errors.New("no offlinePusher is configured") + +func NewPusher(config *config.GlobalConfig, discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, + groupLocalCache *rpccache.GroupLocalCache, conversationLocalCache *rpccache.ConversationLocalCache, + conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient, +) *Pusher { + return &Pusher{ + config: config, + discov: discov, + database: database, + offlinePusher: offlinePusher, + groupLocalCache: groupLocalCache, + conversationLocalCache: conversationLocalCache, + msgRpcClient: msgRpcClient, + conversationRpcClient: conversationRpcClient, + groupRpcClient: groupRpcClient, + } +} + +func NewOfflinePusher(config *config.GlobalConfig, cache cache.MsgModel) offlinepush.OfflinePusher { + var offlinePusher offlinepush.OfflinePusher + switch config.Push.Enable { + case "getui": + offlinePusher = getui.NewClient(config, cache) + case "fcm": + offlinePusher = fcm.NewClient(config, cache) + case "jpush": + offlinePusher = jpush.NewClient(config) + default: + offlinePusher = dummy.NewClient() + } + return offlinePusher +} + +func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error { + conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID) + maxSeq, err := p.msgRpcClient.GetConversationMaxSeq(ctx, conevrsationID) + if err != nil { + return err + } + return p.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq) +} + +func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { + log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) + if err := callbackOnlinePush(ctx, p.config, userIDs, msg); err != nil { + return err + } + // push + wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs) + if err != nil { + return err + } + + isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) + log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs) + + if !isOfflinePush { + return nil + } + + if len(wsResults) == 0 { + return nil + } + onlinePushSuccUserIDSet := utils.SliceSet(utils.Filter(wsResults, func(e *msggateway.SingleMsgToUserResults) (string, bool) { + return e.UserID, e.OnlinePush && e.UserID != "" + })) + offlinePushUserIDList := utils.Filter(wsResults, func(e *msggateway.SingleMsgToUserResults) (string, bool) { + _, exist := onlinePushSuccUserIDSet[e.UserID] + return e.UserID, !exist && e.UserID != "" && e.UserID != msg.SendID + }) + + if len(offlinePushUserIDList) > 0 { + if err = callbackOfflinePush(ctx, p.config, offlinePushUserIDList, msg, &[]string{}); err != nil { + return err + } + err = p.offlinePushMsg(ctx, msg.SendID, msg, offlinePushUserIDList) + if err != nil { + return err + } + } + return nil +} + +func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error { + var notification sdkws.NotificationElem + if err := json.Unmarshal(bytes, ¬ification); err != nil { + return err + } + + return json.Unmarshal([]byte(notification.Detail), t) +} + +/* +k8s deployment,offline push group messages function. +*/ +func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error { + + var needOfflinePushUserIDs []string + for _, v := range wsResults { + if !v.OnlinePush { + needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID) + } + } + if len(needOfflinePushUserIDs) > 0 { + var offlinePushUserIDs []string + err := callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs) + if err != nil { + return err + } + + if len(offlinePushUserIDs) > 0 { + needOfflinePushUserIDs = offlinePushUserIDs + } + if msg.ContentType != constant.SignalingNotification { + resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( + ctx, + &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, + ) + if err != nil { + return err + } + if len(resp.UserIDs) > 0 { + err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) + if err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) + return err + } + } + } + + } + return nil +} +func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { + log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) + var pushToUserIDs []string + if err = callbackBeforeSuperGroupOnlinePush(ctx, p.config, groupID, msg, &pushToUserIDs); err != nil { + return err + } + + if len(pushToUserIDs) == 0 { + pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID) + if err != nil { + return err + } + + switch msg.ContentType { + case constant.MemberQuitNotification: + var tips sdkws.MemberQuitTips + if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { + return err + } + defer func(groupID string, userIDs []string) { + if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { + log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) + } + }(groupID, []string{tips.QuitUser.UserID}) + pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID) + case constant.MemberKickedNotification: + var tips sdkws.MemberKickedTips + if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { + return err + } + kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID }) + defer func(groupID string, userIDs []string) { + if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil { + log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs) + } + }(groupID, kickedUsers) + pushToUserIDs = append(pushToUserIDs, kickedUsers...) + case constant.GroupDismissedNotification: + // Messages arrive first, notifications arrive later + if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { + var tips sdkws.GroupDismissedTips + if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { + return err + } + log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs) + if len(p.config.Manager.UserID) > 0 { + ctx = mcontext.WithOpUserIDContext(ctx, p.config.Manager.UserID[0]) + } + if len(p.config.Manager.UserID) == 0 && len(p.config.IMAdmin.UserID) > 0 { + ctx = mcontext.WithOpUserIDContext(ctx, p.config.IMAdmin.UserID[0]) + } + defer func(groupID string) { + if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil { + log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID) + } + }(groupID) + } + } + } + + wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs) + if err != nil { + return err + } + + log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg) + isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) + if isOfflinePush && p.config.Envs.Discovery == "k8s" { + return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults) + } + if isOfflinePush && p.config.Envs.Discovery == "zookeeper" { + var ( + onlineSuccessUserIDs = []string{msg.SendID} + webAndPcBackgroundUserIDs []string + ) + + for _, v := range wsResults { + if v.OnlinePush && v.UserID != msg.SendID { + onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID) + } + + if v.OnlinePush { + continue + } + + if len(v.Resp) == 0 { + continue + } + + for _, singleResult := range v.Resp { + if singleResult.ResultCode != -2 { + continue + } + + isPC := constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC + isWebID := singleResult.RecvPlatFormID == constant.WebPlatformID + + if isPC || isWebID { + webAndPcBackgroundUserIDs = append(webAndPcBackgroundUserIDs, v.UserID) + } + } + } + + needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs) + + // Use offline push messaging + if len(needOfflinePushUserIDs) > 0 { + var offlinePushUserIDs []string + err = callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs) + if err != nil { + return err + } + + if len(offlinePushUserIDs) > 0 { + needOfflinePushUserIDs = offlinePushUserIDs + } + if msg.ContentType != constant.SignalingNotification { + resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs( + ctx, + &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs}, + ) + if err != nil { + return err + } + if len(resp.UserIDs) > 0 { + err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs) + if err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg) + return err + } + if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil { + log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs)) + return err + } + } + } + + } + } + return nil +} + +func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + var usersHost = make(map[string][]string) + for _, v := range pushToUserIDs { + tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v) + if err != nil { + log.ZError(ctx, "get msggateway hash error", err) + return nil, err + } + tUsers, tbl := usersHost[tHost] + if tbl { + tUsers = append(tUsers, v) + usersHost[tHost] = tUsers + } else { + usersHost[tHost] = []string{v} + } + } + log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) + var usersConns = make(map[*grpc.ClientConn][]string) + for host, userIds := range usersHost { + tconn, _ := p.discov.GetConn(ctx, host) + usersConns[tconn] = userIds + } + var ( + mu sync.Mutex + wg = errgroup.Group{} + maxWorkers = p.config.Push.MaxConcurrentWorkers + ) + if maxWorkers < 3 { + maxWorkers = 3 + } + wg.SetLimit(maxWorkers) + for conn, userIds := range usersConns { + tcon := conn + tuserIds := userIds + wg.Go(func() error { + input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds} + msgClient := msggateway.NewMsgGatewayClient(tcon) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) + if err != nil { + return nil + } + log.ZDebug(ctx, "push result", "reply", reply) + if reply != nil && reply.SinglePushResult != nil { + mu.Lock() + wsResults = append(wsResults, reply.SinglePushResult...) + mu.Unlock() + } + return nil + }) + } + _ = wg.Wait() + return wsResults, nil +} +func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { + if p.config.Envs.Discovery == "k8s" { + return p.k8sOnlinePush(ctx, msg, pushToUserIDs) + } + conns, err := p.discov.GetConns(ctx, p.config.RpcRegisterName.OpenImMessageGatewayName) + log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) + if err != nil { + return nil, err + } + + var ( + mu sync.Mutex + wg = errgroup.Group{} + input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs} + maxWorkers = p.config.Push.MaxConcurrentWorkers + ) + + if maxWorkers < 3 { + maxWorkers = 3 + } + + wg.SetLimit(maxWorkers) + + // Online push message + for _, conn := range conns { + conn := conn // loop var safe + wg.Go(func() error { + msgClient := msggateway.NewMsgGatewayClient(conn) + reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input) + if err != nil { + return nil + } + + log.ZDebug(ctx, "push result", "reply", reply) + if reply != nil && reply.SinglePushResult != nil { + mu.Lock() + wsResults = append(wsResults, reply.SinglePushResult...) + mu.Unlock() + } + + return nil + }) + } + + _ = wg.Wait() + + // always return nil + return wsResults, nil +} + +func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error { + title, content, opts, err := p.getOfflinePushInfos(conversationID, msg) + if err != nil { + return err + } + err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts) + if err != nil { + prommetrics.MsgOfflinePushFailedCounter.Inc() + return err + } + return nil +} + +func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, err error) { + opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}} + // if msg.ContentType > constant.SignalingNotificationBegin && msg.ContentType < constant.SignalingNotificationEnd { + // req := &sdkws.SignalReq{} + // if err := proto.Unmarshal(msg.Content, req); err != nil { + // return nil, utils.Wrap(err, "") + // } + // switch req.Payload.(type) { + // case *sdkws.SignalReq_Invite, *sdkws.SignalReq_InviteInGroup: + // opts.Signal = &offlinepush.Signal{ClientMsgID: msg.ClientMsgID} + // } + // } + if msg.OfflinePushInfo != nil { + opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount + opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound + opts.Ex = msg.OfflinePushInfo.Ex + } + return opts, nil +} + +func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) { + if p.offlinePusher == nil { + err = errNoOfflinePusher + return + } + + type atContent struct { + Text string `json:"text"` + AtUserList []string `json:"atUserList"` + IsAtSelf bool `json:"isAtSelf"` + } + + opts, err = p.GetOfflinePushOpts(msg) + if err != nil { + return + } + + if msg.OfflinePushInfo != nil { + title = msg.OfflinePushInfo.Title + content = msg.OfflinePushInfo.Desc + } + if title == "" { + switch msg.ContentType { + case constant.Text: + fallthrough + case constant.Picture: + fallthrough + case constant.Voice: + fallthrough + case constant.Video: + fallthrough + case constant.File: + title = constant.ContentType2PushContent[int64(msg.ContentType)] + case constant.AtText: + ac := atContent{} + _ = utils.JsonStringToStruct(string(msg.Content), &ac) + if utils.IsContain(conversationID, ac.AtUserList) { + title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common] + } else { + title = constant.ContentType2PushContent[constant.GroupMsg] + } + case constant.SignalingNotification: + title = constant.ContentType2PushContent[constant.SignalMsg] + default: + title = constant.ContentType2PushContent[constant.Common] + } + } + if content == "" { + content = title + } + return +} diff --git a/internal/push/tools.go b/internal/push/tools.go new file mode 100644 index 000000000..3242767b1 --- /dev/null +++ b/internal/push/tools.go @@ -0,0 +1,32 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package push + +import ( + "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/protocol/sdkws" + "google.golang.org/protobuf/proto" +) + +func GetContent(msg *sdkws.MsgData) string { + if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd { + var tips sdkws.TipsComm + _ = proto.Unmarshal(msg.Content, &tips) + content := tips.JsonDetail + return content + } else { + return string(msg.Content) + } +} diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 9ae32cd8e..81b4d9d45 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -48,14 +48,6 @@ type conversationServer struct { config *config.GlobalConfig } -func (c *conversationServer) GetConversationNotReceiveMessageUserIDs( - ctx context.Context, - req *pbconversation.GetConversationNotReceiveMessageUserIDsReq, -) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) { - //TODO implement me - panic("implement me") -} - func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { rdb, err := cache.NewRedis(config) if err != nil { diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 75a3bc1d8..9466c5224 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -31,23 +31,16 @@ import ( "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" - "github.com/OpenIMSDK/protocol/sdkws" registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" - "google.golang.org/grpc" ) type friendServer struct { diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 726cc3420..6212dea03 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -15,11 +15,8 @@ package msg import ( - "context" "github.com/openimsdk/open-im-server/v3/pkg/rpccache" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msg" diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index b964eca9b..318464cf8 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -67,7 +67,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if black { return errs.ErrBlockedByPeer.Wrap() } - if *config.Config.MessageVerify.FriendVerify { + if m.config.MessageVerify.FriendVerify != nil && *m.config.MessageVerify.FriendVerify { friend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { return err diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index 8aee6ded3..ef725642c 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -16,7 +16,6 @@ package cache import ( "context" - "errors" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 00f715785..eb79fdb70 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -17,7 +17,6 @@ package cache import ( "context" "errors" - "github.com/dtm-labs/rockscache" "strconv" "time" diff --git a/pkg/common/db/localcache/conversation.go b/pkg/common/db/localcache/conversation.go deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/common/db/localcache/group.go b/pkg/common/db/localcache/group.go deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index 607ea74ed..ec7aab695 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -26,7 +26,6 @@ import ( "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - "google.golang.org/grpc" ) type Group struct { @@ -41,7 +40,7 @@ func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry, config *config.Glob util.ExitWithError(err) } client := group.NewGroupClient(conn) - return &Group{discov: discov, conn: conn, Client: client, Config: config} + return &Group{discov: discov, Client: client, Config: config} } type GroupRpcClient Group