想要看更加舒服的排版、更加准时的推送
关注公众号“不太灵光的程序员”
每日八点有干货推送
公众号“不太灵光的程序员” 同时发布 《基于Redis实现消息队列的6种方案之方案简述(上)》
阅读原文
大家好,这次我会带大家根据实际的业务场景来学习下基于Redis是如何实现消息队列的,因为大多数关注我的朋友都是初学者,我会从比较基础的知识点讲起。
有那么多的消息队列产品为什么要用Redis去实现呢??
不光市面上的消息队列产品多,而且很多东西都是可以拿来做消息队列服务的,可以存储的都可以做队列的,比如文件、数据库、管道啊都是可以做队列的。
Redis做消息队列就因为简单封装下LPUSH和RPOP就可以支撑百万日活的服务;缓存服务大家都需要用的,开发也不用再次学习;部署一套也就搞定了。
Redis支持集群部署的,读写性能满足不了,加机器!容易宕机稳定性不了,加机器!!上万台服务器才会产生的集群瓶颈也不是一般公司能达到的集群规模。
Redis还是单线程的,就根本不会有竞争的情况出现,都这么简单了为啥不用呢。
Redis本来都不做消息队列的,最后被自己打败了,在Redis5.0中增加了Stream类型对消息队列进行完善的实现。
1 基于Redis实现消息队列的6种方案
- 基于List的 LPUSH+RPOP 的实现
- 基于List的 LPUSH+BRPOP 的实现
- 基于List的 LPUSH+LRANGE+RPOP 的实现
- 基于List的 LPUSH+BRPOPLPUSH+LREM 的实现
- 基于Sorted Set 的实现
- PUB/SUB,订阅/发布模式
- 基于Stream类型的实现
一、基于List的 LPUSH+RPOP 的实现方案
- LPUSH在头部(List的左边)添加一个新的元素并返回List长度,充当消息队列中的生成者。
- RPOP在尾部(List的右边)删除一个元素并返回该元素的值,充当消息队列中的消费者。
- 基于List类型的插入删除元素操作实现,就是一个典型的先进先出队列的解决方案。
优点:
- List类型是基于链表实现,插入删除元素时间复杂度仅为常量级,有先进先出的特点来保证数据的顺序
- Redis支持消息持久化,在服务端数据是安全的
缺点:
- 消费确认机制实现麻烦加之不能重复消费,一但消费数据就会删除,导致客户端数据是不安全的,当客户端宕机、网络断开会出现数据丢失,也不能实现广播模式
- 没有数据权重的概念,只能先进先出
- 当队列中没有元素时,消费者需要轮询获取数据,会增加Redis的访问压力,增加客户端的cpu占用
- 不支持分组消费
二、基于List的 LPUSH+BRPOP 的实现方案
方案二是在方案一上针对队列没有元素时造成服务器资源浪费进行的优化方案,使用了BRPOP做消费者,BRPOP是阻塞的。
消费者可以设置数据不存在时的阻塞时间,来减少不必要的轮询。
三、基于List的 LPUSH+LRANGE+RPOP 的实现
方案三是在方案一上针对客户端数据安全进行的优化方案,使用LRANGE首先对队列元素只做读取不做消费,在客户端消费完成后,再使用RPOP对服务端进行消费。
由于LRANGE不是阻塞的就又回到了方案二解决的资源浪费问题上了,无法减少不必要的轮询。
还存在重复执行的问题,由于先读再消费,在消费者宕机重启后会再次读到没有确认消费的但是已经在消费者处理过的元素,就有了重复消费的风险。真的是绕啊。
四、基于List的 LPUSH+BRPOPLPUSH+LREM 的实现
方案四是也对客户端数据安全进行的优化方案,是一种安全的队列,虽然也会存在重复消费的风险,但是元素队列的操作都是在服务端进行的,问题发生的概率会大大降低。
首先消息队列数量增加了,一个存储待消费消息的队列暂且称为A,一个存储正在消费消息的队列暂且称为B,关键在与BRPOPLPUSH操作,
RPOPLPUSH是将A中队尾的消息删除(消费掉)并添加到B队列的队头。
BRPOPLPUSH是原子性的操作,所以不用担心从A中消费后数据丢失的问题。
BRPOPLPUSH是阻塞的,不过你也可以使用RPOPLPUSH非阻塞模式。
这个方案当然也不是完美的,还是存在客户端宕机的情况,正在处理中的队列存在长期不消费的消息怎么办?
可以再添加一台客户端来监控长期不消费的消息,重新将消息打回待消费的队列,这个可以使用循环队列的模式来实现。
懂了啵!!!
内容太多了,将会写成系列文章
感觉用就点个关注、点个看一看噢