feat: add redis cache and fanout support
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/tinode/chat/server/logs"
|
||||
"github.com/tinode/chat/server/store"
|
||||
"github.com/tinode/chat/server/store/types"
|
||||
)
|
||||
|
||||
func startRedisGroupFanout(ctx context.Context) {
|
||||
if err := store.StartGroupFanoutSubscriber(ctx, currentNodeID(), func(raw []byte) {
|
||||
var msg ServerComMessage
|
||||
if err := json.Unmarshal(raw, &msg); err != nil {
|
||||
logs.Warn.Println("redis group fanout: malformed message", err)
|
||||
return
|
||||
}
|
||||
if !msg.TenantID.IsValid() || msg.RcptTo == "" || types.GetTopicCat(msg.RcptTo) != types.TopicCatGrp {
|
||||
return
|
||||
}
|
||||
msg.sess = nil
|
||||
deliverRedisGroupFanout(&msg)
|
||||
}); err != nil {
|
||||
logs.Warn.Println("redis group fanout: subscriber disabled", err)
|
||||
}
|
||||
}
|
||||
|
||||
func deliverRedisGroupFanout(msg *ServerComMessage) {
|
||||
key := types.TopicKey{TenantID: msg.TenantID, Topic: msg.RcptTo}
|
||||
if globals.hub.topicGet(key) != nil {
|
||||
select {
|
||||
case globals.hub.routeSrv <- msg:
|
||||
default:
|
||||
logs.Err.Println("redis group fanout: hub routeSrv queue full", msg.RcptTo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
delivered := 0
|
||||
localMembers := redisGroupFanoutMembers(msg.TenantID, msg.RcptTo)
|
||||
globals.sessionStore.Range(func(_ string, sess *Session) bool {
|
||||
if sess == nil || sess.isCluster() || sess.isProxy() || sess.tenantID != msg.TenantID || sess.sid == msg.SkipSid {
|
||||
return true
|
||||
}
|
||||
_, listedOnline := localMembers[sess.uid.UserId()]
|
||||
if !listedOnline && !sess.hasSub(msg.RcptTo) {
|
||||
return true
|
||||
}
|
||||
|
||||
msgCopy := msg.copy()
|
||||
prepareRedisGroupFanoutMessage(msgCopy, msg.RcptTo)
|
||||
if sess.queueOut(msgCopy) {
|
||||
delivered++
|
||||
} else {
|
||||
logs.Warn.Printf("redis group fanout: session queue full, sid=%s topic=%s", sess.sid, msg.RcptTo)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if delivered == 0 {
|
||||
logs.Info.Println("redis group fanout: no local subscribers for unloaded topic", msg.RcptTo)
|
||||
}
|
||||
}
|
||||
|
||||
func redisGroupFanoutMembers(tenantID types.TenantID, topic string) map[string]struct{} {
|
||||
uids, err := store.GetTopicNodeOnlineMembers(tenantID, topic, currentNodeID())
|
||||
if err != nil {
|
||||
logs.Warn.Println("redis group fanout: failed to read local online members", topic, err)
|
||||
return nil
|
||||
}
|
||||
members := make(map[string]struct{}, len(uids))
|
||||
for _, uid := range uids {
|
||||
members[uid.UserId()] = struct{}{}
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
func prepareRedisGroupFanoutMessage(msg *ServerComMessage, topic string) {
|
||||
switch {
|
||||
case msg.Data != nil && msg.Data.Topic == "":
|
||||
msg.Data.Topic = topic
|
||||
case msg.Pres != nil && msg.Pres.Topic == "":
|
||||
msg.Pres.Topic = topic
|
||||
case msg.Info != nil && msg.Info.Topic == "":
|
||||
msg.Info.Topic = topic
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user