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

    xiaoxiao2022-07-14  167

    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)