Java Web分布式篇之关于MQ的一些思考

Java Web系列文章汇总贴: Java Web知识总结汇总


MQ作用

  • 解耦(减少系统间耦合)
  • 异步(高性能保证)
  • 削峰(高可用保证)

MQ缺点

  • 系统可用性降低(MQ的故障)
  • 系统复杂性变高(服务与MQ的通讯问题)
  • 一致性问题(返回用户成功,后面消息没有全部处理成功)

MQ对比

相同点:
高可用,消息不丢。

差异:

  • ActiveMQ:单机吞吐量 每秒万级,偶尔丢消息,社区不活跃。
  • RabbitMQ:吞吐量万级,管理界面很好,低时延,社区活跃,基于erLang开发,不易维护。适合中小型公司。
  • RocketMQ:吞吐量十万,性能很好,分布式扩展性好。适合大型公司。
  • Kafka:吞吐量十万,实时计算,日志采集,易于扩展。适合大数据计算。

MQ高可用

RabbitMQ:镜像复制,每个节点存储所有的元数据与消息
Kafka:多broker、多分区(leader,follower、选举机制)保证


MQ消息消费幂等性保证

出现消费者重复消费原因(Kafka)

消费者消费完某条消息后宕机,此时未上报偏移量。消费者重启后再次拉取消息,即拉取了之前的消息,此时需要消费者自身保证幂等性。

幂等性保证

1、消息给予唯一标识id,处理(如入库)前,需要先判断之前处理的消息key的缓存中是否包含该key,即之前是否处理过该消息,处理过则不重复处理。
2、根据业务灵活选用其他方案。如入库场景,可以使用主键的唯一性维护等。

详细参考:
RabbitMQ之消息确认机制(事务+Confirm)


MQ消息丢失

以下从三个角度考虑该问题

1、生产者发送消息到MQ丢失

RabbitMQ

问题:

网络故障、MQ故障无法接收数据

解决方案:

需要生产者感知消息是否被MQ接收到,失败则重试。两种方式:

  • 生产者同步发送消息,失败则重试(简单易行,但是吞吐量会降低)
  • 生产者异步发送消息,Confirm机制保证事务,MQ接收到消息后,通知生产者,生产者根据具体消息做出相应

Kafka

问题:

网络故障、MQ故障无法接收数据、分区进行主备切换(leader收到消息后挂了,数据未同步到follower,选举leader后新的leader丢失了数据)

解决方案:

每个分区有多个副本,消息在写入所有分区后才认为写入成功,写入失败就一直重试。
需要设置4个参数:

  • 给这个topic设置replication.factor参数,这个值必须大于1,要求每个分区(partition)至少有2个副本
  • 在Kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个要求一个leader必须有至少感知到有一个follower还跟自己联系,没掉队,这样才能保证leader挂了还有一个follower
  • 在producer端设置acks=all:这个是要求每条数据,必须是写入replica之后,才能认为是写成功了。
  • 在producer端设置retries=MAX:要求一旦写入失败,就无限重试,卡在这里了。

2、MQ中消息丢失

RabbitMQ

问题:

消息在内存中,或消息过期被丢弃

解决方案:

消息永久不过期,消息持久化到磁盘中。具体步骤大概是:

  • 创建queue的时候将其设置为持久化的,这样可以保证RabbitMQ持久化queue中的元数据,但是不会持久化queue里面的数据
  • 发送消息时将消息的deliveryMode设置为2,将消息设置为持久化,此时RabbitMQ就会把消息持久化到磁盘,这样RabbitMQ挂了,重启后也能从磁盘恢复queue的数据

详细参考:
RabbitMQ消息持久化

Kafka

问题:

消息在内存中,或消息过期被丢弃

解决方案:

消息永久不过期,消息持久化到磁盘。每个分区有多个副本,保证高可用。

3、消费者消费消息丢失

问题:

消费者未真正消费完消息,就把消费记录上报给MQ,这样消费者挂掉后重启,会从后面的消息偏移量开始消息,则未真正消费的数据就丢失了。

RabbitMQ & Kafka解决方案:

关闭autoACK,不进行消息的偏移量自动提交,真正消费完消息才提交消费偏移量


MQ消息有序

RabbitMQ

问题:

一个queue,多个consumer,明显会乱序

解决方案:

  • 拆分为多个queue,每个queue一个consumer,就是多一些queue而已
  • 就一个queue,对应一个consumer,然后consumer内部用内存队列做排队,然后分发给不同的worker来处理

Kafka

问题:

生成消息指定key,保证需要保证有序的消息被分配到同一个分区,Kafka的一个分区对应一个消费者消费,所以能保证有序。但是,一个消费者往往内部有多线程处理消息,这样就乱序了。

解决方案:

消费者中使用多个内存队列,一个线程对应一个内存队列进行消费。保证需要有序的的消息维护在同一个内存队列,这样就能保证消费的有序性。


MQ消息大量积压,如何处理

原因

  • 消费者故障
  • 消费者依赖的组件故障,导致消费者挂掉

解决方案

需要临时紧急扩容,具体步骤:

  • 先恢复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉
  • 新建一个topic,partition数量扩展为原来的10倍,临时建立好原来10倍或20倍的queue数量
  • 然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的业务处理,直接均匀轮询写入临时建立好的10倍数量的queue
  • 接着使用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据
  • 这种做法相当于临时将queue和consumer资源扩大10倍,以正常10倍速度消费积压数据
  • 如果当前磁盘已经快爆掉,也可以临时丢弃数据,使用工具收集丢弃的数据,等凌晨再补上数据了

如何设计一个MQ

  • 首先MQ要支持可伸缩行,需要的时候快速扩容,就能增加吞吐量和容量,如何做呢?可以参考Kafka的设计理念,broker->topic->partition,每个partition放一个机器,就存一部分数据。如果资源不够了,只需要给topic增加partition,然后做数据迁移,增加机器,就可以存放更多数据,增加吞吐量
  • 考虑MQ的数据落地磁盘,即使进程挂掉,数据也不会丢,怎么做呢?顺序写磁盘,这样就没有磁盘随机读写的寻址开销,这样磁盘读写的性能就很高了,这也是Kafka思路。
  • 其次考虑MQ的高可用,怎么做?参考Kafka的高可用机制,多副本->leader&follower->broker挂了重新选举leader即可对外服务
  • 考虑数据的0丢失,怎么做?也可Kafka的0丢失方案,生产者失败重试,MQ存储高可用,消费者不autoACK,消费完才提交偏移量

相关:
中华石杉-高性能专题(MQ、Redis、ES)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值