基于Redis实现分布式消息队列(3)

1、Redis是什么鬼?

Redis是一个简单的,高效的,分布式的,基于内存的缓存工具。 假设好服务器后,通过网络连接(类似数据库),提供Key-Value式缓存服务。

简单,是Redis突出的特色。 简单可以保证核心功能的稳定和优异。

2、性能

性能方面:Redis是足够高效的。 和Memecached对比,在数据量较小大情况下,Redis性能更优秀。 数据量大到一定程度的时候,Memecached性能稍好。

简单结论:但总体上讲Redis性能已经足够好。

// Ref: Redis性能测试 原则:Value大小不要超过1390Byte。

经实验得知: List操作和字符串操作性能相当,略差,几乎可以忽略。 使用Jedis自带pool,“每次从pool中取用完放回“ 和 “重用单个连接“ 相比,平均用时是3倍。这部分需要继续研究底层机制,采用更合理的实验方法进一步获得数据。 使用Jedis自带pool,性能上是满足当前访问量需要的,等有时间了再进一步深入。

3、数据类型

Redis支持5种数据类型:字符串、Map、List、Set、Sorted Set。 List特别适合用于实现队列。提供的操作包括: 从左侧(或右侧)放入一个元素,从右侧(或左侧)取出一个元素,读取某个范围的元素,删除某个范围的元素。

Sorted Set中元素是唯一的,可以通过名字找。 Map可以高效地通过key找。 假如我们需要实现finishTash(taskId),需要通过名字在队列中找元素,上面两个可能会用到。

4、原子操作

实现分布式队列首要问题是:不能出现并发问题。

Redis是底层是单线程的,命令执行是原子操作,支持事务,契合了我们的需求。

Redis直接提供的命令都是原子操作,包括lpush、rpop、blpush、brpop等。

Redis支持事务。通过类似 begin…[cancel]…commit的语法,提供begin…commit之间的命令为原子操作的功能,之间命令对数据的改变对其他操作是不可见的。类似关系型数据库中的存储过程,同时提供了最高级别的事务隔离级别。

Redis支持脚本,每个脚本的执行是原子性的。

做了一下并发测试: 写了个小程序,随机对List做push或pop操作,push的比pop的稍多。 记录每次处理的详细信息到数据库。 最后把List中数据都pop出来,详细记录每次pop详细信息。 统计push和pop是否相等,统计针对每条数据是否都有push和pop。 500并发,没有出现并发问题。

5、集群

实现分布式队列另一个重要问题是:不能出现单点故障。

Redis支持Master-Slave数据复制,从服务器设置 slave-of master-ip:port 即可。 集群功能可以由客户端提供。 客户端使用哨兵,可自动切换主服务器。

由于队列操作都是写操作,从服务器主要目的是备份数据,保证数据安全。

如果想基于 sharding 做多master集群,可以结合 zookeeper 自己做。

Redis 3.0支持集群了,还没细看,应该是个好消息,等大家都用起来,没什么问题的话,可以考虑试试看。

如果 master 宕掉,怎么办? “哨兵”会选出一个新的master来。产生过程中,消息队列暂停服务。 最极端的情况,所有Redis都停了,当消息队列发现Redis停止响应时,对业务系统的请求应抛出异常,停止队列服务。 这样会影响业务,业务系统下订单、审批等操作会失败。如果可以接受,这是一种方案。 Redis整个集群宕掉,这种情况很少发生,如果真发生了,业务系统停止服务也是可以理解的。

如果想要在Redis整个集群宕掉的情况下,消息队列仍继续提供服务。 方法是这样的: 启用备用存储机制,可以是zookeeper、可以是关系型数据库、可以是另外可用的Memecached等。 本地内存存储是不可取的,首先,同步多个客户端虚拟机内存数据太复杂,相当于自己实现了一个Redis,其次,保证内存数据存储安全太复杂。 备用存储机制相当于实现了另外一个版本的消息队列,逻辑一致,底层存储不同。这个实现可以性能低一些,保证最基本的原则即可。 想要保证不出现并发问题,由于消息队列程序同时运行在多个虚拟机中,对象锁、方法锁无效。需要有一个独立于虚拟机的锁机制,zookeeper是个好选择。 将关系型数据库设置为最高级别的事务隔离级别,太傻了。除了zk有其他好办法吗?

Redis集群整个宕掉的同时Zookeeper也全军覆没怎么办? 这个问题是没有尽头的,提供了第二备用存储、第三备用存储、第四备用存储、…,理论上也会同时宕掉,那时候怎么办? 有钱任性的土豪可以继续,预算有限的情况,能做到哪步就做到哪步。

6、持久化

分布式队列的应用场景和缓存的应用场景是不一样的。

如果有没来得及持久化的数据怎么办? 从业务系统的角度,已经成功发送给消息队列了。 消息队列也以为Redis妥妥地收好了。 可Redis还没写到日记里,更没有及时通知小伙伴,挂了。可能是断电了,可能是进程被kill了。

后果会怎样? 已经执行过的任务会再次执行一遍。 已经放到队列中的任务,消失了。 标记为已经完成的任务,状态变为“进行中”了,然后又被执行了一遍。 后果不可接受。

分布式队列不允许丢数据。 从业务角度,哪怕丢1条数据也是无法接受的。 从运维角度,Redis丢数据后,如果可以及时发现并补救,也是可以接受的。

从架构角度,队列保存在Redis中,业务数据(包括任务状态)保存在关系型数据库中。 任务状态是从业务角度确定的,消息队列不应该干涉。如果业务状态没有统一的规范和定义,从业务数据比对任务队列是否全面正确,就只能交给业务开发方来做。 从分工上来看,任务队列的目的是管理任务执行的状态,业务系统把这个职责交给了任务队列,业务系统自身的任务状态维护未必准确。 结论:任务队列不能推卸责任,不能丢数据是核心功能,不能打折扣。

采用 Master-Slave 数据复制模式,配置bgsave,追加存储到aof。

在从服务器上配置bgsave,不影响master性能。

队列操作都是写操作,master任务繁重,能让slave分担的持久化工作,就不要master做。

rdb和aof两种方法都用上,多重保险。 appendfsync设为always。// 单节点测性能,连续100000次算平均时间,和per second比对,性能损失不大。 性能会有些许损失,但任务执行为异步操作,无需用户同步等待,为了保证数据安全,这样是值得的。

上帝从不埋怨人们的愚昧,人们却埋怨上帝的不公平

基于Redis实现分布式消息队列(3)

相关文章:

你感兴趣的文章:

标签云: