文档

java体系技术文档


MQ

<h2>为什么使用消息队列</h2> <p>根据MQ的异步、削峰、解耦三个优点的场景来回答</p> <h2>消息队列有什么优缺点</h2> <p><strong>优点:</strong></p> <ol> <li>异步:一个操作需要A、B、C三个系统执行完之后,才算整个流程完成,但是这样消耗的时间太长了;使用MQ之后,A系统执行完之后,立刻写入MQ,然后返回结果,后续的步骤等B、C去MQ里消费然后执行成功即可;</li> <li>削峰:当某一时间点,大量的请求(5000)到来,但系统并不能处理这么多,只能处理(2000),那就让系统以2000的速度一直处理,系统没有那么多请求来的时候也是如此,直到处理完;</li> <li>解耦:A系统通过接口发送消息给B、C两个系统,如果现在还要再发送给D系统,或者现在又不想发给B系统了,那只能重新写代码并发布;如果采用MQ,A系统只需要将消息发送到MQ,哪个系统需要A系统的消息,就去MQ里面消费即可,不需要了就取消消费;</li> </ol> <p><strong>缺点:</strong></p> <ol> <li>降低系统可用性:新增了一个MQ组件,就多了一个不确定性,如果MQ挂掉了,整个系统就不可用了;</li> <li>提高系统复杂度:需要对重复消费、消息丢失、保证消息的传递顺序等问题进行处理;</li> <li>数据一致性问题:A、B、C三个系统,A系统处理完了就返回成功,但是假如B、C两个系统有一个失败了,数据就不一致了。</li> </ol> <h2>针对MQ的缺点如何解决这些问题</h2> <h3>如何保证MQ的高可用</h3> <ol> <li>以kafka为例,kafka的一个基本结构是:一个kafka集群有多个broker,每一个broker就是一个节点,你创建一个topic(队列),这个topic里可以有多个partition,每个partition又可以存在于不同的broker上,每个partition存储一部分数据。所以说,kafka是天然支持分布式的,一个topic的数据是分布在多个broker上的,每个机器存储一部分。</li> <li>kafka提供了HA机制,就是副本(replica)机制,每个partition的数据会产生几个replica(多少个可以自己设定)副本数据在不同的机器上,从所有的replica中选举一个leader出来,其余的都是follower,生产者和消费者都和leader打交道。写数据时,生产者往leader写入数据,leader将数据同步给follower;读数据时,直接从leader读取数据。当某一个broker宕机了,并且这个节点上有某个partition的leader,那么就从其它broker中选举一个follower称为新的leader,这样就保证了kafka的高可用。</li> </ol> <h3>防止重复消费</h3> <ol> <li>还是以kafka为例,kafka有一个offset的概念,每一个消息写入进去,都有一个offset,代表消息的序号,然后consumer消费了消息并处理完成之后,手动提交offset,表示已经处理完成。</li> <li>这种方式不能完全避免重复消费,假设消息已经处理成功了,在提交offset之前,机器挂掉了,就还是会出现重复消费的情况;这时候就需要consumer方保证幂等性(一次或多次请求某一资源,对资源的影响都是一致的)了,可以通过流水号或订单号等来保证,每次处理数据时,都先判断一下是否已经处理过,如果处理过,就直接丢弃。</li> </ol> <h3>保证消息不丢失</h3> <p>数据丢失由三种情况:</p> <ol> <li>生产者将消息发送给MQ的过程中丢失;</li> <li>消息已经在MQ里,由于机器挂掉,导致丢失;</li> <li>消费方已经获取到消息,并且已经通知MQ收到消息,但是还没处理成功,consumer挂掉了,导致消息丢失; 解决方式:</li> <li>针对第一种情况,在 producer 端设置 acks=all,retries=MAX,只有当数据写入每一个replica之后,才认为写入成功,一旦写入失败,就无限重试;</li> <li>做好MQ数据的持久化工作,并保证每个partition至少有两个副本;</li> <li>关闭kafka的自动提交offset,只有当消息处理完之后,才手动提交offset;</li> </ol> <h3>保证消息的顺序性</h3> <p>生产者将消息发送给MQ一定是有顺序的,消费者从MQ读取消息也是有顺序的,当消费端开启多个线程来处理消息时,处理完消息的顺序可能会不一致。 解决的方法是:在消费端创建多个本地queue,然后将同一相关性操作的消息放到同一个内存级别的queue中,每个线程分别消费一个queue即可,这样就保证了消息的顺序性。</p> <h3>数据一致性问题</h3> <p>数据一致性问题需要用到分布式事务来解决。</p> <h2>技术选型</h2> <p>针对RabbitMQ、RocketMQ、Kafka等消息队列,我们如何选择呢?主要可以从单机吞吐量、topic数量对吞吐量的影响、可用性、可靠性、时效性、功能完整度以及适合的场景等来考虑。</p>

页面列表

ITEM_HTML