消息队列笔记一:技术选型、优缺点、高可用、三保证

一. 技术选型

  • activeMQ:有较低的概率丢失数据,社区不活跃,维护较少,而且现在数据量越来越大,activeMQ单机吞吐量只有万级,用它越来越不合适了。
  • rabbitMQ:单击吞吐量也是万级,时效性是微秒级,延迟最低:这是rabbitMQ的一大特点,因为它是基于erlang开发的,所以并发能力很强,性能极好,延迟很低。而且开源提供的管理界面很方便使用,社区相对比较活跃,国内互联网公司用rabbitMQ的也偏多。缺点是吞吐量比较低,而且国内研究erlang语言的人不多,导致很难定制和掌控。
  • rocketMQ(阿里开源):单机吞吐量达到十万级,topic可以达到几百、几千个的级别,这是它的一大优势。可用性非常高,是一种分布式架构,扩展性好。在阿里大规模应用过,经得起考验,java的源码,方便阅读。
  • kafka:单机吞吐量达到十万级别,它是分布式的,可用性高,支持较少的topic,功能较为简单,主要是支持简单的MQ功能,在大数据的实时计算以及日志采集领域大规模使用。

二. 消息队列的作用或优点(解耦、异步、削峰)

  • 解耦: A 系统通过接口调用发送数据到 B、C、D 三个系统。如果 C 系统不需要数据了,同时又多了个 E 系统也要 A 系统发送的数据,那么 A 系统的维护成本就很高,而且 A 系统要时刻考虑 B、C、D、E 四个系统如果出现故障该怎么办? 使用消息队列就可以解决这个问题,也就是 A 系统不直接调用其他四个系统的接口了,它只负责生产数据发送到 MQ 中,然后哪个系统需要数据就自己去 MQ 中消费。这样就实现了 A 系统跟其他系统的解耦。
  • 异步: A 系统需要发送个请求给 B 系统处理,由于 B 系统需要查询数据库花费时间较长,以至于 A系统要等待 B 系统处理完毕后再发送下个请求,造成 A 系统资源浪费。使用消息队列后,A系统生产完消息后直接丢进消息队列里,不用等待 B 系统的结果。
  • 削峰: A 系统调用 B 系统处理数据,每天上午低峰期时,A 系统风平浪静,每秒并发请求数量就100 个。结果每次一到中午高峰期,每秒并发请求数量突然会暴增到 1 万条。但是 B 系统最大的处理能力就只能是每秒钟处理 1000 个请求,这样系统很容易崩掉。这种情况可以引入消息队列,把请求数据先存入消息队列中,消费系统再根据自己的消费能力拉取消费。

三. 消息队列的缺点

  1. 降低系统的可用性:系统引入的外部依赖越多,越容易挂掉;
  2. 系统复杂度提高:使用 MQ 后可能需要保证消息没有被重复消费、消息没有丢失、保证消息传递的顺序性 等问题;
  3. 一致性问题:A 系统处理完直接返回成功了,而且 B 和 D 两个系统也写库成功了,但 C 系统写库失败了,就造成数据不一致了。

四. 如何保证消息队列的高可用?

RabbitMQ —— 镜像集群模式 : RabbitMQ 基于主从做高可用性,它的镜像集群模式跟普通集群模式不一样的是,创建的 Queue,无论元数据还是Queue 里的消息都会存在于多个实例上,每次写消息到 Queue 的时候,都会自动和多个实例的Queue 进行消息同步。这样设计的好处是:任何一个机器宕机不影响其他机器的使用。坏处是:1. 性能开销大:消息同步所有机器,导致网络带宽压力和消耗很重;2. 扩展性差:如果某个 Queue 负载很重,即便加机器,新增的机器也包含了这个 Queue 的所有数据,并没有办法线性扩展这个Queue。

Kafka —— partition 和 replica 机制 : Kafka 基本架构是多个 broker 组成,每个 broker 是一个节点。创建一个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,只放一部分数据,所以它是天然的分布式消息队列。也就是一个 topic 的数据,分散放在多个机器上,每个机器只放一部分数据。Kafka 0.8 以后,提供了 HA 机制,就是 replica 副本机制。每个 partition 的数据都会同步到其他机器上,形成自己的多个 replica 副本。然后所有 replica 会选举一个 leader 出来,生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有的 follower 上,读的时候就直接读 leader 上的数据即可。Kafka 会均匀的将一个 partition 的所有 replica 分布在不同的机器上,这样提高了容错性。

五. 如何保证消息不被重复消费(如何保证消息的幂等性)?

幂等性:无论你重复请求多少次,得到的结果都是一样的。例如:一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。

如何保证幂等性?:

  1. 写数据时,先根据主键查一下这条数据是否存在,如果已经存在则 update;
  2. 数据库的唯一键约束也可以保证不会重复插入多条数据,因为重复插入多条只会报错,不会导致数据库中出现脏数据;
  3. 如果是写 redis 就没有问题,因为 set 操作是天然幂等性的。

六. 如何保证消息的可靠性(如何保证不丢失消息)?

1. rabbitMQ 如何保证消息的可靠性传输?(如何处理消息丢失的问题?)

生产者弄丢数据:

  • 方案一:开启RabbitMQ事务(不推荐):生产者在发送数据之前开启RabbitMQ事务,然后发送消息,如果消息没有成功被RabbitMQ接收到,那么生产者会收到异常报错,此时就可以回滚事务,然后重新发送消息;如果收到了消息,就可以提交事务。这样做的问题是,事务机制是同步的,生产者发送的消息会同步阻塞卡住来等待消息是否发送成功,从而导致生产者发送消息的吞吐量会降下来,所以不推荐这种方案。

  • 方案二:开启confirm模式:在生产者那里设置开启confirm模式后,每次写的消息都会分配一个唯一id,如果写入了RabbitMQ中,那么mq会回传一个ack消息来告诉这个消息成功收到了;如果mq没能处理这个消息,会回调一个nack接口来告诉这个消息接收失败了,可以再次重发。confirm机制是一种异步的模式,发送消息后不会阻塞,所以吞吐量会较高一些。

RabbitMQ弄丢数据:

  • 开启RabbitMQ的持久化,就是消息写入后会持久化到磁盘,如果RabbitMQ自己挂了,恢复后会自动读取之前磁盘存储的数据,避免了数据的丢失,具体步骤分两步:第一步是创建queue时将其设置为持久化的,这样就可以保证rabbitMQ持久化queue的元数据(但是不会持久化queue里的数据);第二步是发送消息的时候将消息的deliveryMode设置为2,也就是将消息设置为持久化的,此时RabbitMQ就会将消息持久化到磁盘上去。

  • 但还是有一种可能会丢失数据,就是消息写到rabbitMQ后,还没来得及持久化到磁盘,mq就挂了,从而导致内存里那点数据会丢失。解决办法是可以跟生产者那边的confirm机制配合起来使用,只有消息被持久化到磁盘后,才会通知生成ack了,所以哪怕持久化到磁盘之前,mq挂了,数据丢了,但生产者没收到ack,所以还是会重发消息,避免了数据丢失。

消费者弄丢数据:

  • 场景:是由于打开了消费者的AutoAck机制,这个机制是指当消费者消费到数据后,会自动通知rabbitMQ成功消费了这条消息,但如果消费者正在消费数据,还没处理完,此时它会自动AutoAck,通知rabbitMQ这条消息已消费,结果消费者系统突然宕机了,从而导致没处理完的消息丢失了,而且rabbitMQ还以为这条消息已经处理了。

  • 解决方法是关闭AutoAck,每次确定处理完消息后再发送ack给rabbitMQ。这样的话如果没处理完就宕机,会导致rabbitMQ收不到ack消息,那么它就会将这条消息重新分配给其他的消费者来处理。

2. kafka 如何保证消息的可靠性传输?

消费者弄丢数据:

  • 场景:kafka消费者消费到数据后会写到内存的queue里先缓冲一下,结果有的时候,消息写入内存queue并且消费者自动提交offset后,重启了系统,就会导致内存queue里还没来得及处理的数据丢失了,而kafka认为已经消费了这个数据。
  • 解决办法是关闭自动提交 offset,在处理完后自己手动提交 offset,就可以保证数据不会丢了。

kafka弄丢数据:

  • 场景:kafka会把leader里的数据同步给其他的follower,而有可能其他的follower还没同步完,leader就挂了,然后选举某个follower成为leader后,就丢失了一些数据。
  • 解决办法:设置四个参数:
    • ①给topic设置 replication.factor 参数:这个值必须大于1,这个是要求每个partition必须有至少2个副本,即只是一个leader副本,一个follower副本。
    • ②在kafka服务端设置 min.insync.replicas 参数:这个值必须大于1,这个是要求一个leader至少感知到至少有一个follower还跟自己保持着联系,这样才能确保leader挂了还有一个follower。
    • ③在生产端设置 ack=all:这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了。
    • ④在生产端设置 retries=MAX:这个是要求一旦写入失败,就无限重试。

生产者不会弄丢数据:

  • 如果按照前面的思路设置了 ack=all,则生产者一定不会丢数据,因为它要求 leader 接收到消息后,所有的follower都同步了消息,才认为本次写成功了。如果没满足这个条件,生产者会不断的重试。

七. 如何保证消息的顺序性?

1. rabbitMQ如何保证消息的顺序性?
  • 场景:一个queue,多个消费者,导致顺序错乱。
  • 解决:①拆分多个queue,每个queue对应一个消费者。②一个queue对应一个消费者,然后这个消费者内部用内存队列做排队,然后分发给底层不同的worker来处理。
2. kafka如何保证消息的顺序性?
  • 场景:一个topic,一个partition,一个消费者,而这个消费者内部是多线程的,会导致输出给数据库时顺序错乱了。
  • 解决:一个topic,一个partition,一个消费者,内部单线程消费,写 N 个内存 Queue,具有相同 key 的数据都到同一个内存 Queue 里,然后对于 N 个线程,每个线程分别消费一个内存 Queue ,这样就能保证顺序性了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值