此si-mqtt提供了一种比较开放的集群消息处理方式:
集群消息发送只开放一个方法,负责发送publish、subscribe、unsubscribe msg.
// SendMsgToCluster 发送消息到集群
// shareName 共享主题组
// targetNode 目标节点
// 这两个参数用于集群共享主题消息发送到特定的节点
func SendMsgToCluster(msg messagev5.Message, shareName, targetNode string, allSuccess func(message messagev5.Message),
oneNodeSendSucFunc func(name string, message messagev5.Message),
oneNodeSendFailFunc func(name string, message messagev5.Message)) {
if sender == nil {
logger.Logger.Warnf("sender is nil")
return
}
if targetNode != "" { // 单个发送,可能是共享消息
sender.SendOneNode(msg, shareName, targetNode, oneNodeSendSucFunc, oneNodeSendFailFunc)
return
}
// 发送全部节点
sender.SendAllNode(msg, allSuccess, oneNodeSendSucFunc, oneNodeSendFailFunc)
}
而真正的消息发送只需实现一个接口接口并注册上去即可:
// Sender 只要实现此接口就可以通过SendMsgToCluster(...)方法发送集群消息
type Sender interface {
// SendOneNode 主要是用来发送共享主题消息
SendOneNode(msg messagev5.Message, shareName, targetNode string,
oneNodeSendSucFunc func(name string, message messagev5.Message),
oneNodeSendFailFunc func(name string, message messagev5.Message))
// SendAllNode 发送除共享主题消息外的消息
SendAllNode(msg messagev5.Message, allSuccess func(message messagev5.Message),
oneNodeSendSucFunc func(name string, message messagev5.Message),
oneNodeSendFailFunc func(name string, message messagev5.Message))
}
此mqtt broker目前提供了两种sender的实现,一种简单的直接发送数据到其它节点,一种通过存储组件实现【它只用了mongo,因为简单】
我们来看一下后者
client: 消息发送
func (this *dbSender) SendOneNode(msg messagev5.Message,
shareName, targetNode string,
oneNodeSendSucFunc func(name string, message messagev5.Message),
oneNodeSendFailFunc func(name string, message messagev5.Message)) {
var e error
switch msg := msg.(type) {
case *messagev5.PublishMessage:
if targetNode != "" && shareName != "" {
e = this.c.SaveSharePub(context.TODO(), "cluster_msg", targetNode, shareName, msg)
} else {
e = this.c.SavePub(context.TODO(), "cluster_msg", msg)
}
case *messagev5.SubscribeMessage:
e = this.c.SaveSub(context.TODO(), "cluster_msg", msg)
case *messagev5.UnsubscribeMessage:
e = this.c.SaveUnSub(context.TODO(), "cluster_msg", msg)
}
if e != nil {
go oneNodeSendSucFunc(targetNode, msg)
} else {
go oneNodeSendFailFunc(targetNode, msg)
}
}
func (this *dbSender) SendAllNode(msg messagev5.Message,
allSuccess func(message messagev5.Message),
oneNodeSendSucFunc func(name string, message messagev5.Message),
oneNodeSendFailFunc func(name string, message messagev5.Message)) {
var e error
switch msg := msg.(type) {
case *messagev5.PublishMessage:
e = this.c.SavePub(context.TODO(), "cluster_msg", msg)
case *messagev5.SubscribeMessage:
e = this.c.SaveSub(context.TODO(), "cluster_msg", msg)
case *messagev5.UnsubscribeMessage:
e = this.c.SaveUnSub(context.TODO(), "cluster_msg", msg)
}
if e != nil {
go allSuccess(msg)
go oneNodeSendSucFunc(colong.AllNodeName, msg)
} else {
go oneNodeSendFailFunc(colong.AllNodeName, msg)
}
}
server: 消息消费
// period 获取数据周期,单位ms
// size 每次获取数据量
func (s *dbRcv) run(period, size int64) {
go func() {
for {
select {
case <-time.After(time.Duration(period) * time.Millisecond):
case <-s.stop:
return
}
ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
go func() {
defer func() {
if e := recover(); e != nil {
println(e)
}
cancel()
}()
select {
case <-time.After(10 * time.Second):
cancel()
case <-ctx.Done():
}
}()
msg, err := s.c.GetBatch(ctx, "cluster_msg", size)
if err != nil {
cancel()
println(err)
}
for k := 0; k < len(msg); k++ {
mg := msg[k]
if mg.IsSub() {
s.submit(func() {
tpk := mg.Sub.topic
node := mg.Sender
for i := 0; i < len(tpk); i++ {
// 解析share name
shareName, top := shareTopic([]byte(tpk[i]))
if shareName != "" {
err = s.shareTopicMapNode.AddTopicMapNode(top, shareName, node)
if err != nil {
logger.Logger.Errorf("%s,共享订阅节点新增失败, shareName:%v , err: %v", node, shareName, err)
} else {
logger.Logger.Debugf("收到节点:%s 发来的 共享订阅:topic-%s, shareName-%s", node, top, shareName)
}
} else {
logger.Logger.Warnf("收到非法订阅:%s", tpk[i])
}
}
})
} else if mg.IsUnSub() {
s.submit(func() {
tpk := mg.Sub.topic
node := mg.Sender
for i := 0; i < len(tpk); i++ {
// 解析share name
shareName, top := shareTopic([]byte(tpk[i]))
if shareName != "" {
err = s.shareTopicMapNode.RemoveTopicMapNode(top, shareName, node)
if err != nil {
logger.Logger.Errorf("%s,共享订阅节点减少失败, shareName:%v , err: %v", node, shareName, err)
} else {
logger.Logger.Debugf("收到节点:%s 发来的 取消共享订阅:topic-%s, shareName-%s", node, top, shareName)
}
} else {
logger.Logger.Warnf("收到非法取消订阅:%s", string(tpk[i]))
}
}
})
} else if mg.IsShare() {
s.submit(func() {
msg1 := poToVo(mg.Msg)
err = s.clusterInToPubShare(msg1, mg.ShareName)
if err != nil {
logger.Logger.Errorf("clusterInToPubShare: err %v", err)
} else {
logger.Logger.Debugf("收到节点:%s 发来的 共享消息:%s", mg.Sender, msg1)
}
})
} else if mg.IsPub() {
s.submit(func() {
msg1 := poToVo(mg.Msg)
err = s.clusterInToPub(msg1)
if err != nil {
logger.Logger.Errorf("clusterInToPub: err %v", err)
} else {
logger.Logger.Debugf("收到节点:%s 发来的 普通消息:%s", mg.Sender, msg1)
}
})
} else {
logger.Logger.Infof("OnMessage: %+v", mg)
}
}
}
}()
}
当然,此si-mqtt的处理还很毛糙,有比较大的优化空间。