Go实战全家桶之五:gonats完成nats消息应用层协议栈,同步消息Request/Response&异步消息Publish;10个channel队列,10个消费协程

总体框架:

8ba5ab212c4e4a5b8541aed289bf4c76.png

实现UML设计:

       

       目前实现了同步,异步也已实现;总感觉异步不太靠谱 ;是不是还要通知客户端消息处理结果ok!

消息适配器模式:

主协程收到的消息分发到10个channel, 然后有10个协程消费消息。如果处理时间长,10消息的业务处理又可以启动新的协程,保证高并发。channel是什么,队列,多协程安全。

       同步流程是消息返回响应,异步流程publish只发不收,要观察测试查看日志表。

领域事件处理框架

cde675e9a9c844cd90853bbc2ff1181c.png

主消息流程框架:

bed28745c87145819696801a0cb1ed4d.png

开源地址:https://gitee.com/ichub/gonats.git

cc895474594b4fd9af1d178fc930631a.png操作步骤

启动服务:

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))
}

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leijmdas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值