总体框架:
实现UML设计:
目前实现了同步,异步也已实现;总感觉异步不太靠谱 ;是不是还要通知客户端消息处理结果ok!
消息适配器模式:
主协程收到的消息分发到10个channel, 然后有10个协程消费消息。如果处理时间长,10消息的业务处理又可以启动新的协程,保证高并发。channel是什么,队列,多协程安全。
同步流程是消息返回响应,异步流程publish只发不收,要观察测试查看日志表。
领域事件处理框架
主消息流程框架:
开源地址:https://gitee.com/ichub/gonats.git
操作步骤
启动服务:
package main import ( "context" "gitee.com/ichub/gonats/general/gonats/goframe/sync/serversync" "gitee.com/ichub/gonats/general/goutil" ) func main() { goutil.FindBeanGoLog().Info(context.Background(), "starting serverSync....") serversync.FindBeanServerSync().StartServer() serversync.FindBeanServerSync().CloseChannels() }
执行用例
func Test002_client(t *testing.T) { var i = 0 for i = 0; i < 110; i++ { var msg = gomsg.FindBeanGonatsMsg() var ii = 1 + 100000 + i msg.Header.Id = "abc" + gconv.String(100000+ii) msg.Data = []byte("abc" + gconv.String(ii)) var resp, err = clisync.FindBeanCliSync().Request(msg) if err != nil { logrus.Error(err) continue } logrus.Info("resp=", jsonutils.ToJsonPretty(resp)) } }
执行结果:
024-05-01 10:16:53.186 [INFO] go handle Response gonatsmsg: { "nats_header": { "topic": "GeneralSyncEs", "action": "", "domain": "general", "msg_id": "efbb31f2-aa32-4d81-89ae-f24faa69ad69", "seq_id": "", "id": "abc200110 202929292", "msg_type": "GeneralSyncEs" }, "data": "YWJjMTAwMTEw" }
代码解析:
package serversync import ( "context" "gitee.com/ichub/goconfig/common/base/basedto" "gitee.com/ichub/goconfig/common/base/baseutils/jsonutils" "gitee.com/ichub/gonats/general/gonats/gobase" "gitee.com/ichub/gonats/general/gonats/goconsts" "gitee.com/ichub/gonats/general/gonats/gomsg" "gitee.com/ichub/gonats/general/goutil" "github.com/gogf/gf/util/gconv" "github.com/nats-io/nats.go" "github.com/sirupsen/logrus" "sync" "time" ) // ServerSync 是一个请求响应服务器结构体,继承了ReqRespBase和basedto.BaseEntitySingle。 type ServerSync struct { basedto.BaseEntitySingle *gobase.GonatsProxy } func NewServerSync() *ServerSync { var e = &ServerSync{ GonatsProxy: gobase.NewGonatsProxy(), } e.InitChannels() e.InitProxy(e) return e } func (this *ServerSync) Server() { this.ServerTopic(goconsts.Topic_Async_Default) } func (this *ServerSync) ServerEs() { this.ServerTopic(goconsts.Topic_DOMAIN_GENEAL_SyncES) } func (this *ServerSync) StartServer() { this.WaitGroup.Add(1) go this.HandleChannels() this.start(goconsts.Topic_DOMAIN_GENEAL_SyncES) this.WaitGroup.Wait() } func (this *ServerSync) chooseChannel(m *nats.Msg) int { // 这里可以添加选择逻辑,例如根据消息内容等 // 简单的轮询选择 return 1 //int(m.Sub.Subject) % 10 } func (this *ServerSync) start(topic string) { if !this.CheckTimeout() { panic("timeout is not set or invalid!" + gconv.String(this.Timeout)) } defer this.WaitGroup.Done() nc, err := this.Conn() defer nc.Close() // 创建一个Subscription来监听特定的Subject sub, err := nc.SubscribeSync(topic) if err != nil { logrus.Error(err) return } defer func() { var err = sub.Unsubscribe() if err != nil { logrus.Error("Error unsubscribing: %v", err) } }() logrus.Info("Starting server, Waiting for requests on ", topic) // 创建一个请求响应循环 for { // 等待请求消息 msg, err := sub.NextMsg(this.ITimeout) if err != nil { time.Sleep(time.Millisecond) continue } // 响应消息 msg.RespondMsg(m.To()) this.Dispatch(msg) goutil.FindBeanGoLog().Info(context.Background(), "Response 2channel:"+jsonutils.ToJsonPretty(msg)) } } func (this *ServerSync) Dispatch(msg *nats.Msg) { // 处理请求消息 var i = this.FindChannelNum() var lock sync.Mutex lock.Lock() defer lock.Unlock() this.Channels[i] <- msg } func (this *ServerSync) HandleChannels() { for { for i := 0; i < goconsts.CHANNEL_NUMBER; i++ { this.WaitGroup.Add(1) go this.handleChannel(this.Channels[i]) } this.WaitGroup.Wait() } } func (this *ServerSync) handleChannel(ch chan *nats.Msg) { for msg := range ch { logrus.Info("takeFromChannel waiting! " + gconv.String(1)) this.handleMessage(msg) logrus.Info("takeFromChannel=", msg) } } func (this *ServerSync) handleMessage(msg *nats.Msg) { defer this.WaitGroup.Done() var gonatsmsg = gomsg.NewGonatsMsg().From(msg) gonatsmsg.Header.Id = gonatsmsg.Header.Id + " 202929292" var err = msg.RespondMsg(gonatsmsg.To()) if err != nil { logrus.Error(err) return } goutil.FindBeanGoLog().Info(context.Background(), "go handle Response gonatsmsg: ", gonatsmsg.ToPrettyString()) } func (this *ServerSync) ServerTopic(topic string) { defer this.WaitGroup.Done() nc, err := this.Conn() defer nc.Close() // 创建一个Subscription来监听特定的Subject sub, err := nc.SubscribeSync(topic) if err != nil { logrus.Error(err) return } defer func() { var err = sub.Unsubscribe() if err != nil { logrus.Printf("Error unsubscribing: %v", err) } }() // 创建一个请求响应循环 for { // 等待请求消息 msg, err := sub.NextMsg(gconv.Duration(this.Timeout)) if err != nil { //logrus.Error(err) time.Sleep(time.Millisecond) continue } // 处理请求消息 responseMsg := "Received: ***" + string(msg.Data) + "***" err = msg.Respond([]byte(responseMsg)) if err != nil { logrus.Error(err) continue } logrus.Info("Response sent:", responseMsg) } }
关于统一ES服务与NATS
应用协议栈技术路线图
第一步:完成ES通用接口服务;
第二步:完成NATS消息应用协议栈,完成业务事件处理器框架;
第三步:NATS消息应用协议栈对接ES服务
第四步:各领域全面应用、对接ES服务;
第五步:各领域全面应用NATS消息应用层协议栈。
同步消息测试用例
func Test001_asyncCli_publish(t *testing.T) { for i := 0; i < 110; i++ { var msg = gomsg.FindBeanGonatsMsg() msg.Header.Id = gconv.String(baseutils.SnowflakeNextVal()) msg.Header.Topic = goconsts.Topic_DOMAIN_GENEAL_AsyncES var result = basedto.NewIchubResult() msg.SetData(result) msg.Header.MsgReq = true msg.ParseData(basedto.NewIchubResult()) var err = cliasync.FindBeanCliAsync().Publish(msg) if err != nil { logrus.Error(err) panic(err) } logrus.Info(msg.ToPrettyString()) } }
同步消息代码
package serverasync import ( "context" "gitee.com/ichub/goconfig/common/base/basedto" "gitee.com/ichub/goconfig/common/base/baseutils/jsonutils" "gitee.com/ichub/gonats/general/gonats/goconsts" "gitee.com/ichub/gonats/general/gonats/gomsg" "gitee.com/ichub/gonats/general/gonats/goproxy" "gitee.com/ichub/gonats/general/goutil" "github.com/gogf/gf/util/gconv" "github.com/nats-io/nats.go" "github.com/sirupsen/logrus" ) type ServerAsync struct { basedto.BaseEntitySingle *goproxy.GonatsProxy } func NewServerAsync() *ServerAsync { var e = &ServerAsync{ GonatsProxy: goproxy.NewGonatsProxy(), } e.InitChannels() e.InitProxy(e) return e } // subscribe func (self *ServerAsync) Subscribe(Topic string) (*nats.Conn, error) { return self.GonatsProxy.Subscribe(Topic, self.HandleMsg) } func (self *ServerAsync) HandleMsg(msg *nats.Msg) { //var l sync.Mutex l.Lock() defer l.Unlock() logrus.Info("Handle msg=", jsonutils.ToJsonPretty(msg)) self.Dispatch(msg) goutil.FindBeanGoLogServer().Info(context.Background(), "serverASync receive request message from client is ", gomsg.NewGonatsMsg().From(msg).ToPrettyString()) } func (self *ServerAsync) StartServer() *nats.Conn { // 异步消息处理 go self.HandleChannels() if conn, err := self.Subscribe(goconsts.Topic_DOMAIN_GENEAL_AsyncES_PRE); err != nil { panic(err) } else { logrus.Info("conn is = ", conn.IsConnected()) return conn } return nil } // self.Subscribe(goconsts.Topic_DOMAIN_GENEAL_AsyncES_PRE) func (this *ServerAsync) Dispatch(msg *nats.Msg) { // 处理请求消息 var i = this.FindChannelNum() this.Channels[i] <- msg } func (this *ServerAsync) HandleChannels() { for { for i := 0; i < goconsts.CHANNEL_NUMBER; i++ { this.WaitGroup.Add(1) go this.handleChannel(this.Channels[i]) } this.WaitGroup.Wait() } } func (this *ServerAsync) handleChannel(ch chan *nats.Msg) { for msg := range ch { logrus.Info("handleChannel waiting " + gconv.String(1)) this.handleMessage(msg) } } func (this *ServerAsync) handleMessage(msg *nats.Msg) { defer this.WaitGroup.Done() var gonatsmsg = gomsg.NewGonatsMsg().From(msg) goutil.FindBeanGoLogServer().Info(context.Background(), "serverASync handle message is ", gonatsmsg.ToPrettyString()) //msg.Respond([]byte("hello world")) logrus.Info("handleMessage waiting " + gconv.String(2)) }