redis实战(三)

redis使用场景(list)

前言

在开始redis list使用场景之前,让我们回顾一下redis 的list数据结构。除了特殊场景下(内容较小使用压缩链表),都是采用linkedList,那么linkedList的数据结构固然对我们的使用场景起着决定性因素。无序链表决定我们不能随机取出队列中的某个元素,只能从头或者尾取数据。也就是我们的队列也只能保证这种规则,请看官方给出的redis-list相关命令。

//  入队操作  
#将1 从队列右边放入1
rpush mykey 1
#将1 从队列右边放入1  如果队列不存在 不做任何操作
rpushx mykey 1
#将1 从队列左边放入1
lpush mykey 1
#将1 从队列左边放入1  如果队列不存在 不做任何操作
lpushx mykey 1

//出队操作
#从队列左边取出一条数据
lpop mykey 
#从队列右边取出一条数据
rpop mykey 
#从队列左边取出一条数据 并把数据从右边放进去
lpoprpush mykey
#从队列右边取出一条数据 并把数据从左边放进去
rpoplpush mykey

//骚操作
# b block 阻塞的意思 所以 顾名思义 阻塞拿数据 如果队列为空 将进行阻塞(可以指定时间)
blpop mykey 0
brpop mykey 0

正文

我们已经了解了redis操作list的基本命令,已经可以通过客户端愉快地操作redis的队列了。但是有没有小伙伴在疑惑,目前已经有那么成熟的队列可以使用了(rabbitMq、rocketMq、kafka)等,为什么还要用redis的队列呢?redis队列有什么牛逼的呢?答案是没有!任何的技术没有好与不好只有适不适合。不同的应用场景会采取不同的技术方案,这也是为什么我们要学习多种技术(为了混口饭吃呀!!!)。我这边给出了一张表,以rabbitMQ为例,列出了二者的对比。

 

 RabbitMQredis队列
可靠性可靠不可靠(自行实现)
高可用支持支持
持久化可选择整个redis实力都会被持久化
消费者负载均衡支持不支持(自行实现)
队列监控有控制台
流量控制可控不可控
性能较低较高
总体牛逼一般

为什么要使用redis队列?

大家看了上边的对比图,就会发现rabbitMQ好强大呀,用什么redis队列,啥玩意都要自己搞。那为什么不是所有的场景都用rabbitMQ呢?     第一:维护难。rabbitMQ是一个中间件,从单机到集群都需要搭建以及运维。     第二:成本大。rabbitMQ属于CS服务,那么服务端会占用不少的资源。说白了,如果你足够有钱,足够膨胀那就所有场景都RabbitMQ(如果真有这样的,请让我和你做朋友),^_^

实现一个redis队列

想法:redis队列的入队就比较简单,往里边放就行了。而客户端取数据,就需要启动一个定时任务,定时去取redis队列中的数据。 基于这个简单的想法,我们上代码。

//生产者
public class RedisQueueProducer{
    
    @Autowired
    private RedisTemplate redisTemplate;

     /**
     * 入队
     */
    public void add( Object o) {
        redisTemplate.opsForList().leftPush("mykey", e);
    }

}

//消费者
pubic class Consumer{
    @Autowired
    private RedisTemplate redisTemplate;

     /**
     * 出队
     */
    public Object pop() {
        return redisTemplate.opsForList().rightPop("mykey");

    //消费
    public void consume(){
        //循环消费队列
        while(true){
            log.info(pop());
        }
    }
}

细心的同学已经发现了上边代码的问题。当队列为空的了,消费者还在不断的去pop队列,浪费了CPU资源并且对redis性能有所影响。那么我们如何解决呢?

if(pop() == null){
    //如果发现队列为空 进行休眠
    Thread.sleep(100);
}

这样万事大吉了吗? 当你读到这句话,就已经明白战斗还没有结束。休眠难道不对吗?感觉貌似没啥问题呀! 是的,这样确实没啥性能问题,但是休眠的时间如何界定?太长——数据不实时  太短——性能影响太大。 怎么办呢? 还记得上边redis命令中的一个骚操作吗?何为骚操作,就是在你觉得无解的时候,嗅到了出路。

//brpop/blpop 这两个命令上边已经说了 阻塞取数据,
//使用此命令会阻塞队列,每次弹出一个消息,如果没有消息会阻塞,
//把阻塞时间设置为0,就是没有消息就一直阻塞,只要有消息为止。
//这样不仅解决了性能问题,也解决了消费实时问题,代码如下

    public Object pop(){
        //一直阻塞
        return redisTemplate.opsForList().rightPop(key, 0, TimeUnit.SECONDS);
    }

    //消费
    public void consume(){
        //循环消费队列
        while(true){
            log.info(pop());
        }
    }

如何实现ACK

为什么要ack?解决worker拿到消息然后挂了,导致该消息从队列移除,但是没有实现自己的价值。怎么解决呢?

  • 维护一个队列,一个表

  • 当出队后, 分配一个work线程去处理消息(给消息增加当前时间戳以及当前线程名称),然后写入表

  • 入表后,work线程消费消息,然后移除表中对应数据

  • 启动定时任务扫表,如果消息中的时间戳超时,检查是否work线程是否存在,如果存在取消任务,回滚事务,把消息重新入队

  • 如果消费者业务处理失败,主动回滚,把消息重新入队

结语

消息队列作为我们目前最火热的解耦、削峰方案。不管是redis队列、rabbitMQ、rocketMQ还是kafka,任何的技术都有自己的优缺点,所以没有最好的技术,只有最好的使用场景。

关注不迷路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值