RabbitMQ
RabbitMQ刷盘机制
- 异步
- 写入文件前会有一个Buffer,大小为1M(1048576),数据在写入文件时,首先会写入到这个Buffer,如果Buffer已满,则会将Buffer写入到文件(此时写入到内核态缓存中, 未必刷到磁盘);
- 有个固定的刷盘时间:25ms,也就是不管Buffer满不满,每隔25ms,Buffer里的数据及未刷新到磁盘的文件内容必定会刷到磁盘(调用系统fsync);
- 每次消息写入后,如果没有后续写入请求,则会直接将已写入的消息刷到磁盘:使用Erlang的receive x after 0来实现,只要进程的信箱里没有消息,则产生一个timeout消息,而timeout会触发刷盘操作
- 同步
Broker接收到消息后立即同步到磁盘
RabbitMQ如何保证消息不丢失
- 发送者确认
对于rabbitMQ,发布者确认需要手动开启,默认是没有开启的,也就是说默认情况下我们消息发出去就完事儿了,但是服务端有没有成功处理并不一定,此时虽然消息发布成功但是broker可能并没有正确处理该消息导致消息丢失,所以为了消息的可靠性,我们需要开启发布确认; 开启了发布确认后,在我们发布完消息后服务端会在接收到消息并成功处理后返回给我们一个ack,表示消息接收处理完毕(注意,只是broker端处理完毕,不是消费者消费完成) 而broker的ack时机对于我们很关键,broker会在以下时机给我们返回ack:如果未开启消息持久化,则消息在被路由到各个队列(如果是镜像队列,这意味着所有镜像队列都成功接收到消息)后broker返回ack; 如果开启消息持久化,则消息在被路由到各个队列并且所有需要持久化的队列持久化完成后返回ack;而如果其中某部分队列持久化未完成则broker会返回nack,表示服务端接收失败,需要生产者重新发布消息; 注意:此时虽然部分队列持久化失败,但是持久化成功的那部分是不会回滚的,也就是说对于持久化成功的队列上绑定的消费者可能会重复收到消息,此时消费者应该去重;
可以看出,发布者确认可以解决消息异步持久化中的问题,这两项措施保证了我们的消息肯定会发布成功并且不会丢失,后续就是消费者的事情了; 注意:其实以上措施只能保证在硬件正常的情况下消息不会丢失,而如果broker是单点部署的,则这个broker的磁盘损坏仍然会导致数据丢失,而如果broker是集群部署的,如果集群中所有broker的磁盘都损坏,此时消息也会丢失,由于硬件故障是无法避免的,只能根据消息的重要性做集群,集群规模越大、磁盘可靠性越高,消息丢失的概率越小;
- 消费者确认
- 队列/交换机持久化
- 消息持久化
- 开启同步刷盘, 性能存在一定影响
- 消息存入数据库, 定时扫描重发
- 主从配置, 开启同步复制
RocketMQ
刷盘机制
- 异步
写入缓存返回成功, 由异步线程进行刷盘
- 同步
写入磁盘返回成功
如何保证消息不丢失 思路与RabbitMQ一致
RocketMQ最佳实践 https://github.com/apache/rocketmq/blob/master/docs/cn/best_practice.md