mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-07-06 20:03:23 +08:00
* fix: performance issues with Kafka caused by encapsulating the MQ interface * fix: admin token in standalone mode * fix: full id version * fix: resolve deadlock in cache eviction and improve GetBatch implementation * refactor: replace LongConn with ClientConn interface and simplify message handling * refactor: replace LongConn with ClientConn interface and simplify message handling * fix: seq use $setOnInsert for min_seq in conversation update * feat: add error code for handled friend requests and improve error handling in friend operations * refactor(msg): update regex pattern for conversationID to include a trailing colon * refactor: streamline cache initialization and enhance queue engine configuration * refactor: streamline cache initialization and enhance queue engine configuration * feat: add RegisterIP to API config and implement Redis server registration * feat(redis): implement standalone gateway registration with Redis * chore: update openimsdk/tools dependency to v0.0.50-alpha.121 * fix: change timer to ticker for improved periodic execution * refactor: replace Kafka topic configuration with mqbuild constants for improved readability * feat(redis): implement Redis-based locking mechanism for cron tasks
174 lines
5.1 KiB
Go
174 lines
5.1 KiB
Go
package mqbuild
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
|
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
|
"github.com/openimsdk/tools/mq"
|
|
"github.com/openimsdk/tools/mq/kafka"
|
|
"github.com/openimsdk/tools/mq/redismq"
|
|
"github.com/openimsdk/tools/mq/simmq"
|
|
)
|
|
|
|
type Builder interface {
|
|
GetTopicProducer(ctx context.Context, topic string) (mq.Producer, error)
|
|
GetTopicConsumer(ctx context.Context, topic string) (mq.Consumer, error)
|
|
}
|
|
|
|
const (
|
|
TopicToRedis = "toRedis"
|
|
TopicToMongo = "toMongo"
|
|
TopicToPush = "toPush"
|
|
TopicToOfflinePush = "toOfflinePush"
|
|
)
|
|
|
|
const (
|
|
GroupRedis = "redis"
|
|
GroupMongo = "mongo"
|
|
GroupPush = "push"
|
|
GroupOfflinePush = "offlinePush"
|
|
)
|
|
|
|
func NewBuilder(queue config.EngineSelector, kafka *config.Kafka, redis redis.UniversalClient) (Builder, error) {
|
|
engine, err := config.ValidateQueueEngine(queue.Engine, config.Standalone())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch engine {
|
|
case config.QueueEngineKafka:
|
|
if kafka == nil {
|
|
return nil, fmt.Errorf("nil kafka config")
|
|
}
|
|
return newKafkaBuilder(kafka), nil
|
|
case config.QueueEngineRedis:
|
|
return newRedisBuilder(redis), nil
|
|
case config.QueueEngineMemory:
|
|
return standaloneBuilder{}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported queue engine %s", queue.Engine)
|
|
}
|
|
}
|
|
|
|
func newKafkaBuilder(kafka *config.Kafka) Builder {
|
|
topics := MergeTopics(KafkaTopics(kafka))
|
|
return &kafkaBuilder{
|
|
addr: kafka.Address,
|
|
config: kafka.Build(),
|
|
logicalTopic: LogicalTopicNames(topics),
|
|
topicGroupID: TopicGroupID(topics),
|
|
}
|
|
}
|
|
|
|
func newRedisBuilder(redis redis.UniversalClient) Builder {
|
|
return redismq.NewBuilder(redis, TopicGroupID(DefaultTopics()), redismq.Config{StreamPrefix: "mq:"})
|
|
}
|
|
|
|
type QueueTopicsConfig struct {
|
|
ToRedis QueueTopicConfig
|
|
ToMongo QueueTopicConfig
|
|
ToPush QueueTopicConfig
|
|
ToOfflinePush QueueTopicConfig
|
|
}
|
|
|
|
type QueueTopicConfig struct {
|
|
Topic string
|
|
GroupID string
|
|
}
|
|
|
|
func DefaultTopics() QueueTopicsConfig {
|
|
return QueueTopicsConfig{
|
|
ToRedis: QueueTopicConfig{Topic: TopicToRedis, GroupID: GroupRedis},
|
|
ToMongo: QueueTopicConfig{Topic: TopicToMongo, GroupID: GroupMongo},
|
|
ToPush: QueueTopicConfig{Topic: TopicToPush, GroupID: GroupPush},
|
|
ToOfflinePush: QueueTopicConfig{Topic: TopicToOfflinePush, GroupID: GroupOfflinePush},
|
|
}
|
|
}
|
|
|
|
func KafkaTopics(kafka *config.Kafka) QueueTopicsConfig {
|
|
if kafka == nil {
|
|
return QueueTopicsConfig{}
|
|
}
|
|
return QueueTopicsConfig{
|
|
ToRedis: QueueTopicConfig{Topic: kafka.ToRedisTopic, GroupID: kafka.ToRedisGroupID},
|
|
ToMongo: QueueTopicConfig{Topic: kafka.ToMongoTopic, GroupID: kafka.ToMongoGroupID},
|
|
ToPush: QueueTopicConfig{Topic: kafka.ToPushTopic, GroupID: kafka.ToPushGroupID},
|
|
ToOfflinePush: QueueTopicConfig{Topic: kafka.ToOfflinePushTopic, GroupID: kafka.ToOfflineGroupID},
|
|
}
|
|
}
|
|
|
|
func MergeTopics(topics QueueTopicsConfig) QueueTopicsConfig {
|
|
defaults := DefaultTopics()
|
|
fillTopic(&topics.ToRedis, defaults.ToRedis)
|
|
fillTopic(&topics.ToMongo, defaults.ToMongo)
|
|
fillTopic(&topics.ToPush, defaults.ToPush)
|
|
fillTopic(&topics.ToOfflinePush, defaults.ToOfflinePush)
|
|
return topics
|
|
}
|
|
|
|
func fillTopic(topic *QueueTopicConfig, defaultTopic QueueTopicConfig) {
|
|
if topic.Topic == "" {
|
|
topic.Topic = defaultTopic.Topic
|
|
}
|
|
if topic.GroupID == "" {
|
|
topic.GroupID = defaultTopic.GroupID
|
|
}
|
|
}
|
|
|
|
func LogicalTopicNames(topics QueueTopicsConfig) map[string]string {
|
|
return map[string]string{
|
|
TopicToRedis: topics.ToRedis.Topic,
|
|
TopicToMongo: topics.ToMongo.Topic,
|
|
TopicToPush: topics.ToPush.Topic,
|
|
TopicToOfflinePush: topics.ToOfflinePush.Topic,
|
|
}
|
|
}
|
|
|
|
func TopicGroupID(topics QueueTopicsConfig) map[string]string {
|
|
return map[string]string{
|
|
topics.ToRedis.Topic: topics.ToRedis.GroupID,
|
|
topics.ToMongo.Topic: topics.ToMongo.GroupID,
|
|
topics.ToPush.Topic: topics.ToPush.GroupID,
|
|
topics.ToOfflinePush.Topic: topics.ToOfflinePush.GroupID,
|
|
}
|
|
}
|
|
|
|
type standaloneBuilder struct{}
|
|
|
|
func (standaloneBuilder) GetTopicProducer(ctx context.Context, topic string) (mq.Producer, error) {
|
|
return simmq.GetTopicProducer(topic), nil
|
|
}
|
|
|
|
func (standaloneBuilder) GetTopicConsumer(ctx context.Context, topic string) (mq.Consumer, error) {
|
|
return simmq.GetTopicConsumer(topic), nil
|
|
}
|
|
|
|
type kafkaBuilder struct {
|
|
addr []string
|
|
config *kafka.Config
|
|
logicalTopic map[string]string
|
|
topicGroupID map[string]string
|
|
}
|
|
|
|
func (x *kafkaBuilder) GetTopicProducer(ctx context.Context, topic string) (mq.Producer, error) {
|
|
realTopic, ok := x.logicalTopic[topic]
|
|
if !ok {
|
|
return nil, fmt.Errorf("topic %s not found", topic)
|
|
}
|
|
return kafka.NewKafkaProducerV2(x.config, x.addr, realTopic)
|
|
}
|
|
|
|
func (x *kafkaBuilder) GetTopicConsumer(ctx context.Context, topic string) (mq.Consumer, error) {
|
|
realTopic, ok := x.logicalTopic[topic]
|
|
if !ok {
|
|
return nil, fmt.Errorf("topic %s not found", topic)
|
|
}
|
|
groupID, ok := x.topicGroupID[realTopic]
|
|
if !ok {
|
|
return nil, fmt.Errorf("topic %s groupID not found", realTopic)
|
|
}
|
|
return kafka.NewMConsumerGroupV2(ctx, x.config, groupID, []string{realTopic}, true)
|
|
}
|