hotgo/server/internal/library/queue/redismq.go

288 lines
6.4 KiB
Go
Raw Normal View History

2022-02-25 17:11:17 +08:00
package queue
import (
"encoding/json"
"fmt"
"github.com/bufanyun/pool"
"github.com/gogf/gf/v2/errors/gerror"
2022-11-24 23:37:34 +08:00
"github.com/gogf/gf/v2/frame/g"
2022-02-25 17:11:17 +08:00
"github.com/gomodule/redigo/redis"
2022-11-24 23:37:34 +08:00
"hotgo/utility/encrypt"
2022-02-25 17:11:17 +08:00
"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{
2022-11-24 23:37:34 +08:00
RunType: SendMsg,
Topic: topic,
MsgId: msgId,
Body: body,
Timestamp: time.Now(),
2022-02-25 17:11:17 +08:00
}
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() {
2022-11-24 23:37:34 +08:00
for range time.Tick(500 * time.Millisecond) {
2022-02-25 17:11:17 +08:00
mqMsgList := r.loopReadQueue(queueName)
for _, mqMsg := range mqMsgList {
receiveDo(mqMsg)
}
}
}()
2022-11-24 23:37:34 +08:00
select {}
2022-02-25 17:11:17 +08:00
}
// 生成队列名称
func (r *RedisMq) genQueueName(groupName string, topic string) string {
return fmt.Sprintf("queue:%s_%s", groupName, topic)
2022-02-25 17:11:17 +08:00
}
func (r *RedisMq) loopReadQueue(queueName string) (mqMsgList []MqMsg) {
rdx, put, err := getRedis(r.poolName, r.retry)
defer put()
if err != nil {
2022-11-24 23:37:34 +08:00
g.Log().Warningf(ctx, "loopReadQueue getRedis err:%+v", err)
2022-02-25 17:11:17 +08:00
return
}
for {
infoByte, err := redis.Bytes(rdx.Do("RPOP", queueName))
2022-11-24 23:37:34 +08:00
if redis.ErrNil == err || len(infoByte) == 0 {
break
}
if err != nil {
g.Log().Warningf(ctx, "loopReadQueue redis RPOP err:%+v", err)
2022-02-25 17:11:17 +08:00
break
}
2022-11-24 23:37:34 +08:00
2022-02-25 17:11:17 +08:00
var mqMsg MqMsg
2022-11-24 23:37:34 +08:00
if err = json.Unmarshal(infoByte, &mqMsg); err != nil {
g.Log().Warningf(ctx, "loopReadQueue Unmarshal err:%+v", err)
break
}
2022-02-25 17:11:17 +08:00
if mqMsg.MsgId != "" {
mqMsgList = append(mqMsgList, mqMsg)
}
}
return mqMsgList
}
2023-02-08 20:29:34 +08:00
func RegisterRedisMqProducer(connOpt RedisOption, poolOpt PoolOption, groupName string, retry int) (client MqProducer, err error) {
2022-02-25 17:11:17 +08:00
client, err = RegisterRedisMq(connOpt, poolOpt, groupName, retry)
if err != nil {
2023-02-08 20:29:34 +08:00
err = gerror.Newf("RegisterRedisMqProducer err:%+v", err)
2022-11-24 23:37:34 +08:00
return
2022-02-25 17:11:17 +08:00
}
2023-02-08 20:29:34 +08:00
return
2022-02-25 17:11:17 +08:00
}
2023-02-08 20:29:34 +08:00
// RegisterRedisMqConsumer 注册消费者
func RegisterRedisMqConsumer(connOpt RedisOption, poolOpt PoolOption, groupName string) (client MqConsumer, err error) {
2022-02-25 17:11:17 +08:00
client, err = RegisterRedisMq(connOpt, poolOpt, groupName, 0)
if err != nil {
2023-02-08 20:29:34 +08:00
err = gerror.Newf("RegisterRedisMqConsumer err:%+v", err)
2022-11-24 23:37:34 +08:00
return
2022-02-25 17:11:17 +08:00
}
2023-02-08 20:29:34 +08:00
return
2022-02-25 17:11:17 +08:00
}
2022-11-24 23:37:34 +08:00
// RegisterRedisMq 注册redis实例
2022-02-25 17:11:17 +08:00
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配置
2022-11-24 23:37:34 +08:00
func registerRedis(host, pass string, dbNum int, opt PoolOption) (poolName string, err error) {
poolName = encrypt.Md5ToString(fmt.Sprintf("%s-%s-%d", host, pass, dbNum))
2022-02-25 17:11:17 +08:00
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 != "" {
2023-02-08 20:29:34 +08:00
if _, err = conn.Do("AUTH", pass); err != nil {
2022-02-25 17:11:17 +08:00
return nil, err
}
}
2022-11-24 23:37:34 +08:00
if dbNum > 0 {
2023-02-08 20:29:34 +08:00
if _, err = conn.Do("SELECT", dbNum); err != nil {
2022-02-25 17:11:17 +08:00
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
}
2023-02-08 20:29:34 +08:00
2022-02-25 17:11:17 +08:00
put = func() {
2023-02-08 20:29:34 +08:00
if err = redisPool.Put(conn); err != nil {
return
}
2022-02-25 17:11:17 +08:00
}
db = conn.(redis.Conn)
return db, put, nil
}
2023-02-08 20:29:34 +08:00
func getRandMsgId() string {
2022-02-25 17:11:17 +08:00
rand.Seed(time.Now().UnixNano())
radium := rand.Intn(999) + 1
timeCode := time.Now().UnixNano()
2023-02-08 20:29:34 +08:00
return fmt.Sprintf("%d%.4d", timeCode, radium)
2022-02-25 17:11:17 +08:00
}