mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-11-12 20:23:52 +08:00
发布v2.15.1版本,更新内容请查看:https://github.com/bufanyun/hotgo/blob/v2.0/docs/guide-zh-CN/start-update-log.md
This commit is contained in:
@@ -30,6 +30,6 @@ func ConsumerLog(ctx context.Context, topic string, mqMsg MqMsg, err error) {
|
||||
// ProducerLog 生产日志
|
||||
func ProducerLog(ctx context.Context, topic string, mqMsg MqMsg, err error) {
|
||||
if err != nil {
|
||||
Logger().Errorf(ctx, ProducerLogErrFormat, topic, string(mqMsg.Body), err)
|
||||
Logger().Infof(ctx, ProducerLogErrFormat, topic, string(mqMsg.Body), err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,16 @@ func Push(topic string, data interface{}) (err error) {
|
||||
}
|
||||
|
||||
// DelayPush 推送延迟队列
|
||||
func DelayPush(topic string, data interface{}, second int64) (err error) {
|
||||
// redis delay 传入 秒。如:10代表延迟10秒
|
||||
// rocketmq delay 传入 延迟级别。如:2代表延迟5秒
|
||||
// rocketmq reference delay level definition: 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
|
||||
// rocketmq delay level starts from 1. for example, if we set param level=1, then the delay time is 1s.
|
||||
func DelayPush(topic string, data interface{}, delay int64) (err error) {
|
||||
q, err := InstanceProducer()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mqMsg, err := q.SendDelayMsg(topic, gconv.String(data), second)
|
||||
mqMsg, err := q.SendDelayMsg(topic, gconv.String(data), delay)
|
||||
ProducerLog(ctx, topic, mqMsg, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
type MqProducer interface {
|
||||
SendMsg(topic string, body string) (mqMsg MqMsg, err error)
|
||||
SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error)
|
||||
SendDelayMsg(topic string, body string, delaySecond int64) (mqMsg MqMsg, err error)
|
||||
SendDelayMsg(topic string, body string, delay int64) (mqMsg MqMsg, err error)
|
||||
}
|
||||
|
||||
type MqConsumer interface {
|
||||
@@ -34,7 +34,6 @@ const (
|
||||
type Config struct {
|
||||
Switch bool `json:"switch"`
|
||||
Driver string `json:"driver"`
|
||||
Retry int `json:"retry"`
|
||||
GroupName string `json:"groupName"`
|
||||
Redis RedisConf
|
||||
Rocketmq RocketmqConf
|
||||
@@ -45,9 +44,14 @@ type Config struct {
|
||||
type RedisConf struct {
|
||||
Timeout int64 `json:"timeout"`
|
||||
}
|
||||
|
||||
type RocketmqConf struct {
|
||||
Address []string `json:"address"`
|
||||
LogLevel string `json:"logLevel"`
|
||||
NameSrvAdders []string `json:"nameSrvAdders"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
BrokerAddr string `json:"brokerAddr"`
|
||||
Retry int `json:"retry"`
|
||||
LogLevel string `json:"logLevel"`
|
||||
}
|
||||
|
||||
type KafkaConf struct {
|
||||
@@ -106,11 +110,11 @@ func NewProducer(groupName string) (mqClient MqProducer, err error) {
|
||||
|
||||
switch config.Driver {
|
||||
case "rocketmq":
|
||||
if len(config.Rocketmq.Address) == 0 {
|
||||
err = gerror.New("queue rocketmq address is not support")
|
||||
if len(config.Rocketmq.NameSrvAdders) == 0 {
|
||||
err = gerror.New("queue.rocketmq.nameSrvAdders is empty.")
|
||||
return
|
||||
}
|
||||
mqClient, err = RegisterRocketProducer(config.Rocketmq.Address, groupName, config.Retry)
|
||||
mqClient, err = RegisterRocketProducer()
|
||||
case "kafka":
|
||||
if len(config.Kafka.Address) == 0 {
|
||||
err = gerror.New("queue kafka address is not support")
|
||||
@@ -142,7 +146,6 @@ func NewProducer(groupName string) (mqClient MqProducer, err error) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
mqProducerInstanceMap[groupName] = mqClient
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -155,11 +158,11 @@ func NewConsumer(groupName string) (mqClient MqConsumer, err error) {
|
||||
|
||||
switch config.Driver {
|
||||
case "rocketmq":
|
||||
if len(config.Rocketmq.Address) == 0 {
|
||||
err = gerror.New("queue.rocketmq.address is empty.")
|
||||
if len(config.Rocketmq.NameSrvAdders) == 0 {
|
||||
err = gerror.New("queue.rocketmq.nameSrvAdders is empty.")
|
||||
return
|
||||
}
|
||||
mqClient, err = RegisterRocketConsumer(config.Rocketmq.Address, groupName)
|
||||
mqClient, err = RegisterRocketConsumer()
|
||||
case "kafka":
|
||||
if len(config.Kafka.Address) == 0 {
|
||||
err = gerror.New("queue kafka address is not support")
|
||||
@@ -176,7 +179,7 @@ func NewConsumer(groupName string) (mqClient MqConsumer, err error) {
|
||||
return item, nil
|
||||
}
|
||||
|
||||
clientId := "HOTGO-Consumer-" + groupName
|
||||
clientId := "hotgo-consumer-" + groupName
|
||||
if config.Kafka.RandClient {
|
||||
clientId += "-" + randTag
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ func (r *RedisMq) SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -119,7 +118,6 @@ func (r *RedisMq) SendDelayMsg(topic string, body string, delaySecond int64) (mq
|
||||
_, _ = conn.Expire(ctx, timePiece, r.timeout+delaySecond)
|
||||
_, _ = conn.Expire(ctx, key, r.timeout)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,42 +8,114 @@ package queue
|
||||
import (
|
||||
"context"
|
||||
"github.com/apache/rocketmq-client-go/v2"
|
||||
"github.com/apache/rocketmq-client-go/v2/admin"
|
||||
"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/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/grpool"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/utility/simple"
|
||||
"hotgo/utility/validate"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RocketMq struct {
|
||||
endPoints []string
|
||||
producerIns rocketmq.Producer
|
||||
consumerIns rocketmq.PushConsumer
|
||||
}
|
||||
|
||||
// rewriteLog 重写日志
|
||||
func rewriteLog() {
|
||||
rlog.SetLogger(&RocketMqLogger{Flag: "[rocket_mq]", LevelLog: g.Cfg().MustGet(ctx, "queue.rocketmq.logLevel", "debug").String()})
|
||||
type RocketManager struct {
|
||||
Producer *RocketMq
|
||||
Consumer *RocketMq
|
||||
pMutex sync.Mutex
|
||||
cMutex sync.Mutex
|
||||
goPool *grpool.Pool
|
||||
}
|
||||
|
||||
var rocketManager = &RocketManager{}
|
||||
|
||||
func init() {
|
||||
setRocketCloseEvent()
|
||||
}
|
||||
|
||||
func setRocketCloseEvent() {
|
||||
simple.Event().Register(consts.EventServerClose, func(ctx context.Context, args ...interface{}) {
|
||||
if rocketManager == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if rocketManager.Producer != nil {
|
||||
err := rocketManager.Producer.producerIns.Shutdown()
|
||||
if err != nil {
|
||||
Logger().Warningf(ctx, "rocketmq producer close err:%v", err)
|
||||
return
|
||||
}
|
||||
Logger().Debug(ctx, "rocketmq producer close...")
|
||||
}
|
||||
|
||||
if rocketManager.Consumer != nil {
|
||||
err := rocketManager.Consumer.consumerIns.Shutdown()
|
||||
if err != nil {
|
||||
Logger().Warningf(ctx, "rocketmq consumer close err:%v", err)
|
||||
return
|
||||
}
|
||||
Logger().Debug(ctx, "rocketmq consumer close...")
|
||||
}
|
||||
|
||||
for rocketManager.goPool != nil && rocketManager.goPool.Size() != 0 {
|
||||
g.Log().Debugf(ctx, "waiting for eocketmq consumer to complete execution[%v][%v]...", rocketManager.goPool.Size(), rocketManager.goPool.Jobs())
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func GetRocketManager() *RocketManager {
|
||||
return rocketManager
|
||||
}
|
||||
|
||||
// RegisterRocketProducer 注册并启动生产者接口实现
|
||||
func RegisterRocketProducer(endPoints []string, groupName string, retry int) (client MqProducer, err error) {
|
||||
rewriteLog()
|
||||
client, err = RegisterRocketMqProducer(endPoints, groupName, retry)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
func RegisterRocketProducer() (client MqProducer, err error) {
|
||||
return RegisterRocketMqProducer()
|
||||
}
|
||||
|
||||
// RegisterRocketConsumer 注册消费者
|
||||
func RegisterRocketConsumer(endPoints []string, groupName string) (client MqConsumer, err error) {
|
||||
rewriteLog()
|
||||
client, err = RegisterRocketMqConsumer(endPoints, groupName)
|
||||
if err != nil {
|
||||
func RegisterRocketConsumer() (client MqConsumer, err error) {
|
||||
return RegisterRocketMqConsumer()
|
||||
}
|
||||
|
||||
// createTopicIfNotExists 主题不存在就自动创建
|
||||
func (r *RocketMq) createTopicIfNotExists(topic string) (err error) {
|
||||
if len(config.Rocketmq.BrokerAddr) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
client, err := admin.NewAdmin(
|
||||
admin.WithResolver(primitive.NewPassthroughResolver(config.Rocketmq.NameSrvAdders)),
|
||||
admin.WithCredentials(primitive.Credentials{
|
||||
AccessKey: config.Rocketmq.AccessKey,
|
||||
SecretKey: config.Rocketmq.SecretKey,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
result, err := client.FetchAllTopicList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validate.InSlice(result.TopicList, topic) {
|
||||
return
|
||||
}
|
||||
|
||||
Logger().Debugf(ctx, "create topic:%v", topic)
|
||||
err = client.CreateTopic(ctx, admin.WithTopicCreate(topic), admin.WithBrokerAddrCreate(config.Rocketmq.BrokerAddr))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,7 +130,7 @@ func (r *RocketMq) SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err erro
|
||||
return mqMsg, gerror.New("rocketMq producer not register")
|
||||
}
|
||||
|
||||
result, err := r.producerIns.SendSync(context.Background(), &primitive.Message{
|
||||
result, err := r.producerIns.SendSync(ctx, &primitive.Message{
|
||||
Topic: topic,
|
||||
Body: body,
|
||||
})
|
||||
@@ -79,9 +151,29 @@ func (r *RocketMq) SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err erro
|
||||
return mqMsg, nil
|
||||
}
|
||||
|
||||
func (r *RocketMq) SendDelayMsg(topic string, body string, delaySecond int64) (mqMsg MqMsg, err error) {
|
||||
err = gerror.New("implement me")
|
||||
return
|
||||
func (r *RocketMq) SendDelayMsg(topic string, body string, delayTimeLevel int64) (mqMsg MqMsg, err error) {
|
||||
if r.producerIns == nil {
|
||||
return mqMsg, gerror.New("rocketMq producer not register")
|
||||
}
|
||||
|
||||
msg := primitive.NewMessage(topic, []byte(body))
|
||||
msg.WithDelayTimeLevel(int(delayTimeLevel))
|
||||
|
||||
result, err := r.producerIns.SendSync(ctx, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.Status != primitive.SendOK {
|
||||
return mqMsg, gerror.Newf("rocketMq producer send msg error status:%v", result.Status)
|
||||
}
|
||||
|
||||
mqMsg = MqMsg{
|
||||
RunType: SendMsg,
|
||||
Topic: topic,
|
||||
MsgId: result.MsgID,
|
||||
Body: []byte(body),
|
||||
}
|
||||
return mqMsg, nil
|
||||
}
|
||||
|
||||
// ListenReceiveMsgDo 消费数据
|
||||
@@ -90,13 +182,22 @@ func (r *RocketMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg))
|
||||
return gerror.New("rocketMq consumer not register")
|
||||
}
|
||||
|
||||
rocketManager.cMutex.Lock()
|
||||
defer rocketManager.cMutex.Unlock()
|
||||
|
||||
if err = r.createTopicIfNotExists(topic); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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,
|
||||
rocketManager.goPool.Add(ctx, func(ctx context.Context) {
|
||||
receiveDo(MqMsg{
|
||||
RunType: ReceiveMsg,
|
||||
Topic: item.Topic,
|
||||
MsgId: item.MsgId,
|
||||
Body: item.Body,
|
||||
})
|
||||
})
|
||||
}
|
||||
return consumer.ConsumeSuccess, nil
|
||||
@@ -114,53 +215,96 @@ func (r *RocketMq) ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg))
|
||||
}
|
||||
|
||||
// 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,
|
||||
func RegisterRocketMqProducer() (mqIns *RocketMq, err error) {
|
||||
if rocketManager.Producer != nil {
|
||||
return rocketManager.Producer, nil
|
||||
}
|
||||
|
||||
rocketManager.pMutex.Lock()
|
||||
defer rocketManager.pMutex.Unlock()
|
||||
|
||||
if rocketManager.Producer != nil {
|
||||
return rocketManager.Producer, nil
|
||||
}
|
||||
|
||||
mqIns = new(RocketMq)
|
||||
retry := config.Rocketmq.Retry
|
||||
if retry <= 0 {
|
||||
retry = 0
|
||||
}
|
||||
|
||||
mqIns.producerIns, err = rocketmq.NewProducer(
|
||||
producer.WithNameServer(addr),
|
||||
producer.WithNsResolver(primitive.NewPassthroughResolver(config.Rocketmq.NameSrvAdders)),
|
||||
producer.WithRetry(retry),
|
||||
producer.WithGroupName(groupName),
|
||||
producer.WithGroupName(config.GroupName),
|
||||
producer.WithCredentials(primitive.Credentials{
|
||||
AccessKey: config.Rocketmq.AccessKey,
|
||||
SecretKey: config.Rocketmq.SecretKey,
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = mqIns.producerIns.Start()
|
||||
if err != nil {
|
||||
if err = mqIns.producerIns.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mqIns, nil
|
||||
|
||||
_, err = mqIns.producerIns.SendSync(ctx, primitive.NewMessage("hotgo-ping", []byte("1")))
|
||||
if err != nil {
|
||||
err = gerror.Newf("连通性测试不通过,请检查`queue.rocketmq.nameSrvAdders`或权限配置是否有误。err:%+v", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
SetRLogLevel()
|
||||
rocketManager.Producer = mqIns
|
||||
return rocketManager.Producer, nil
|
||||
}
|
||||
|
||||
// RegisterRocketMqConsumer 注册rocketmq消费者
|
||||
func RegisterRocketMqConsumer(endPoints []string, groupName string) (mqIns *RocketMq, err error) {
|
||||
addr, err := primitive.NewNamesrvAddr(endPoints...)
|
||||
if err != nil {
|
||||
func RegisterRocketMqConsumer() (mqIns *RocketMq, err error) {
|
||||
if rocketManager.Consumer != nil {
|
||||
return rocketManager.Consumer, nil
|
||||
}
|
||||
|
||||
rocketManager.cMutex.Lock()
|
||||
defer rocketManager.cMutex.Unlock()
|
||||
|
||||
if rocketManager.Consumer != nil {
|
||||
return rocketManager.Consumer, nil
|
||||
}
|
||||
|
||||
// 利用生产者检查一下连通性
|
||||
if _, err = RegisterRocketMqProducer(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mqIns = &RocketMq{
|
||||
endPoints: endPoints,
|
||||
}
|
||||
|
||||
mqIns = new(RocketMq)
|
||||
mqIns.consumerIns, err = rocketmq.NewPushConsumer(
|
||||
consumer.WithNameServer(addr),
|
||||
consumer.WithConsumerModel(consumer.Clustering),
|
||||
consumer.WithGroupName(groupName),
|
||||
consumer.WithNsResolver(primitive.NewPassthroughResolver(config.Rocketmq.NameSrvAdders)),
|
||||
consumer.WithGroupName(config.GroupName),
|
||||
consumer.WithCredentials(primitive.Credentials{
|
||||
AccessKey: config.Rocketmq.AccessKey,
|
||||
SecretKey: config.Rocketmq.SecretKey,
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mqIns, nil
|
||||
|
||||
// 开多携程处理消费任务,可以根据业务实际情况调整该配置
|
||||
rocketManager.goPool = grpool.New(5)
|
||||
|
||||
SetRLogLevel()
|
||||
rocketManager.Consumer = mqIns
|
||||
return rocketManager.Consumer, nil
|
||||
}
|
||||
|
||||
// SetRLogLevel 设置rocketmq日志输出等级
|
||||
func SetRLogLevel() {
|
||||
level := g.Cfg().MustGet(ctx, "queue.rocketmq.logLevel", "all").String()
|
||||
rlog.SetLogLevel(level)
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// Package queue
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package queue
|
||||
|
||||
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" {
|
||||
Logger().Debug(ctx, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RocketMqLogger) Level(level string) {
|
||||
Logger().Info(ctx, level)
|
||||
}
|
||||
|
||||
func (l *RocketMqLogger) OutputPath(path string) (err error) {
|
||||
Logger().Info(ctx, 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" {
|
||||
Logger().Info(ctx, 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" {
|
||||
Logger().Warning(ctx, 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" {
|
||||
Logger().Error(ctx, 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" {
|
||||
Logger().Fatal(ctx, msg)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user