高频消息中间件面试题解析

一:消息队列的主要作用是什么?

1.消息队列的特性:

业务无关,一个具有普适性质的消息队列组件不需要考虑上层的业务模型,只做好消息的分发就可以了,上层业务的不同模块反而需要依赖消息队列所定义的规范进行通信。FIFO,先投递先到达的保证是一个消息队列和一个buffer的本质区别。容灾,对于普适的消息队列组件来说,节点的动态增删和消息的持久化,都是支持其容灾能力的重要基本特性。性能,这个不必多说了,消息队列的吞吐量上去了,整个系统的内部通信效率也会有提高。

2.为什么需要消息队列:

当系统中出现“生产“和“消费“的速度或稳定性等因素不一致的时候,就需要消息队列,作为抽象层,弥合双方的差异。“ 消息 ”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中,“ 消息队列 ”是在消息的传输过程中保存消息的容器 

使用场景:

  • 业务系统触发短信发送申请,但短信发送模块速度跟不上,需要将来不及处理的消息暂存一下,缓冲压力。就可以把短信发送申请丢到消息队列,直接返回用户成功,短信发送模块再可以慢慢去消息队列中取消息进行处理。
  • 调远程系统下订单成本较高,且因为网络等因素,不稳定,攒一批一起发送。
  • 任务处理类的系统,先把用户发起的任务请求接收过来存到消息队列中,然后后端开启多个应用程序从队列中取任务进行处理。

3.使用消息队列有什么好处(消息队列作用)

  • 异步:假设你有一个系统调用链路,是系统A调用系统B,一般耗时20ms;系统B调用系统C,一般耗时200ms;系统C调用系统D,一般耗时2s。用户一个请求过来巨慢无比,因为走完一个链路,需要耗费:2220ms,如果业务流程支持异步化的话,是不是就可以考虑把系统C对系统D的调用抽离出去做成异步化的,不要放在链路中同步依次调用。这样,实现思路就是系统A -> 系统B -> 系统C,直接就耗费220ms后直接成功了。然后系统C就是发送个消息到MQ中间件里,由系统D消费到消息之后慢慢的异步来执行这个耗时2s的业务处理。通过这种方式直接将核心链路的执行性能提升了10倍。
  • 解耦:假设你有个系统A,这个系统A会产出一个核心数据,现在下游有系统B和系统C需要这个数据。那简单,系统A就是直接调用系统B和系统C的接口发送数据给他们就好了。但是现在要是来了系统D、系统E、系统F、系统G,等等,十来个其他系统慢慢的都需要这份核心数据呢?那么负责系统A的就要被烦死了,然后如果要是某个下游系统突然宕机了呢?系统A的调用代码里是不是会抛异常?那系统A的同学会收到报警说异常了,结果他还要去care是下游哪个系统宕机了。所以在实际的系统架构设计中,如果全部采取这种系统耦合的方式,在某些场景下绝对是不合适的,系统耦合度太严重。并且互相耦合起来并不是核心链路的调用,而是一些非核心的场景(比如上述的数据消费)导致了系统耦合,这样会严重的影响上下游系统的开发和维护效率。因此在上述系统架构中,就可以采用MQ中间件来实现系统解耦系统A就把自己的一份核心数据发到MQ里,下游哪个系统感兴趣自己去消费即可,不需要了就取消数据的消费。
  • 消峰:假设有一个系统,平时正常的时候每秒可能就几百个请求,正常处理都是OK的,在高峰期一下子来了每秒钟几千请求,弹指一挥间出现了流量高峰,此时我们就可以用MQ中间件来进行流量削峰。所有机器前面部署一层MQ,平时每秒几百请求大家都可以轻松接收消息。一旦到了瞬时高峰期,一下涌入每秒几千的请求,就可以积压在MQ里面,然后那一台机器慢慢的处理和消费。等高峰期过了,再消费一段时间,MQ里积压的数据就消费完毕了。这个就是很典型的一个MQ的用法,用有限的机器资源承载高并发请求,如果业务场景允许异步削峰,高峰期积压一些请求在MQ里,然后高峰期过了,后台系统在一定时间内消费完毕不再积压的话,那就很适合用这种技术方案。

4.消息队缺点:

  • 系统可用性降低:系统引入的外部依赖越多,越容易挂掉,本来就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的,偏加个MQ进来,万一MQ挂了,整套系统崩溃了。
  • 系统复杂性提高:硬生生加个MQ进来,怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?
  • 一致性问题:A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,这数据就不一致了。

二:项目中是如何使用MQ的?MQ的使用场景?

1.电商系统:

为了解决分布式下多个服务中间的耦合度,通过引入MQ实现松耦合,当管理后台修改商品后,向MQ中发送一条消息,其他服务订阅这条消息,做出对应的修改,实现松耦合。 

2.日志收集系统:

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题,日志采集客户端,负责日志数据采集,定时写受写入Kafka队列,Kafka消息队列,负责日志数据的接收,存储和转发,日志处理应用:订阅并消费kafka队列中的日志数据 。

三:如何保证消息队列的高可用?

1.RabbitMQ高可用

单机模式:Demo级别的,生产环境不会使用

普通集群模式:

同时部署多台RabbitMQ服务器,当生产者将消息发送到RabbitMQ的集群中时 ,消息会存在元数据(类似于消息的描述信息)+消息数据,收到消息的MQ会将消息的元数据信息同步到其他的节点上,当消费者从任意一台服务器上获取消息时,如果当前服务器存在该消息的数据信息就获取成功,否则就会根据元数据信息从其他节点上获取消息数据,这样做并没有保证MQ的高可用,因为存在消息数据的服务器挂掉,消息一样不存在,这样做只能保证MQ的吞吐量比较大。

采用镜像集群模式:

你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。  这样的话,好处在于,你任何一个机器宕机了,别的机器都可以用。坏处在于

  • 性能开销大,消息同步所有机器,导致网络带宽压力和消耗很重!
  • 没有扩展性,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue  

开启镜像集群模式:rabbitmq有很好的管理控制台,在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候可以要求数据同步到所有节点的,也可以要求就同步到指定数量的节点,然后你再次创建queue的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。

2.ActiveMQ的高可用:

使用ZooKeeper(集群)注册所有的ActiveMQ Broker。只有其中的一个Broker 可以提供服务,被视为Master,其他的Broker 处于待机状态,被视为Slave。如果Master 因故障而不能提供服务,ZooKeeper会从 Slave中选举出一个 Broker充当 Master。 Slave 连接 Master并同步他们的存储状态,Slave不接受客户端连接。所有的存储操作都将被复制到连接至 Master 的Slaves。如果 Master 宕了,得到了最新更新的 Slave 会成为 Master。故障节点在恢复后会重新加入到集群中并连接 Master 进入Slave 模式。 所有需要同步到磁盘的消息操作都将等待存储状态被复制到其他法定节点的操作完成才能完成。所以,如果你配置了replicas=3,那么法定大小是(3/2)+1=2。Master 将会存储并更新然后等待 (2-1)=1 个Slave存储和更新完成,才汇报 success。至于为什么是 2-1,熟悉 Zookeeper 的应该知道,有一个 node要作为观察者存在。当一个新的Master 被选中,你需要至少保障一个法定node 在线以能够找到拥有最新状态的node。这个node 可以成为新的Master。因此,推荐运行至少3 个replica nodes,以防止一个node失败了,服务中断。(原理与 ZooKeeper 集群的高可用实现方式类似),具体实现:https://blog.csdn.net/qq_36625757/article/details/90207070

  • Master 和 Slave各自都单独存储持久化的消息,它们不共享数据。
  • Master收到持久化消息时,需要先同步(sync)给Slave之后,才向Producer发送ACK确认。
  • 只有Master负责Client的请求,Slave不接收Client请求。Slave连接到Master,负责备份消息。
  • Master出现故障,Slave有两种处理方式:自己成为Master或关闭(停服务)---根据具体配置而定。
  • Master 与 Slave之间可能会出现“脑裂”现象。比如:Master本身是正常的,但是Master与Slave之间的网络出现故障,网络故障导致Slave认为Master已经宕机,因此它自己会成为Master(根据配置:shutdownOnMasterFailure)。此时,对Client而言,就会存在两个Master。
  • Slave 只能同步它连接到Master之后的消息。在Slave连接到Master之前Producer向Master发送的消息将不会同步给Slave,这可以通过配置(waitForSlave)参数,只有当Slave也启动之后,Master才开始初始化TransportConnector接受Client的请求(Producer的请求)
  • 如果Master 或者 Slave其中之一宕机,它们之间不同步的消息无法自动进行同步,此时只能手动恢复不同步的消息了。也就是说:“ActiveMQ没有提供任何有效的手段,能够让master与slave在故障恢复期间,自动进行数据同步”
  • 对于非持久化消息,并不会同步给Slave。因此,Master宕机,非持久化消息会丢失。

3.kafka的高可用

kafka一个最基本的架构认识:多个broker组成,每个broker是一个节点;你创建一个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition就放一部分数据。

这就是天然的分布式消息队列,就是说一个topic的数据,是分散放在多个机器上的,每个机器就放一部分数据。kafka 0.8以前,是没有HA机制的,就是任何一个broker宕机了,那个broker上的partition就废了,没法写也没法读,没有什么高可用性可言。kafka 0.8以后,提供了HA机制,就是replica副本机制。每个partition的数据都会同步到其他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。只能读写leader?很简单,要是你可以随意读写每个follower,那么就要care数据一致性的问题,系统复杂度太高,很容易出问题。kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性。

写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)

消费的时候,只会从leader去读,但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。

四:Kafka,ActiveMQ,RabbitMQ,RocketMQ的区别?

1.Kafka:

  • 不完全符合jms规范,注重吞吐量,类似udp 和 tcp
  • 一般做大数据吞吐的管道 我们现在的用途就是负责在各个idc之间通信
  • 量大对数据不是百分之百保证的,会有数据丢失,不是百分百送达(amq和rmq等有重发机制,而kafka没有);在吞吐量有提升 ,在这方面就得有牺牲, 所以kafka适合大数据量流转, 比如日志数据比如用作统计的数据。

2.ActiveMQ:

ActiveMQ可以部署于发布订阅和点对点模式。类似于RabbitMQ,它易于实现高级场景,而且只需付出低消耗。

3.RabbitMQ:

RabbitMQ是AMQP协议领先的一个实现,它实现了代理(Broker)架构,意味着消息在发送到客户端之前可以在中央节点上排队。此特性使得RabbitMQ易于使用和部署,适宜于很多场景如路由、负载均衡或消息持久化等,用消息队列只需几行代码即可搞定。但是,这使得它的可扩展性差,速度较慢,因为中央节点增加了延迟,消息封装后也比较大。

4.RocketMQ :

RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的高可靠的消息发布与订阅服务。同时,广泛应用于多个领域。具有以下特点:      

  • 能够保证严格的消息顺序       
  • 提供丰富的消息拉取模式      
  • 高效的订阅者水平扩展能力      
  • 实时的消息订阅机制      
  • 亿级消息堆积能力

5.总体对比:

特性ActiveMQRabbitMQRocketMQKafka
单机吞吐量万级,比 RocketMQ、Kafka 低一个数量级同 ActiveMQ10 万级,支撑高吞吐10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响  topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topictopic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性ms级us级,这是 RabbitMQ 的一大特点,延迟最低ms级ms级以内
可用性高,基于主从架构实现高可用同 ActiveMQ非常高,分布式架构非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性有较低的概率丢失数据基本不丢经过参数优化配置,可以做到 0 丢失同 RocketMQ
功能支持MQ 领域的功能极其完备,有较多的文档;各种协议支持较好,但近年来社区活跃度比较低基于 erlang 开发,并发能力很强,性能极好,延时很低,社区活跃度较好,管理界面非常方便,源码研究不容易MQ 功能较为完善,还是分布式的,扩展性好,经过阿里的检验,性能可靠,源码为java,阅读性较好,社区活跃度较好,文档较少,万一阿里抛弃RocketMQ维护,对系统维护有影响功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用

6.MQ选型总结:

  • 性能小量用什么都没有关系,性质是一样的,如果消息性能要求高,用rocketMQ与kafka可以更优,rocketMQ与kafka 比较就看技术选型了,各有利弊,看业务需要。
  • activeMQ、rabbitMQ与 kafka、rocketMQ有很大的区别就是前2个只支持主从模式,后2个是分布式消息系统,支持分布式。
  • 高并发,从实现语言来看,RabbitMQ最高,原因是它的实现语言是天生具备高并发高可用的erlang语言。RabbitMQ、activeMQ中,综合来看,RabbitMQ是首选。
  • RabbitMq比kafka成熟,在可用性上,稳定性上,可靠性上,RabbitMq超过kafka
  • Kafka设计的初衷就是处理日志的,可以看做是一个日志系统,针对性很强,所以它并没有具备一个成熟MQ应该具备的特性

五:高并发情况下消息队列满了如何防止消息丢失?

其实这个防止消息丢失,每种MQ都要从三个角度来分析:生产者弄丢数据、消息队列弄丢数据、消费者弄丢数据,以RabbitMQ为例:

1.生产者丢数据:

从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。transaction机制就是说,发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。然而缺点就是吞吐量下降了。因此,生产上用confirm模式的居多。一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

2.消息队列丢数据

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步:

  • 将queue的持久化标识durable设置为true,则代表是一个持久的队列
  • 发送消息的时候将deliveryMode=2

这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据

3.消费者丢数据

消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rahbitMQ会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就会丢失该消息。至于解决方案,采用手动确认消息即可。

ActiveMQ如何防止消息丢失:https://blog.csdn.net/qq_36625757/article/details/90207070

其他的MQ可自己根据上面模式自己查阅资料。

六:消费者消费消息,如何保证MQ幂等性(消息不被重复消费)?

1.幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。举个最简单的例子,那就是支付,用户购买商品使用约支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条

2.MQ消息发送

  • 发送端MQ-client(消息生产者:Producer)将消息发送给MQ-server;
  • MQ-server将消息落地(持久化存到数据库中);
  • MQ-server回ACK给MQ-client(Producer);
  • MQ-server将消息发送给消息接受端MQ-client(消息消费者:Customer);
  • MQ-client(Customer)消费接受到消息后发送ACK给MQ-server;
  • MQ-server将落地消息删除

3.消息重复发送原因

为了保证消息必达,MQ使用了消息超时、重传、确认机制。使得消息可能被重复发送,如上图中,由于网络不可达原因:3和5中断,可能导致消息重发。消息生产者a收不到MQ-server的ACK,重复向MQ-server发送消息。MQ-server收不到消息消费者c的ACK,重复向消息消费者c发消息。

4.MQ内部如何做到幂等性的

对于每条消息,MQ内部生成一个全局唯一、与业务无关的消息ID:inner-msg-id。当MQ-server接收到消息时,先根据inner-msg-id判断消息是否重复发送,再决定是否将消息落地到DB中。这样,有了这个inner-msg-id作为去重的依据就能保证一条消息只能一次落地到DB。

5.消息消费者应当如何做到幂等性

  • 对于非幂等性业务且要求实现幂等性业务:生成一个唯一ID标记每一条消息,将消息处理成功和去重日志通过事物的形式写入去重表。
  • 对于非幂等性业务可不实现幂等性的业务:权衡去重所花的代价决定是否需要实现幂等性,如:购物会员卡成功,向用户发送通知短信,发送一次或者多次影响不大。不做幂等性可以省掉写去重日志的操作。

6.结合业务思考

  • 业务表添加约束条件 如果你的数据库将来都不会分库分表,那么可以在业务表字段加上唯一约束条件(UNIQUE),这样相同的数据就不会保存为多份。
  • 将处理后的消息写数据库(主键唯一),你先根据主键查一下,如果这数据已经存在,就不再消费。
  • 使用 redis 如果你的系统是分布式的,又做了分库分表,那么可以使用 redis 来做记录,把消息 id 存在 redis 里,下次再有重复消息 id 在消费的时候,如果发现 redis 里面有了就不能进行消费。
  • 基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。

七.MQ的死信队列

1.死信队列:造成原因

  • 消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false
  • 消息超期 (rabbitmq  Time-To-Live -> messageProperties.setExpiration())
  • 队列超载(队列满了,无法再添加数据到mq中)

2.ActiveMQ配置死信队列

activemq.xml的policyEntries标签中配置

<policyEntry queue=">">
	<deadLetterStrategy>
		<!--
			processNonPersistent:非持久消息是否保存到死信队列
			processExpired:过期消息是否保存到死信队列
			expiration:定时将过期消息发送到死信队列,毫秒值
		-->
		<sharedDeadLetterStrategy processNonPersistent="true" processExpired="true" expiration="30000"/>
		<!--
			queuePrefix:设置死信队列前缀
			useQueueForQueueMessages: 设置使用队列保存死信,还可以设置useQueueForTopicMessages,使用Topic来保存死信
		-->
		<individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true" />
	</deadLetterStrategy>
</policyEntry>

activemq.xml的broker标签中配置

<plugins>
    <!-- 丢弃所有死信-->
    <discardingDLQBrokerPlugindropAll="true"  dropTemporaryTopics="true" dropTemporaryQueues="true" />
    <!-- 丢弃指定死信,目的名称使用空格分隔-->
    <!-- <discardingDLQBrokerPlugindropOnly="MY.EXAMPLE.TOPIC.29 MY.EXAMPLE.QUEUE.87" reportInterval="1000" />-->
    <!--使用丢弃正则匹配到死信-->
    <!--<discardingDLQBrokerPlugindropOnly="MY.EXAMPLE.TOPIC.[0-9]{3} MY.EXAMPLE.QUEUE.[0-9]{3}" reportInterval="3000"/>-->
</plugins>

八:百万级别消息积压,如何处理?

1.为何会产生消息积压:当生产者能力大于消费者的能力时(系统的并发访问过高),势必会造成数据积压,造成消息队列大量数据的堆积。

2.大量消息在mq里积压的解决思路:

  • 先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉;
  • 新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量;
  • 然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue;
  • 接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;
  • 这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据;
  • 等快速消费完积压数据之后,在恢复原先部署架构,重新用原先的consumer机器来消费消息;

3.消息队列中积压大量消息,并且消息设置了过期时间,导致大量消息因为超时而丢失:写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入mq里面去,把白天丢的数据给他补回来

 4.大量消息堆积导致MQ都快满了:写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后按照消息因为超时丢失的处理方式来处理。

5.ActiveMQ处理消息积压问题:https://www.jianshu.com/p/ac71c09f407b

  • 取消监听器监听器SessionAwareMessageListener异步回调onMessage方法的同步锁,优化ActiveMQ批处理参数(queuePrefetch表示MQ的消费者一次从Queue中拉取的数量,默认为1000)。
  • 使用双队列,双队列的核心思想为如果队列1中的消息通知失败,则不再重新放入队列1,而是放入队列2去通知,这样可以起到消息数据分离的作用,及失败通知的数据不再会影响到后续可以成功通知的消息,从而提高队列消息通知的整体性能!
  • MQ组件重选型,将ActiveMQ替换为RabbitMQ、RocketMQ、Kafka。

九:如何保证消息的有序性?

1.rabbitMQ

rabbitmq可能出现顺序问题

生产者发送的消息是有序的,通过消息队列分发到不同的消费者上,但消费者消费的顺序不一致,可能导致消息的顺序就不一致。

解决:

一个消费者对应一个queue,消息只发送到一个queue中,即可保证消息的顺序

2.kafka

1个topic,3个partition,3个consumer,每个消费者消费一个partition,需要保证顺序的消息都放入同一个partiton,但是如果一个消费者开启多个线程来处理,还是无法保证消息的顺序性。

解决:

每个消费者内部设置多个内存队列,对消息的key做hash,将需要保证顺序的消息映射到同一个内存队列中,每个线程负责处理一个内存队列

3.activeMQ

activeMq 里面有 messageGroups 属性,可以指定 JMSXGroupID,消费者会消费指定的 JMSXGroupID。即保证了顺序性,又解决负载均衡的问题。

4.rocketMQ

 生产者中把 orderId 进行取模,把相同模的数据放到 messagequeue 里面,消费者消费同一个 messagequeue,只要消费者这边有序消费,那么可以保证数据被顺序消费。

十:如果让你设计消息中间件,你会咋架构?

  • 首先这个mq得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下kafka的设计理念,broker -> topic -> partition,每个partition放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给topic增加partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?
  • 其次你得考虑一下这个mq的数据要不要落地磁盘吧?那肯定要了,落磁盘,才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是kafka的思路。
  •  其次你考虑一下你的mq的可用性啊?这个事儿,具体参考我们之前可用性那个环节讲解的kafka的高可用保障机制。多副本 -> leader & follower -> broker挂了重新选举leader即可对外服务。
  • 能不能支持数据0丢失啊?可以的,参考之前说的那个数据零丢失方案

其实一个mq肯定是很复杂的,面试官问你这个问题,其实是个开放题,他就是看看你有没有从架构角度整体构思和设计的思维以及能力。确实这个问题可以刷掉一大批人,因为大部分人平时不思考这些东西。

十一:ActiveMQ发送消息的方式有哪些?

  • 同步方式:两个通信应用服务之间必须要进行同步,两个服务之间必须都是正常运行的。发送程序和接收程序都必须一直处于运行状态,并且随时做好相互通信的准备。发送程序首先向接收程序发起一个请求,称之为发送消息,发送程序紧接着就会堵塞当前自身的进程,不与其他应用进行任何的通信以及交互,等待接收程序的响应,待发送消息得到接收程序的返回消息之后会继续向下运行,进行下一步的业务处理。
  • 异步方式:两个通信应用之间可以不用同时在线等待,任何一方只需各自处理自己的业务,比如发送方发送消息以后不用等待接收方的响应,可以接着处理其他的任务。也就是说发送方和接收方都是相互独立存在的,发送方只管发送,接收方只管接收,无须去等待对方的响应。Java中JMS就是典型的异步消息处理机制,JMS消息有两种类型:点对点、发布/订阅。

十二:ActiveMQ服务宕机咋办?

在通常的情况下,非持久化消息是存储在内存中的,持久化消息是存储在文件中的,它们的最大限制在配置文件的<systemUsage>节点中配置。但是,在非持久化消息堆积到一定程度,内存告急的时候,ActiveMQ会将内存中的非持久化消息写入临时文件中,以腾出内存。虽然都保存到了文件里,但它和持久化消息的区别是,重启后持久化消息会从文件中恢复,非持久化的临时文件会直接删除,所以,尽量不要用非持久化消息,非要用的话,将临时文件限制尽可能的调大

十三:ActiveMQ消息消费不均匀的问题?

有时在发送一些消息之后,开启2个消费者去处理消息。会发现一个消费者处理了所有的消息,另一个消费者根本没收到消息。原因在于ActiveMQ的prefetch机制。当消费者去获取消息时,不会一条一条去获取,而是一次性获取一批,默认是1000条。这些预获取的消息,在还没确认消费之前,在管理控制台还是可以看见这些消息的,但是不会再分配给其他消费者,此时这些消息的状态应该算作“已分配未消费”,如果消息最后被消费,则会在服务器端被删除,如果消费者崩溃,则这些消息会被重新分配给新的消费者。但是如果消费者既不消费确认,又不崩溃,那这些消息就永远躺在消费者的缓存区里无法处理。更通常的情况是,消费这些消息非常耗时,你开了10个消费者去处理,结果发现只有一台机器吭哧吭哧处理,另外9台啥事不干。将queuePrefetch设为1,每次处理1条消息,处理完再去取,这样也慢不了多少。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值