Redis队列——消息队列,延迟队列

异步消息队列

  1. 使用的数据结构: list
    在这里插入图片描述
  2. 主要实现: go实现简单消息队列
package main

import (
	"encoding/json"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"os"
)

type Producer struct {
	// 生产者
}

func (p *Producer)publish(conn redis.Conn, listKey string, data string) (reply interface{}, err error){
	return conn.Do("lpush", listKey, data)
}

type Customer struct {
	// 消费者
}

func (c *Customer)putMessage(conn redis.Conn, listKey string) (interface{}, error) {
	return conn.Do("rpop", listKey)
}

func (c *Customer)getCount(conn redis.Conn, listKey string) (interface{}, error) {
	return conn.Do("llen", listKey)
}

func HandlecError(err error, when string)  {
	if err != nil{
		fmt.Println("error happen at", when)
		os.Exit(500)
	}else {
		fmt.Println("连接成功")
	}
}

func main()  {
	// 连接redis操作
	conn, err := redis.Dial("tcp","127.0.0.1:6379")
	HandlecError(err, "connect")
	defer func() {
		_ = conn.Close()
	}()

	producer := Producer{}
	personMap := make(map[string]interface{})
	personMap["name"]  = "hxh"
	personMap["work"] = "toDoSomething"
	bytes, _ := json.Marshal(personMap)
	_,_ = producer.publish(conn, "test_queue", string(bytes))

	customer := Customer{}
	num, _ := customer.getCount(conn, "test_queue")
	fmt.Println("队列数量为", num)

	values, err := redis.String(customer.putMessage(conn,"test_queue"))
	dataMap := make(map[string]interface{})	// 准备好map来装
	_ = json.Unmarshal([]byte(values), &dataMap)
	fmt.Println(dataMap["work"])

}	
  1. 问题:
    • 空队列: 如果队列为空,客户端会不断pop空轮询,这样拉高客户端的cpu和服务器redis的qps

      可以sleep一下

    • 队列延迟问题: 使用阻塞读blpop/brpop,在队列没有数据的时候就会进入休眠状态
    • 空闲断开问题: 线程一直阻塞在哪里,Redis 的客户端连接就成了闲置连接,闲置过久,服务器一般会主动断开连接,减少闲置资源占用。这个时候 blpop/brpop 会抛出异常来。要对其进行错误处理
  2. 缺点: 不能保证消息可靠(没有 ack 保证)

延迟消息队列

  1. 使用的数据结构: zset
  2. 主要实现: 将消息序列化成一个字符串作 为 zset 的 value,这个消息的到期处理时间作为 score,然后用多个线程轮询 zset 获取到期 的任务进行处理.
  3. 问题
    • 保障可用性,万一挂了一个线程还有其它线程可以继续处理。
    • 并发争抢任务,确保任务不能被多次执行。
  4. 实现: go实现简单延迟队列
package main

import (
	"encoding/json"
	"fmt"
	"github.com/gomodule/redigo/redis"
	uuid "github.com/satori/go.uuid"
	"os"
	"sync"
	"time"
)

type delayQueue struct {
	// 延迟队列
}

func (d *delayQueue) publish(conn redis.Conn, zSetKey string, dataMap map[string]interface{}, time int64) (reply interface{}, err error) {
	// 生成唯一id,保证zset的每一个value都不一样,time为执行的时间戳
	dataMap["uuid"] = uuid.NewV4().String()
	bytes, _ := json.Marshal(dataMap)
	return conn.Do("zadd", zSetKey, time, string(bytes))
}

func (d *delayQueue) customer(conn redis.Conn,  zSetKey string) {
	for true {
		now := time.Now().Unix()
		data, err := redis.Strings(conn.Do("zrangebyscore", zSetKey, 0, now,  "limit", 0, 1))
		if err == nil &&  len(data) > 0 {
			res, delErr := conn.Do("zrem", "test-delay-queue", data[0])
			if res.(int64) >= 1 && delErr == nil {
				dataMap := make(map[string]interface{}) // 准备好map来装
				_ = json.Unmarshal([]byte(data[0]), &dataMap)
				fmt.Println("任务是:", dataMap["work"])
			}else {
				fmt.Println(delErr)
			}
		}else {
			time.Sleep(time.Second * 10)
			continue
		}
	}
}

func HandlecError(err error, when string) {
	if err != nil {
		fmt.Println("error happen at", when)
		os.Exit(500)
	} else {
		fmt.Println("连接成功")
	}
}

func main() {
	var wg sync.WaitGroup

	// 连接redis操作
	conn, err := redis.Dial("tcp", "127.0.0.1:6379")
	HandlecError(err, "connect")
	defer func() {
		_ = conn.Close()
	}()

	delayQueue := delayQueue{}
	personMap := make(map[string]interface{})
	personMap["name"] = "hxh"
	personMap["work"] = "toDoSomething"
	_,_ = delayQueue.publish(conn, "test-delay-queue", personMap, time.Now().Unix())

	wg.Add(1)
	go func() {
		delayQueue.customer(conn, "test-delay-queue")
		wg.Done()
	}()
	wg.Wait()

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值