This commit is contained in:
孟帅
2022-11-24 23:37:34 +08:00
parent 4ffe54b6ac
commit 29bda0dcdd
1487 changed files with 97869 additions and 96539 deletions

View File

@@ -0,0 +1,216 @@
// Package queue
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package queue
import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"hotgo/utility/charset"
"sync"
"time"
)
type MqProducer interface {
SendMsg(topic string, body string) (mqMsg MqMsg, err error)
SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error)
}
type MqConsumer interface {
ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg)) (err error)
}
const (
_ = iota
SendMsg
ReceiveMsg
)
type Config struct {
Switch bool `json:"switch"`
Driver string `json:"driver"`
Retry int `json:"retry"`
MultiComsumer bool `json:"multiComsumer"`
GroupName string `json:"groupName"`
Redis RedisConf
Rocketmq RocketmqConf
Kafka KafkaConf
}
type RedisConf struct {
Address string `json:"address"`
Db int `json:"db"`
Pass string `json:"pass"`
Timeout int `json:"timeout"`
}
type RocketmqConf struct {
Address []string `json:"address"`
LogLevel string `json:"logLevel"`
}
type KafkaConf struct {
Address []string `json:"address"`
Version string `json:"version"`
RandClient bool `json:"randClient"`
}
type MqMsg struct {
RunType int `json:"run_type"`
Topic string `json:"topic"`
MsgId string `json:"msg_id"`
Offset int64 `json:"offset"`
Partition int32 `json:"partition"`
Timestamp time.Time `json:"timestamp"`
Body []byte `json:"body"`
}
var (
ctx = gctx.New()
mqProducerInstanceMap map[string]MqProducer
mqConsumerInstanceMap map[string]MqConsumer
mutex sync.Mutex
config Config
)
func init() {
mqProducerInstanceMap = make(map[string]MqProducer)
mqConsumerInstanceMap = make(map[string]MqConsumer)
get, err := g.Cfg().Get(ctx, "queue")
if err != nil {
g.Log().Fatalf(ctx, "queue config load fail, err .%v", err)
return
}
get.Scan(&config)
}
// InstanceConsumer 实例化消费者
func InstanceConsumer() (mqClient MqConsumer, err error) {
return NewConsumer(config.GroupName)
}
// InstanceProducer 实例化生产者
func InstanceProducer() (mqClient MqProducer, err error) {
return NewProducer(config.GroupName)
}
// NewProducer 新建一个生产者实例
func NewProducer(groupName string) (mqClient MqProducer, err error) {
if item, ok := mqProducerInstanceMap[groupName]; ok {
return item, nil
}
if groupName == "" {
return mqClient, gerror.New("mq groupName is empty.")
}
switch config.Driver {
case "rocketmq":
if len(config.Rocketmq.Address) == 0 {
g.Log().Fatal(ctx, "queue rocketmq address is not support")
}
mqClient = RegisterRocketProducerMust(config.Rocketmq.Address, groupName, config.Retry)
case "kafka":
if len(config.Kafka.Address) == 0 {
g.Log().Fatal(ctx, "queue kafka address is not support")
}
mqClient = RegisterKafkaProducerMust(KafkaConfig{
Brokers: config.Kafka.Address,
GroupID: groupName,
Version: config.Kafka.Version,
})
case "redis":
address, _ := g.Cfg().Get(ctx, "queue.redis.address", nil)
if len(address.String()) == 0 {
g.Log().Fatal(ctx, "queue redis address is not support")
}
mqClient = RegisterRedisMqProducerMust(RedisOption{
Addr: config.Redis.Address,
Passwd: config.Redis.Pass,
DBnum: config.Redis.Db,
Timeout: config.Redis.Timeout,
}, PoolOption{
5, 50, 5,
}, groupName, config.Retry)
default:
g.Log().Fatal(ctx, "queue driver is not support")
}
mutex.Lock()
defer mutex.Unlock()
mqProducerInstanceMap[groupName] = mqClient
return mqClient, nil
}
// NewConsumer 新建一个消费者实例
func NewConsumer(groupName string) (mqClient MqConsumer, err error) {
randTag := string(charset.RandomCreateBytes(6))
// 是否支持创建多个消费者
if config.MultiComsumer == false {
randTag = "001"
}
if item, ok := mqConsumerInstanceMap[groupName+"-"+randTag]; ok {
return item, nil
}
if groupName == "" {
return mqClient, gerror.New("mq groupName is empty.")
}
switch config.Driver {
case "rocketmq":
if len(config.Rocketmq.Address) == 0 {
return nil, gerror.New("queue.rocketmq.address is empty.")
}
mqClient = RegisterRocketConsumerMust(config.Rocketmq.Address, groupName)
case "kafka":
if len(config.Kafka.Address) == 0 {
g.Log().Fatal(ctx, "queue kafka address is not support")
}
clientId := "HOTGO-Consumer-" + groupName
if config.Kafka.RandClient {
clientId += "-" + randTag
}
mqClient = RegisterKafkaMqConsumerMust(KafkaConfig{
Brokers: config.Kafka.Address,
GroupID: groupName,
Version: config.Kafka.Version,
ClientId: clientId,
})
case "redis":
if len(config.Redis.Address) == 0 {
g.Log().Fatal(ctx, "queue redis address is not support")
}
mqClient = RegisterRedisMqConsumerMust(RedisOption{
Addr: config.Redis.Address,
Passwd: config.Redis.Pass,
DBnum: config.Redis.Db,
Timeout: config.Redis.Timeout,
}, PoolOption{
5, 50, 5,
}, groupName)
default:
g.Log().Fatal(ctx, "queue driver is not support")
}
mutex.Lock()
defer mutex.Unlock()
mqConsumerInstanceMap[groupName] = mqClient
return mqClient, nil
}
// BodyString 返回消息体
func (m *MqMsg) BodyString() string {
return string(m.Body)
}

View File

@@ -0,0 +1,249 @@
// Package queue
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package queue
import (
"context"
"fmt"
"github.com/Shopify/sarama"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"hotgo/utility/signal"
"time"
)
type KafkaMq struct {
endPoints []string
Partitions int32
producerIns sarama.AsyncProducer
consumerIns sarama.ConsumerGroup
}
type KafkaConfig struct {
ClientId string
Brokers []string
GroupID string
Partitions int32
Replication int16
Version string
UserName string
Password string
}
// SendMsg 按字符串类型生产数据
func (r *KafkaMq) SendMsg(topic string, body string) (mqMsg MqMsg, err error) {
return r.SendByteMsg(topic, []byte(body))
}
// SendByteMsg 生产数据
func (r *KafkaMq) SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error) {
msg := &sarama.ProducerMessage{
Topic: topic,
Value: sarama.ByteEncoder(body),
Timestamp: time.Now(),
}
if r.producerIns == nil {
return mqMsg, gerror.New("queue kafka producerIns is nil")
}
r.producerIns.Input() <- msg
ctx, cancle := context.WithTimeout(context.Background(), 5*time.Second)
defer cancle()
select {
case info := <-r.producerIns.Successes():
return MqMsg{
RunType: SendMsg,
Topic: info.Topic,
Offset: info.Offset,
Partition: info.Partition,
Timestamp: info.Timestamp,
}, nil
case fail := <-r.producerIns.Errors():
if nil != fail {
return mqMsg, fail.Err
}
case <-ctx.Done():
return mqMsg, gerror.New("send mqMst timeout")
}
return mqMsg, nil
}
// ListenReceiveMsgDo 消费数据
func (r *KafkaMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg)) (err error) {
if r.consumerIns == nil {
return gerror.New("queue kafka consumer not register")
}
consumer := Consumer{
ready: make(chan bool),
receiveDoFun: receiveDo,
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
if err := r.consumerIns.Consume(ctx, []string{topic}, &consumer); err != nil {
FatalLog(ctx, "kafka Error from consumer", err)
}
if ctx.Err() != nil {
Log(ctx, fmt.Sprintf("kafka consoumer stop : %v", ctx.Err()))
return
}
consumer.ready = make(chan bool)
}
}()
<-consumer.ready // Await till the consumer has been set up
Log(ctx, "kafka consumer up and running!...")
signal.AppDefer(func() {
Log(ctx, "kafka consumer close...")
cancel()
if err = r.consumerIns.Close(); err != nil {
FatalLog(ctx, "kafka Error closing client", err)
}
})
return
}
// RegisterKafkaMqConsumerMust 注册消费者
func RegisterKafkaMqConsumerMust(connOpt KafkaConfig) (client MqConsumer) {
mqIns := &KafkaMq{}
kfkVersion, _ := sarama.ParseKafkaVersion(connOpt.Version)
if validateVersion(kfkVersion) == false {
kfkVersion = sarama.V2_4_0_0
}
brokers := connOpt.Brokers
config := sarama.NewConfig()
config.Consumer.Return.Errors = true
config.Version = kfkVersion
if connOpt.UserName != "" {
config.Net.SASL.Enable = true
config.Net.SASL.User = connOpt.UserName
config.Net.SASL.Password = connOpt.Password
}
// 默认按随机方式消费
config.Consumer.Group.Rebalance.Strategy = sarama.BalanceStrategyRange
config.Consumer.Offsets.Initial = sarama.OffsetNewest
config.Consumer.Offsets.AutoCommit.Interval = 10 * time.Millisecond
config.ClientID = connOpt.ClientId
consumerClient, err := sarama.NewConsumerGroup(brokers, connOpt.GroupID, config)
if err != nil {
g.Log().Fatal(ctx, err)
}
mqIns.consumerIns = consumerClient
return mqIns
}
// RegisterKafkaProducerMust 注册并启动生产者接口实现
func RegisterKafkaProducerMust(connOpt KafkaConfig) (client MqProducer) {
mqIns := &KafkaMq{}
connOpt.ClientId = "HOTGO-Producer"
RegisterKafkaProducer(connOpt, mqIns) //这里如果使用go程需要处理chan同步问题
return mqIns
}
// RegisterKafkaProducer 注册同步类型实例
func RegisterKafkaProducer(connOpt KafkaConfig, mqIns *KafkaMq) {
kfkVersion, _ := sarama.ParseKafkaVersion(connOpt.Version)
if validateVersion(kfkVersion) == false {
kfkVersion = sarama.V2_4_0_0
}
brokers := connOpt.Brokers
config := sarama.NewConfig()
// 等待服务器所有副本都保存成功后的响应
config.Producer.RequiredAcks = sarama.WaitForAll
// 随机向partition发送消息
config.Producer.Partitioner = sarama.NewRandomPartitioner
// 是否等待成功和失败后的响应,只有上面的RequireAcks设置不是NoReponse这里才有用.
config.Producer.Return.Successes = true
config.Producer.Return.Errors = true
config.Producer.Compression = sarama.CompressionNone
config.ClientID = connOpt.ClientId
config.Version = kfkVersion
if connOpt.UserName != "" {
config.Net.SASL.Enable = true
config.Net.SASL.User = connOpt.UserName
config.Net.SASL.Password = connOpt.Password
}
var err error
mqIns.producerIns, err = sarama.NewAsyncProducer(brokers, config)
if err != nil {
g.Log().Fatal(ctx, err)
}
signal.AppDefer(func() {
Log(ctx, "kafka producer AsyncClose...")
mqIns.producerIns.AsyncClose()
})
}
// validateVersion 验证版本是否有效
func validateVersion(version sarama.KafkaVersion) bool {
for _, item := range sarama.SupportedVersions {
if version.String() == item.String() {
return true
}
}
return false
}
type Consumer struct {
ready chan bool
receiveDoFun func(mqMsg MqMsg)
}
// Setup is run at the beginning of a new session, before ConsumeClaim
func (consumer *Consumer) Setup(sarama.ConsumerGroupSession) error {
// Mark the consumer as ready
close(consumer.ready)
return nil
}
// Cleanup is run at the end of a session, once all ConsumeClaim goroutines have exited
func (consumer *Consumer) Cleanup(sarama.ConsumerGroupSession) error {
return nil
}
// ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages().
func (consumer *Consumer) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
// NOTE:
// Do not move the code below to a goroutine.
// The `ConsumeClaim` itself is called within a goroutine, see:
// https://github.com/Shopify/sarama/blob/master/consumer_group.go#L27-L29
// `ConsumeClaim` 方法已经是 goroutine 调用 不要在该方法内进行 goroutine
for message := range claim.Messages() {
consumer.receiveDoFun(MqMsg{
RunType: ReceiveMsg,
Topic: message.Topic,
Body: message.Value,
Offset: message.Offset,
Timestamp: message.Timestamp,
Partition: message.Partition,
})
session.MarkMessage(message, "")
}
return nil
}

View File

@@ -0,0 +1,42 @@
// Package queue
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package queue
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
)
// ConsumerLog 消费日志
func ConsumerLog(ctx context.Context, topic string, mqMsg MqMsg, err error) {
if err != nil {
g.Log(consts.QueueLogPath).Error(ctx, "消费 ["+topic+"] 失败", mqMsg, err)
} else {
g.Log(consts.QueueLogPath).Debug(ctx, "消费 ["+topic+"] 成功", mqMsg.MsgId)
}
}
// ProducerLog 生产日志
func ProducerLog(ctx context.Context, topic string, data interface{}, err error) {
if err != nil {
g.Log(consts.QueueLogPath).Error(ctx, "生产 ["+topic+"] 失败", gconv.String(data))
} else {
g.Log(consts.QueueLogPath).Debug(ctx, "生产 ["+topic+"] 成功", gconv.String(data))
}
}
// FatalLog 致命日志
func FatalLog(ctx context.Context, text string, err error) {
g.Log(consts.QueueLogPath).Fatal(ctx, text+":", err)
}
// Log 通用日志
func Log(ctx context.Context, text string) {
g.Log(consts.QueueLogPath).Debug(ctx, text)
}

View File

@@ -0,0 +1,289 @@
package queue
import (
"encoding/json"
"fmt"
"github.com/bufanyun/pool"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gomodule/redigo/redis"
"hotgo/internal/consts"
"hotgo/utility/encrypt"
"math/rand"
"time"
)
type RedisMq struct {
poolName string
groupName string
retry int
timeout int
}
type PoolOption struct {
InitCap int
MaxCap int
IdleTimeout int
}
type RedisOption struct {
Addr string
Passwd string
DBnum int
Timeout int
}
var redisPoolMap map[string]pool.Pool
func init() {
redisPoolMap = make(map[string]pool.Pool)
}
// SendMsg 按字符串类型生产数据
func (r *RedisMq) SendMsg(topic string, body string) (mqMsg MqMsg, err error) {
return r.SendByteMsg(topic, []byte(body))
}
// SendByteMsg 生产数据
func (r *RedisMq) SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error) {
if r.poolName == "" {
return mqMsg, gerror.New("RedisMq producer not register")
}
if topic == "" {
return mqMsg, gerror.New("RedisMq topic is empty")
}
msgId := getRandMsgId()
rdx, put, err := getRedis(r.poolName, r.retry)
defer put()
if err != nil {
return mqMsg, gerror.New(fmt.Sprint("queue redis 生产者获取redis实例失败:", err))
}
mqMsg = MqMsg{
RunType: SendMsg,
Topic: topic,
MsgId: msgId,
Body: body,
Timestamp: time.Now(),
}
mqMsgJson, err := json.Marshal(mqMsg)
if err != nil {
return mqMsg, gerror.New(fmt.Sprint("queue redis 生产者解析json消息失败:", err))
}
queueName := r.genQueueName(r.groupName, topic)
_, err = redis.Int64(rdx.Do("LPUSH", queueName, mqMsgJson))
if err != nil {
return mqMsg, gerror.New(fmt.Sprint("queue redis 生产者添加消息失败:", err))
}
if r.timeout > 0 {
_, err = rdx.Do("EXPIRE", queueName, r.timeout)
if err != nil {
return mqMsg, gerror.New(fmt.Sprint("queue redis 生产者设置过期时间失败:", err))
}
}
return
}
// ListenReceiveMsgDo 消费数据
func (r *RedisMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg)) (err error) {
if r.poolName == "" {
return gerror.New("RedisMq producer not register")
}
if topic == "" {
return gerror.New("RedisMq topic is empty")
}
queueName := r.genQueueName(r.groupName, topic)
go func() {
for range time.Tick(500 * time.Millisecond) {
mqMsgList := r.loopReadQueue(queueName)
for _, mqMsg := range mqMsgList {
receiveDo(mqMsg)
}
}
}()
select {}
}
// 生成队列名称
func (r *RedisMq) genQueueName(groupName string, topic string) string {
return fmt.Sprintf(consts.QueueName+"%s_%s", groupName, topic)
}
func (r *RedisMq) loopReadQueue(queueName string) (mqMsgList []MqMsg) {
rdx, put, err := getRedis(r.poolName, r.retry)
defer put()
if err != nil {
g.Log().Warningf(ctx, "loopReadQueue getRedis err:%+v", err)
return
}
for {
infoByte, err := redis.Bytes(rdx.Do("RPOP", queueName))
if redis.ErrNil == err || len(infoByte) == 0 {
break
}
if err != nil {
g.Log().Warningf(ctx, "loopReadQueue redis RPOP err:%+v", err)
break
}
var mqMsg MqMsg
if err = json.Unmarshal(infoByte, &mqMsg); err != nil {
g.Log().Warningf(ctx, "loopReadQueue Unmarshal err:%+v", err)
break
}
if mqMsg.MsgId != "" {
mqMsgList = append(mqMsgList, mqMsg)
}
}
return mqMsgList
}
func RegisterRedisMqProducerMust(connOpt RedisOption, poolOpt PoolOption, groupName string, retry int) (client MqProducer) {
var err error
client, err = RegisterRedisMq(connOpt, poolOpt, groupName, retry)
if err != nil {
g.Log().Fatal(ctx, "RegisterRedisMqProducerMust err:%+v", err)
return
}
return client
}
// RegisterRedisMqConsumerMust 注册消费者
func RegisterRedisMqConsumerMust(connOpt RedisOption, poolOpt PoolOption, groupName string) (client MqConsumer) {
var err error
client, err = RegisterRedisMq(connOpt, poolOpt, groupName, 0)
if err != nil {
g.Log().Fatal(ctx, "RegisterRedisMqConsumerMust err:%+v", err)
return
}
return client
}
// RegisterRedisMq 注册redis实例
func RegisterRedisMq(connOpt RedisOption, poolOpt PoolOption, groupName string, retry int) (mqIns *RedisMq, err error) {
poolName, err := registerRedis(connOpt.Addr, connOpt.Passwd, connOpt.DBnum, poolOpt)
if err != nil {
return
}
if retry <= 0 {
retry = 0
}
mqIns = &RedisMq{
poolName: poolName,
groupName: groupName,
retry: retry,
timeout: connOpt.Timeout,
}
return mqIns, nil
}
// RegisterRedis 注册一个redis配置
func registerRedis(host, pass string, dbNum int, opt PoolOption) (poolName string, err error) {
poolName = encrypt.Md5ToString(fmt.Sprintf("%s-%s-%d", host, pass, dbNum))
if _, ok := redisPoolMap[poolName]; ok {
return poolName, nil
}
connRedis := func() (interface{}, error) {
conn, err := redis.Dial("tcp", host)
if err != nil {
return nil, err
}
if pass != "" {
if _, err := conn.Do("AUTH", pass); err != nil {
return nil, err
}
}
if dbNum > 0 {
if _, err := conn.Do("SELECT", dbNum); err != nil {
return nil, err
}
}
return conn, err
}
// closeRedis 关闭连接
closeRedis := func(v interface{}) error {
return v.(redis.Conn).Close()
}
// pingRedis 检测连接连通性
pingRedis := func(v interface{}) error {
conn := v.(redis.Conn)
val, err := redis.String(conn.Do("PING"))
if err != nil {
return err
}
if val != "PONG" {
return gerror.New("queue redis ping is error ping => " + val)
}
return nil
}
p, err := pool.NewChannelPool(&pool.Config{
InitialCap: opt.InitCap,
MaxCap: opt.MaxCap,
Factory: connRedis,
Close: closeRedis,
Ping: pingRedis,
IdleTimeout: time.Duration(opt.IdleTimeout) * time.Second,
})
if err != nil {
return poolName, err
}
mutex.Lock()
defer mutex.Unlock()
redisPoolMap[poolName] = p
return poolName, nil
}
// getRedis 获取一个redis db连接
func getRedis(poolName string, retry int) (db redis.Conn, put func(), err error) {
put = func() {}
if _, ok := redisPoolMap[poolName]; ok == false {
return nil, put, gerror.New("db connect is nil")
}
redisPool := redisPoolMap[poolName]
conn, err := redisPool.Get()
for i := 0; i < retry; i++ {
if err == nil {
break
}
conn, err = redisPool.Get()
time.Sleep(time.Second)
}
if err != nil {
return nil, put, err
}
put = func() {
redisPool.Put(conn)
}
db = conn.(redis.Conn)
return db, put, nil
}
func getRandMsgId() (msgId string) {
rand.Seed(time.Now().UnixNano())
radium := rand.Intn(999) + 1
timeCode := time.Now().UnixNano()
msgId = fmt.Sprintf("%d%.4d", timeCode, radium)
return msgId
}

View File

@@ -0,0 +1,171 @@
// Package queue
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package queue
import (
"context"
"errors"
"fmt"
"github.com/apache/rocketmq-client-go/v2"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
"github.com/apache/rocketmq-client-go/v2/producer"
"github.com/apache/rocketmq-client-go/v2/rlog"
"github.com/gogf/gf/v2/frame/g"
)
type RocketMq struct {
endPoints []string
producerIns rocketmq.Producer
consumerIns rocketmq.PushConsumer
}
// rewriteLog 重写日志
func rewriteLog() {
level, _ := g.Cfg().Get(ctx, "queue.rocketmq.logLevel", "debug")
rlog.SetLogger(&RocketMqLogger{Flag: "[rocket_mq]", LevelLog: level.String()})
}
// RegisterRocketProducerMust 注册并启动生产者接口实现
func RegisterRocketProducerMust(endPoints []string, groupName string, retry int) (client MqProducer) {
rewriteLog()
var err error
client, err = RegisterRocketMqProducer(endPoints, groupName, retry)
if err != nil {
panic(err)
}
return client
}
// RegisterRocketConsumerMust 注册消费者
func RegisterRocketConsumerMust(endPoints []string, groupName string) (client MqConsumer) {
rewriteLog()
var err error
client, err = RegisterRocketMqConsumer(endPoints, groupName)
if err != nil {
panic(err)
}
return client
}
// SendMsg 按字符串类型生产数据
func (r *RocketMq) SendMsg(topic string, body string) (mqMsg MqMsg, err error) {
return r.SendByteMsg(topic, []byte(body))
}
// SendByteMsg 生产数据
func (r *RocketMq) SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error) {
if r.producerIns == nil {
return mqMsg, errors.New("RocketMq producer not register")
}
result, err := r.producerIns.SendSync(context.Background(), &primitive.Message{
Topic: topic,
Body: body,
})
if err != nil {
return
}
if result.Status != primitive.SendOK {
return mqMsg, errors.New(fmt.Sprintf("RocketMq producer send msg error status:%v", result.Status))
}
mqMsg = MqMsg{
RunType: SendMsg,
Topic: topic,
MsgId: result.MsgID,
Body: body,
}
return mqMsg, nil
}
// ListenReceiveMsgDo 消费数据
func (r *RocketMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg)) (err error) {
if r.consumerIns == nil {
return errors.New("RocketMq consumer not register")
}
err = r.consumerIns.Subscribe(topic, consumer.MessageSelector{}, func(ctx context.Context,
msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for _, item := range msgs {
go receiveDo(MqMsg{
RunType: ReceiveMsg,
Topic: item.Topic,
MsgId: item.MsgId,
Body: item.Body,
})
}
return consumer.ConsumeSuccess, nil
})
if err != nil {
return err
}
err = r.consumerIns.Start()
if err != nil {
r.consumerIns.Unsubscribe(topic)
return err
}
return
}
// RegisterRocketMqProducer 注册rocketmq生产者
func RegisterRocketMqProducer(endPoints []string, groupName string, retry int) (mqIns *RocketMq, err error) {
addr, err := primitive.NewNamesrvAddr(endPoints...)
if err != nil {
return nil, err
}
mqIns = &RocketMq{
endPoints: endPoints,
}
if retry <= 0 {
retry = 0
}
mqIns.producerIns, err = rocketmq.NewProducer(
producer.WithNameServer(addr),
producer.WithRetry(retry),
producer.WithGroupName(groupName),
)
if err != nil {
return nil, err
}
err = mqIns.producerIns.Start()
if err != nil {
return nil, err
}
return mqIns, nil
}
// RegisterRocketMqConsumer 注册rocketmq消费者
func RegisterRocketMqConsumer(endPoints []string, groupName string) (mqIns *RocketMq, err error) {
addr, err := primitive.NewNamesrvAddr(endPoints...)
if err != nil {
return nil, err
}
mqIns = &RocketMq{
endPoints: endPoints,
}
mqIns.consumerIns, err = rocketmq.NewPushConsumer(
consumer.WithNameServer(addr),
consumer.WithConsumerModel(consumer.Clustering),
consumer.WithGroupName(groupName),
)
if err != nil {
return nil, err
}
return mqIns, nil
}

View File

@@ -0,0 +1,89 @@
// Package queue
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package queue
import (
"fmt"
)
type RocketMqLogger struct {
Flag string
LevelLog string
}
func (l *RocketMqLogger) Debug(msg string, fields map[string]interface{}) {
if l.LevelLog == "close" {
return
}
if msg == "" && len(fields) == 0 {
return
}
if l.LevelLog == "debug" || l.LevelLog == "all" {
Log(ctx, fmt.Sprint(l.Flag, " [debug] ", msg))
}
}
func (l *RocketMqLogger) Level(level string) {
Log(ctx, fmt.Sprint(l.Flag, " [level] ", level))
}
func (l *RocketMqLogger) OutputPath(path string) (err error) {
Log(ctx, fmt.Sprint(l.Flag, " [path] ", path))
return nil
}
func (l *RocketMqLogger) Info(msg string, fields map[string]interface{}) {
if l.LevelLog == "close" {
return
}
if msg == "" && len(fields) == 0 {
return
}
if l.LevelLog == "info" || l.LevelLog == "all" {
Log(ctx, fmt.Sprint(l.Flag, " [info] ", msg))
}
}
func (l *RocketMqLogger) Warning(msg string, fields map[string]interface{}) {
if l.LevelLog == "close" {
return
}
if msg == "" && len(fields) == 0 {
return
}
if l.LevelLog == "warn" || l.LevelLog == "all" {
Log(ctx, fmt.Sprint(l.Flag, " [warn] ", msg))
}
}
func (l *RocketMqLogger) Error(msg string, fields map[string]interface{}) {
if l.LevelLog == "close" {
return
}
if msg == "" && len(fields) == 0 {
return
}
if l.LevelLog == "error" || l.LevelLog == "all" {
Log(ctx, fmt.Sprint(l.Flag, " [error] ", msg))
}
}
func (l *RocketMqLogger) Fatal(msg string, fields map[string]interface{}) {
if l.LevelLog == "close" {
return
}
if msg == "" && len(fields) == 0 {
return
}
if l.LevelLog == "fatal" || l.LevelLog == "all" {
Log(ctx, fmt.Sprint(l.Flag, " [fatal] ", msg))
}
}