关于MQ的几件小事(四)如何保证消息不丢失

1.mq原则

数据不能多,也不能少,不能多是说消息不能重复消费,这个我们上一节已解决;不能少,就是说不能丢失数据。如果mq传递的是非常核心的消息,支撑核心的业务,那么这种场景是一定不能丢失数据的。

2.丢失数据场景

丢数据一般分为两种,一种是mq把消息丢了,一种就是消费时将消息丢了。下面从rabbitmq和kafka分别说一下,丢失数据的场景,
(1)rabbitmq
A:生产者弄丢了数据
生产者将数据发送到rabbitmq的时候,可能在传输过程中因为网络等问题而将数据弄丢了。
B:rabbitmq自己丢了数据
如果没有开启rabbitmq的持久化,那么rabbitmq一旦重启,那么数据就丢了。所依必须开启持久化将消息持久化到磁盘,这样就算rabbitmq挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢失。除非极其罕见的情况,rabbitmq还没来得及持久化自己就挂了,这样可能导致一部分数据丢失。
C:消费端弄丢了数据
主要是因为消费者消费时,刚消费到,还没有处理,结果消费者就挂了,这样你重启之后,rabbitmq就认为你已经消费过了,然后就丢了数据。

(2)kafka
A:生产者弄丢了数据
生产者没有设置相应的策略,发送过程中丢失数据。
B:kafka弄丢了数据
比较常见的一个场景,就是kafka的某个broker宕机了,然后重新选举partition的leader时。如果此时follower还没来得及同步数据,leader就挂了,然后某个follower成为了leader,他就少了一部分数据。
C:消费者弄丢了数据
消费者消费到了这个数据,然后消费之自动提交了offset,让kafka知道你已经消费了这个消息,当你准备处理这个消息时,自己挂掉了,那么这条消息就丢了。

3.如何防止消息丢失

(1)rabbitmq
A:生产者丢失消息
①:可以选择使用rabbitmq提供是事物功能,就是生产者在发送数据之前开启事物,然后发送消息,如果消息没有成功被rabbitmq接收到,那么生产者会受到异常报错,这时就可以回滚事物,然后尝试重新发送;如果收到了消息,那么就可以提交事物。

  channel.txSelect();//开启事物
  try{
      //发送消息
  }catch(Exection e){
      channel.txRollback();//回滚事物
      //重新提交
  }

缺点:rabbitmq事物已开启,就会变为同步阻塞操作,生产者会阻塞等待是否发送成功,太耗性能会造成吞吐量的下降。

②:可以开启confirm模式。在生产者哪里设置开启了confirm模式之后,每次写的消息都会分配一个唯一的id,然后如何写入了rabbitmq之中,rabbitmq会给你回传一个ack消息,告诉你这个消息发送OK了;如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息失败了,你可以进行重试。而且你可以结合这个机制知道自己在内存里维护每个消息的id,如果超过一定时间还没接收到这个消息的回调,那么你可以进行重发。

    //开启confirm
    channel.confirm();
    //发送成功回调
    public void ack(String messageId){

    }

    // 发送失败回调
    public void nack(String messageId){
        //重发该消息
    }

二者不同
事务机制是同步的,你提交了一个事物之后会阻塞住,但是confirm机制是异步的,发送消息之后可以接着发送下一个消息,然后rabbitmq会回调告知成功与否。
一般在生产者这块避免丢失,都是用confirm机制。
B:rabbitmq自己弄丢了数据
设置消息持久化到磁盘。设置持久化有两个步骤:
①创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里面的数据。
②发送消息的时候讲消息的deliveryMode设置为2,这样消息就会被设为持久化方式,此时rabbitmq就会将消息持久化到磁盘上。
必须要同时开启这两个才可以。

而且持久化可以跟生产的confirm机制配合起来,只有消息持久化到了磁盘之后,才会通知生产者ack,这样就算是在持久化之前rabbitmq挂了,数据丢了,生产者收不到ack回调也会进行消息重发。
C:消费者弄丢了数据
使用rabbitmq提供的ack机制,首先关闭rabbitmq的自动ack,然后每次在确保处理完这个消息之后,在代码里手动调用ack。这样就可以避免消息还没有处理完就ack。

(2)kafka
A:消费端弄丢了数据
关闭自动提交offset,在自己处理完毕之后手动提交offset,这样就不会丢失数据。
B:kafka弄丢了数据
一般要求设置4个参数来保证消息不丢失:
①给topic设置 replication.factor参数:这个值必须大于1,表示要求每个partition必须至少有2个副本。

②在kafka服务端设置min.isync.replicas参数:这个值必须大于1,表示 要求一个leader至少感知到有至少一个follower在跟自己保持联系正常同步数据,这样才能保证leader挂了之后还有一个follower。

③在生产者端设置acks=all:表示 要求每条每条数据,必须是写入所有replica副本之后,才能认为是写入成功了

④在生产者端设置retries=MAX(很大的一个值,表示无限重试):表示 这个是要求一旦写入事变,就无限重试
C:生产者弄丢了数据
如果按照上面设置了ack=all,则一定不会丢失数据,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

上一篇《如何保证消息不重复消费

下一篇《如何保证消息按顺序执行

原文地址:https://www.cnblogs.com/jack1995/p/10908809.html

时间: 2024-11-07 14:32:02

关于MQ的几件小事(四)如何保证消息不丢失的相关文章

关于MQ的几件小事(一)消息队列的用途、优缺点、技术选型

1.为什么使用消息队列? (1)解耦:可以在多个系统之间进行解耦,将原本通过网络之间的调用的方式改为使用MQ进行消息的异步通讯,只要该操作不是需要同步的,就可以改为使用MQ进行不同系统之间的联系,这样项目之间不会存在耦合,系统之间不会产生太大的影响,就算一个系统挂了,也只是消息挤压在MQ里面没人进行消费而已,不会对其他的系统产生影响. (2)异步:加入一个操作设计到好几个步骤,这些步骤之间不需要同步完成,比如客户去创建了一个订单,还要去客户轨迹系统添加一条轨迹.去库存系统更新库存.去客户系统修改

关于MQ的几件小事(六)消息积压在消息队列里怎么办

1.大量消息在mq里积压了几个小时了还没解决 场景:几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多.线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待几个小时消费完毕.这个肯定不行.一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条. 所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来. 解决方案:" 这种时候只能操作临时扩容,以更快的速

关于MQ的几件小事(七)如果让你设计一个MQ,你怎么设计

其实回答这类问题,说白了,起码不求你看过那技术的源码,起码你大概知道那个技术的基本原理,核心组成部分,基本架构构成,然后参照一些开源的技术把一个系统设计出来的思路说一下就好 比如说这个消息队列系统,我们来从以下几个角度来考虑一下 (1)首先这个mq得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下kafka的设计理念,broker -> topic -> partition,每个partition放一个机器,就存一部分数据.如果现在资源不

关于MQ的几件小事(三)如何保证消息不重复消费

1.幂等性 幂等(idempotent.idempotence)是一个数学与计算机学概念,常见于抽象代数中. 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数.这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变.例如,"setTrue()"函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现. 简单来说,幂等性就是一个数据

关于MQ的几件小事(二)如何保证消息队列的高可用

1.RabbitMQ的高可用 RabbitMQ基于主从模式实现高可用.RabbitMQ有三种模式:单机模式,普通集群模式,镜像集群模式. (1)单机模式: 单机模式就是demo级别的,生产中不会有人使用. (2)普通集群模式 普通集群模式就是在多台机器上启动多个rabbitmq实例,每个机器启动一个.但是创建的queue只会放在一个rabbitmq实例上面,但是其他的实例都同步了这个queue的元数据.在你消费的时候,如果连接到了另一个实例,他会从拥有queue的那个实例获取消息然后再返回给你.

一件小事引发纯属自我的调节,于是有了这篇随笔

只能说今天运气差到极点了吧,也是因此,晚上十点半的现在的我也只能在word上把随笔先写好,等网好了再发出去. 原定的计划是先把周末的网页先写得差不多再直接睡觉的,结果先是PS运行不了,再是快把PS安装包下载完的时候网络又出问题了.弄来弄去结果就把心态搞炸了.在写这篇随笔的时候网络还是忽好忽坏,PS还是没有下下来.这么早就睡觉肯定是睡不着的,也是想借写随笔的过程来平复下烦躁的心情吧. 学习日近尾声,老师的节奏加快的同时,自己的节奏越发受到外界因素的影响,许久未曾谋面的烦躁又开始活跃起来了.而且由于

《一件小事.呐喊》--鲁迅 词语解释

<一件小事> -出自鲁迅小说集<呐喊>. 伊:彼,他,她. 装腔作势:故意装出一种腔调,做出一种姿势,用来比喻故意做作. 威压:表现出使人敬畏的气魄.威:表现出来使人敬畏的气魄:威力,威风,权威:凭借力量或势力:威胁,威逼. 压:从上面加力:压住:用威力制服.镇服:镇压,压服,压迫:逼近:大兵压境.

xamarin android——数据绑定到控件(四)

本文为通过自定义列表适配器定义ListView,以上文为基础,基于ListActivity. 定义列表项布局,包含一个图片显示,标题和描述 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="80dip"> <ImageV

单例模式不是一件小事,快回来看看

上次写了一篇<单例模式那件小事,看了你不会后悔>的文章,总结了常用的单例模式的实现.本文是上文的延续,单例模式绝不是一件小事,想弄清楚,真不是那么简单的.上文提到了常用的三种单例模式的实现方法:饿汉式(除了提前占用资源,没毛病.),懒汉式(DCL优化过后,没毛病?),静态内部类式(优雅的方法,没毛病.).文末最后还提到,反射会破坏单例. 本文继续,双重检查锁定优化过后的懒汉式,真的没毛病吗?其实不是,这里涉及到java编译器编译时的一些细节,对象初始化时的写操作与写入 sSingleton 字