消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通

消息队列是一种系统间相互协作的通信机制,使用消息队列的场景一般有异步处理、解耦、流量削峰、日志收集、事务最终一致性这几种,主要解决诸如消息堆积、消息持久化、可靠投递、消息重复、严格有序、集群等各种问题,目前的消息中间件种类有很多,比如ActiveMQ/RabbitMQ/RocketMQ/Kafka,我司使用的中间件为包裹RockerMQ的ZMQ,以及用于日志收集的kafka,本文分析了这几种中间件的特点,并结合公司使用情况,重点阐述RocketMQ的使用 参考资料 《分布式消息中间件实战(倪炜)沈剑》 《架构师之路》 《Spring实战》

文章目录

      • 1、MQ的使用场景?
      • 2、通信协议很多,如db,soa,socket,rmi,为什么要选择mq?
      • 3、jms消息发送时序图?
      • 4、Activemq支持的通信机制?(out)
      • 5、jms的消息格式?
      • 6、消费者的消费模式?
      • 7、MQ如何整合到spring中去?
      • 8、消息的模式?
      • 9、ActiveMq解决了什么问题?
      • 10、mq如何避免重复提交/消息丢失的问题?
      • 11、AcitveMQ的作用、原理、特点?(生产者。消费者。 p2p、订阅实现流程)
      • 12、ActiveMQ在项目中如何应用的?
      • 13、消息队列 参考资料:亿级流量网站架构核心技术 张开涛
      • 14、MQ的优点和缺点?
      • 15、MQ,如何做到==消息必达==? 消息的补发重传解决思路?**20181222**
      • 16、MQ、如何做到消息幂等? 幂等性的解决思路?20181222(任意多次执行所产生的影响均与一次执行的影响相同)
      • 17、MQ、如何做到消息延时?
      • 18、MQ、如何做到流量削峰? 消息的堆积解决思路?20181222
      • 19、了解哪些消息队列,及其对比?
      • 20、ActiveMQ 如果数据提交不成功怎么办?
      • 21、ActiveMQ消息是被顺序接受的吗?如果不是,如何确保它具有顺序的消息?如何保证消息的有序性?20181222
      • 22、MQ集群部署?
      • 23、MQ的持久化存储方案(被政采云问到了)
      • 24、RocketMq的存储机制?(消息的存储和发送)
      • 25、RabbitMQ 由Erlang语言开发 端口号5672 (out)
      • 26、Rocketmq的事务消息,问了TCC?
      • 27、列举一个常用的消息中间件,如果消息要==保序==如何实现? 政采云问到了
      • 28、Mq的持久化机制
      • 29、RocketMQ项目实战`在这里插入代码片`
      • 30、高级功能和源码分析
      • 31、Kafka消息中间件+Flink实时计算框架
        • 1、Kafka如何保证消息队列的高可用?
        • 2、Kafka如何保证消息不重复消费(幂等性)?
        • 3、如何保证消息的可靠传输(不丢失)?
        • 4、如何保证消息的顺序性?
        • 5、如何处理消息推积?
        • 6、mq 中的消息过期失效了
        • 7、mq 都快写满了
        • 8、kafka的持久化机制 kafka down掉会不会丢数据
        • 9、有哪几种持久化机制?区别
      • 32、RocketMQ常见面试题
        • 1、在RocketMQ中,消息丢失问题
        • 2 同步落盘怎么才能快?
        • 3 消息堆积的问题
        • 4 定时消息的实现
        • 5 顺序消息的实现
        • 6 分布式消息的实现(事务消息)
        • 8 消息重复发送的避免
        • 9 RocketMQ 不使用 ZooKeeper 作为注册中心的原因,以及自制的 NameServer 优缺点?
        • 10 加分项咯
        • 11 关于消息队列其他一些常见的问题展望(==后续补充==)

1、MQ的使用场景?

  • 1、数据驱动的任务依赖

    任务之间有一定的依赖关系 1)task3需要使用task2的输出作为输入 2)task2需要使用task1的输出作为输入

  • 2、上游不关心执行结果

    上下游逻辑+物理解耦,除了与MQ有物理连接,模块之间都不相互依赖

  • 3、上游关注执行结果,但执行时间很长

    典型的是调用离线处理,或者跨公网调用,也经常使用回调网关+MQ来解耦。
    例如:微信支付:跨公网调用微信的接口,执行时间会比较长,但调用方又非常关注执行结果
    一般采用“回调网关+MQ”方案来解耦:
    1)调用方直接跨公网调用微信接口
    2)微信返回调用成功,此时并不代表返回成功
    3)微信执行完成后,回调统一网关
    4)网关将返回结果通知MQ
    5)请求方收到结果通知

2、通信协议很多,如db,soa,socket,rmi,为什么要选择mq?

基于我们项目的限制以及功能和性能做出的选择

  • JMS的应用场景:规模和复杂度较高的分布式系统

两种通信模式?

  • 点对点
    一个消息只能被一个接受者接受一次
    生产者把消息发送到队列中(Queue),这个队列可以理解为电视机频道(channel)
    在这个消息中间件上有多个这样的channel
    接受者无需订阅,当接受者未接受到消息时就会处于阻塞状态
  • 广播
    每个消息可以有多个消费者。
    生产者和消费者之间有时间上的相关性。订阅一个主题的消费者只能消费自它订阅之后发布的消息.

3、jms消息发送时序图?

jms连接工厂—jms连接—jms会话—jms生产者—目的地----jms消费者

4、Activemq支持的通信机制?(out)

通讯协议TCP/UDP等

  • 通信的过程: client客户 message broker消息中转器
    1、activeMQ初始化时,通过TcpTransportServer类根据配置打开TCP侦听端口,客户通过该端口发起建立链接的动作。
    2、把accept的Socket放入阻塞队列中
    3、另外一个线程Socket handler阻塞着等待队列中是否有新的Socket,如果有则取出来
    4、生成一个TransportConnection的实例。TransportConnection类的主要作用是处理链路的状态信息,并实现CommandVisitor接口来完成各类消息的处理
    5、TransportConnection会使用一个由多个TransportFilter实例组成的消息处理链条,负责对接收到的各类消息进行处理并发送相应的应答
    6、建链完成,可以进行通讯操作。方法有run()和oneway(),一个负责读取,一个负责发送。
  • 心跳机制:
    为了更好的维护TCP链路的使用,activeMQ采用了心跳机制作为判断双方链路的健康情况。activeMQ使用的是双向心跳,也就是activeMQ的Broker和Client双方都进行相互心跳
    ReadCheck WriteCheck 心跳产生的两个线程

5、jms的消息格式?

由两部分组成:报头和消息主体。

  • 1、报头由路由信息以及有关该消息的元数据(描述数据的数据)组成;
  • 2、消息主体则携带着应用程序的数据或有效负载—根据有效负载的类型来划分,可以将消息分为几种类型。

jms定义了5种不同的消息正文格式,以及调用的消息类型,允许你发送并接受一些不同形式的数据,提供现有消息格式的一些级别的兼容性。

  • streamMessage 原始值流 java原始值得数据流
  • mapmessage 属性集合 一套名称–值对
  • textmessage 简单文本 一个字符串对象 传送的是图书id信息
  • objectmessage 可序列化的对象 一个序列化的java对象
  • bytesmessage 字节流 一个直接的数据流

6、消费者的消费模式?

两种消费模式

  • 1、同步消费 通过调用消费者的receive方法从目的地中显示提取消息。receive方法可以一直阻塞到消息到达
  • 2、异步消费 客户可以为消费者注册一个消息监听器,以定义在消息到达时所采取的动作。实现messageListener接口,在messageListener()方法中实现消息的处理逻辑

7、MQ如何整合到spring中去?

使用的jar包,5.11.2版本,不要使用5.12.0版本
1、创建连接工厂

  • connectionfactory 端口号61616 使用brokerURL中的URL指定指定连接工厂要连接到本地机器的61616端口

2、配置生产者(声明消息的目的地)

  • jmsTemplate jms工具类 queue的目的地 topic的目的地 发送消息 在messagecreator()里面写想要发送的消息

3、配置消费者

  • 通过messagelistenercontainer实现,他负责接受消息,并把接受到的信息发送给真正的messagelistener进行处理。
    指定三个属性:
    1、表示从哪里监听的connectionfactory
    2、监听的destination
    3、接收到消息以后进行消息处理的messageListener

8、消息的模式?

推模式还是拉模式?
两个或多个客户端在相互发送或接受消息是,通常使用两种方法来传递消息。

  • 1、消息推送(点对点),也就是有消息发送方确保接收者成功接收到消息
  • 2、消息拉取 (广播) 这种方法由接收者自己去获取存储在某种邮箱里面的消息。
    默认情况下只通知一次,如果接收不到此消息就没有了。如果要求消息必须送达不可以丢失的话,需要配置持久订阅。
    发布消息和接收消息时需要配置发送模式为持久化。此时 如果客户端接收不到消息,消息会持久化到服务端

消息是被推送(消息生产者)和拉取的(消费端),
流程:

  • 1、消息的发送者把消息发送activeMQ消息服务器进行存储,消息发送者发送消息之前,必须在mq服务器开辟一块消息空间,并给消息空间起个名
  • 2、消费者监听消息空间,一旦发现这个空间有消息,立马接受。
    特点:
    1、一条消息可以被多消费者接受
    2、消息被消费后,持久化
    3、消费者必须先监听,才能获得消息内容;
    4、发布订阅,只能使用监听模式接受

9、ActiveMq解决了什么问题?

1、管理员在商品管理模块添加/更新商品时,需要把数据提交到数据库中,同时,还得把数据发送到solr索引系统,来同步索引库的商品,另一方面,缓存系统来删除此商品的缓存
因此,用到了消息队列MQ,来保证消息处理的有序性及是否能重复消费及如何保证重复消费的幂等性。

10、mq如何避免重复提交/消息丢失的问题?

幂等性


11、AcitveMQ的作用、原理、特点?(生产者。消费者。 p2p、订阅实现流程)

Activemq的作用就是系统之间进行通信。当然可以使用其他方式进行系统间通信,如果使用Activemq的话可以对系统之间的调用进行解耦,实现系统间的异步通信。

  • 原理就是生产者生产消息,把消息发送给activemq。Activemq接收到消息,然后查看有多少个消费者,然后把消息转发给消费者,此过程中生产者无需参与。
    消费者接收到消息后做相应的处理和生产者没有任何关系。
  • 使用的传输协议? tcp nio ssl等等
  • 特点:1.对Spring的支持 2.支持多种传送协议 3.完全支持jms规范

12、ActiveMQ在项目中如何应用的?

Activemq在项目中主要是完成系统之间通信,并且将系统之间的调用进行解耦。例如在添加、修改、删除商品信息后,需要将商品信息同步到solr索引库、同步缓存中的数据redis以及生成静态页面freemarker一系列操作。

  • 在此场景下就可以使用activemq。一旦后台对商品信息进行修改后,就向activemq发送一条消息,然后通过activemq将消息发送给消息的消费端,消费端接收到消息可以进行相应的业务处理。

13、消息队列 参考资料:亿级流量网站架构核心技术 张开涛

使用队列保证最终一致性即可,不需要强一致性。我们也要考虑是否需要保证消息处理的有序性及如何保证,是否能重复消费及如何保证重复消费的幂等性。在实际开发中,我们经常使用队列进行异步处理、系统解耦、数据同步、流量消峰、扩展性、缓冲等。

应用场景:(优点)

  • 异步处理:用户注册后,需要发送注册成功邮件/新用户积分/优惠券等,缓存过期后,先返回过期数据,然后异步更新缓存,异步写日志等。可以提升主流程响应速度。

  • 系统解耦:比如:用户成功支付完成订单后,需要通知生产配货系统、发票系统、库存系统、推荐系统、搜索系统等进行业务处理,而未来需要支持哪些业务是不知道的,并且这些业务不需要实时处理、不需要强一致,只需要保证最终一致性即可。

  • 数据同步:比如:想把Mysql变更的数据同步到redis,或者将mysql的数据同步到mongodb,或者让机房之间的数据同步,或者主从数据同步等,此时可以考虑使用databus、canal、otter等。使用数据总线队列进行数据同步的好处是可以保证数据修改的有序性。

  • 流量削峰:系统瓶颈一般在数据库上,比如扣减库存、下单等。此时可以考虑使用队列将变更请求暂时放入队列,通过缓存+队列暂存的方式将数据库流量削峰。同样,对于秒杀系统,下单服务会是该系统的瓶颈,此时,可以使用队列进行排队和限流,从而保护下单服务,通过队列暂存或者队列限流进行流量削峰。

  • 1、缓冲队列
    典型的如log4j日志缓冲区,当我们使用log4j记录日志时,可以配置直字节缓冲区,字节缓冲区满后,会立即同步到磁盘,log4j是使用bufferedWriter实现的。在缓冲区满时,还是会阻塞主线程。

  • 2、任务队列
    线程池任务队列 默认使用linkedblockingQueue(重点)和Disruptor任务队列。如用户注册完成后,将发送邮件/送积分/送优惠券任务扔到任务队列中进行异步处理。刷数据时,将任务扔到异步处理,处理成功后在异步通知用户;删除SKU操作,在用户请求时直接将任务分解并扔到队列进行异步处理,处理成功后异步通知用户。

  • 3、消息队列
    消息队列有ActiveMQ,Kafka,Redis。使用消息队列存储各业务数据,其他系统根据需要订阅即可。常见的订阅模式是:点对点(一个消息只有一个消费者),发布订阅(一个消息可以有多个消费者)最常使用发布订阅模式。比如:修改商品数据,变更订单状态时,都应该将变更的信息发送到消息队列。其他模块有需要,就直接订阅该消息队列即可。一般我们在应用能够系统中采用双写模式,同时写DB和MQ,然后异构系统订阅MQ进行业务处理。没有事务保证,可能会出现数据不一致的情况。如果对一致性要求没那么严格,这种模式没什么问题。重点来了:如果在事务中发MQ,会存在事务回滚,但是MQ已经发送成功了,则需要消费者进行幂等处理(外部对接口的多次调用得到的结果是相同的)。如果事务提交慢,但是MQ已经发出去了,则此时根据MQ获取数据库的数据可能不是最新的。如果MQ发送慢,则会导致事务无法快速提交,造成数据库堵塞。同样不要在事务中掺杂RPC调用,rpc服务不稳定,同样会引起数据库阻塞。

  • 4、请求队列

  • 5、数据总线队列

  • 6、混合队列

  • 7、其他队列


14、MQ的优点和缺点?

  • 优点:解耦、削峰、数据分发
  • 缺点:
    系统可用性降低(多了一个MQ组件)
    系统复杂度提高(之前系统是同步的远程调用,现在是通过mq进行异步调用)
    一致性问题 a系统处理完业务,通过mq给b,c,d三个系统发消息,如果部分成功,部分失败,如何保证消息数据处理的一致性。消息可靠性和重复性互为矛盾,消息不丢不重难以同时保证
  • 结论:调用方实时依赖执行结果的业务场景,请使用调用,而不是MQ

15、MQ,如何做到消息必达? 消息的补发重传解决思路?20181222

MQ要想尽量消息必达,架构上有两个核心设计点:
(1)消息落地 (2)消息超时、重传、确认
MQ既然将消息投递拆成了上下半场,为了保证消息的可靠投递,上下半场都必须尽量保证消息必达。
MQ-client–1--> MQ-server–4-->MQ-client
<–3-- l l <–5—
2 6
db

  • MQ消息投递上半场
    (1)MQ-client将消息发送给MQ-server(此时业务方调用的是API:SendMsg)
    (2)MQ-server将消息落地,落地后即为发送成功
    (3)MQ-server将应答ACK发送给MQ-client(此时回调业务方是API:SendCallback)

  • MQ消息投递下半场
    (1)MQ-server将消息发送给MQ-client(此时回调业务方是API:RecvCallback)
    (2)MQ-client回复ACK给MQ-server(此时业务方主动调用API:SendAck)
    (3)MQ-server收到ack,将之前已经落地的消息删除,完成消息的可靠投递

  • MQ消息投递的上下半场,都可以出现消息丢失,为了降低消息丢失的概率,MQ需要进行超时和重传。

  • 上半场的超时与重传:MQ上半场的1或者2或者3如果丢失或者超时,MQ-client-sender内的timer会重发消息,直到期望收到3,如果重传N次后还未收到,则SendCallback回调发送失败,需要注意的是,这个过程中MQ-server可能会收到同一条消息的多次重发。

  • 下半场的超时与重传:MQ下半场的4或者5或者6如果丢失或者超时,MQ-server内的timer会重发消息,直到收到5并且成功执行6,这个过程可能会重发很多次消息,一般采用指数退避的策略,先隔x秒重发,2x秒重发,4x秒重发,以此类推,需要注意的是,这个过程中MQ-client-receiver也可能会收到同一条消息的多次重发


16、MQ、如何做到消息幂等? 幂等性的解决思路?20181222(任意多次执行所产生的影响均与一次执行的影响相同)

  • 为保证消息的可达性,超时、重传、确认机制可能导致消息总线、或者业务方收到重复的消息,从而对业务产生影响。

例子:上架图书,后台上传模块负责图书上架,下游索引模块负责更新索引库,通过MQ异步通知,不管是上半场的ACK丢失,导致MQ收到重复的消息,还是下面的ACK丢失,导致索引系统收到重复的商品id。
上半场的幂等:步骤2落地重复的消息,对每条消息,MQ系统内部必须生成一个inner-msg-id,作为去重和幂等的依据,这个内部消息ID的特性是:全局唯一,MQ生成,具备业务无关性。
下半场的幂等:业务消息体中,必须有一个biz-id,作为去重和幂等的依据,这个业务ID的特性是:对于同一个业务场景(支付ID,订单ID,帖子ID),全局唯一,由业务消息消费方负责判重,以保证幂等。

mq为了保证消息必达,消息上下半场均可能发送重复消息,如何保证消息的幂等性?
总结:MQ-client生成inner-msg-id,保证上半场幂等 这个id全局唯一,业务无关,由MQ保证
业务发送发带入biz-id,业务接收方去重保证幂等,这个ID对单业务唯一,业务相关,对MQ透明

RocketMQ中的处理方法:
已业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息key进行设置:

Message msg = new Message();
Msg.setKey(“ORDERID_100”);
sendResult sendResult = producer.send(message);
订阅方收到消息时可以根据消息的key进行幂等处理:
public Action consumer(Message message, ConsumeContext context){
     
String key = message.getKey();
//根据业务唯一标识的key做幂等处理
}

17、MQ、如何做到消息延时?

例如:图书商城订单完成后,如果用户一直不评价,一周后会将自动评价。
高效延时消息,包含两个重要的数据结构
(1)环形队列,例如可以创建一个包含3600个slot的环形队列(本质是个数组)
(2)任务集合,环上每一个slot是一个Set
同时,启动一个timer,这个timer每隔1s,在上述环形队列中移动一格,有一个Current Index指针来标识正在检测的slot

Task结构中有两个很重要的属性:
(1)Cycle-Num:当Current Index第几圈扫描到这个Slot时,执行任务
(2)Task-Function:需要执行的任务指针
假设当前Current Index指向第一格,当有延时消息到达之后,例如希望3610秒之后,触发一个延时消息任务,只需:
(1)计算这个Task应该放在哪一个slot =11 (2)计算这个Task的Cycle-Num =1 减法运算 如果变为0,说明马上要执行这个Task了,取出Task-Funciton,使用了“延时消息”方案之后,只需要在订单完成后,触发一个48小时之后的延时消息即可。

总结:环形队列是一个实现“延时消息”的好方法,开源的MQ好像都不支持延迟消息

对比:

  • rabbitMQ:如何实现消息延时?

18、MQ、如何做到流量削峰? 消息的堆积解决思路?20181222

系统瓶颈一般在数据库上,比如扣减库存、下单等。此时可以考虑使用队列将变更请求暂时放入队列,通过缓存+队列暂存的方式将数据库流量削峰。

流量冲击:订单模块发起下单操作,下游模块完成业务逻辑(库存检查,库存冻结,余额检查,余额冻结,订单生成,余额扣减,库存扣减,生成流水,余额解冻,库存解冻)
上游业务简单,下游业务复杂,很有可能上游不限速的下单,导致下游系统被压垮。

如何缓冲流量?
可以利用哪个MQ来缓冲,MQ-server推模式改为MQ-client拉模式。MQ-client根据自己的处理能力,每隔一定时间,或者每次拉取若干条消息,实施流控,达到保护自身的效果。

总结:MQ-client提供拉模式,定时或者批量拉取,可以削平流量,下游自我保护的作用(MQ来做)
要想提升整体吞吐量,需要下游优化,例如批量处理等方式。


19、了解哪些消息队列,及其对比?

消息中间件 开发语言 单机吞吐量 时效性 消息的存储 可用性 功能特性
activeMQ java 万级 ms级 关系型数据库KahaDB 高(主从架构) 成熟的产品,在很多公司得到应用,有较多的文档;各种协议支持较好
RabbitMQ erlang 万级 us级 文件系统 高(主从架构) 基于erlang开发,所以并发能力很强,性能极其好,延时2很低,管理界面较丰富
RocketMQ java 10万级 ms级 文件系统 非常高(分布式架构) Mq功能比较完善,扩展性佳
Kafka scala 10万级 ms级以内 文件系统 非常高(分布式架构) 只支持主要的mq功能,像一些消息查询,消息回溯等功能没有提供,毕竟是为大数据准备的,在大数据领域应用广。

特点补充
它能够以广播和点对点的技术实现队列,它少量代码就可以高效地实现高级应用场景
它非常重量级,更适合于企业级的开发。同时实现了Broker构架,这意味着消息在发送给客户端时先在中心队列排队。对路由,负载均衡或者数据持久化都有很好的支持

1、是一个分布式的、支持分区的、多副本的、支持zookeeper协调的分布式消息系统
2、可以实时的处理大量数据以满足各种需求场景:比如:基于Hadoop的批处理系统、低延时的实时系统、storm/spark流式处理引擎、web/ nginx日志、访问日志、消息服务等
3、高性能跨语言分布式发布/订阅消息队列系统,可以处理大规模网站中所有动作流数据:网页浏览、搜索和其他用户的行为

3、kafka(scala语言开发): java优先

  • 1、高吞吐量、低延时:kafka每秒可以处理几十万条消息、他的延迟最低只有几毫秒,每个topic(广播)可以分为多个partition、consumer group对partition进行consume操作
  • 2、可扩展性:kafka集群支持热扩展
  • 3、持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
  • 4、容错性:允许集群中结点失败(若副本数据为n,则允许n-1个结点失败)
  • 5、高并发:支持数千个客户端同时读写
    原理:通常由两种消息模型:队列和发布-订阅模式,kafka为这两种模型提供了单一的消费者抽象模型:消费者组(消费者用一个消费者组名标记自己) 一个发布在topic上的消息被分发给此消费者组中的一个消费者。假如所有的消费者都在一个组中,那么这就变成了queue模型。假如所有的消费者都在不同的组中,那么就完全变成了发布-订阅模型.

特点:(消息的顺序保证性强)

  • 1、kafka 能够保证生产者发送到一个特定的Topic的分区上,消息将会按照它们发送的顺序依次加入
  • 2、如果一个 Topic 配置了复制因子(replication facto)为 N,那么可以允许 N-1 服务器宕机而不丢失任何已经提交(committed)的消息

kafka设计思想,底层原理 个推
Controller在ZooKeeper注册Watch:

  • kafka broker集群收zookeeper管理,所有kafka broker结点一起去zookeeper上注册一个临时结点,因为只有一个kafka broker会注册成功,所以这个成功。在zookeeper上注册临时结点的这个kafka broker会成为 Kafka Broker Controller,其他的 Kafka broker 叫 Kafka Broker follower。这个 Controller 会监听其他的 Kafka Broker 的所有信息,如果这个kafka broker controller宕机了,在zookeeper上面的那个临时节点就会消失,此时所有的broker,又会去zookeeper上注册临时结点。
  • broker宕机会发生什么情况?
    这个 kafka broker controller 会读取该宕机broker上所有的partition在zookeeper上的状态,并选取ISR列表中的一个replica作为partition leader。如果ISR列表中的replica全挂,选一个幸存的replica作为leader;如果该partition的所有的replica都宕机了,则将新的leader设置为-1,等待恢复,等待ISR中的任一个Replica“活”过来,并且选它作为Leader;或选择第一个“活”过来的 Replica(不一定是ISR中的)作为Leader。这个broker宕机的事情,kafka controller也会通知zookeeper,zookeeper会通知其他的kafka broker。

20、ActiveMQ 如果数据提交不成功怎么办?

Activemq 有两种通信方式,点到点形式和发布订阅模式。

  • 1、如果是点到点模式的话,如果消息发送不成功此消息默认会保存到 activemq 服务端知道有消费者将其消费,所以此时消息是不会丢失的。
  • 2、如果是发布订阅模式的通信方式,默认情况下只通知一次,如果接收不到此消息就没有了。这种场景只适用于对消息送达率要求不高的情况。
  • 如果要求消息必须送达不可以丢失的话,需要配置持久订阅。每个订阅端定义一个id,在订阅是向 activemq 注册。发布消息和接收消息时需要配置发送模式为持久化。此时如果客户端接收不到消息,消息会持久化到服务端,直到客户端正常接收后为止。

21、ActiveMQ消息是被顺序接受的吗?如果不是,如何确保它具有顺序的消息?如何保证消息的有序性?20181222

不能。所以我们应该用zookeeper来选主,让主去消费队列,并且队列要设置成exclusive。这样我们就保证队列中的消息是被顺序消费的


22、MQ集群部署?

ActiveMQ集群部署

  • 高可用的方案主要是基于Master-Slave模式实现的冷备方案,较为常用的包括基于共享文件系统的master-slave架构和基于共享数据库的master-slave架构(某个表的排它锁)。当master启动时,他会获得共享文件系统的排它锁,而其他slave则stand-by,不对外提供服务,同时等待获取master的排它锁。假如master连接中断或者发生异常,那么他的排它锁会立即四方,此时便会有另外一个slave能够争夺到master的排它锁,从而成为master,对外提供服务。之前故障的master重新连接到共享文件系统系统时,他将作为slave等待,直到master再一次发生异常。

客户端使用failover来容错(失败自动切换,当出现失败,重试其它服务器)

RocketMQ集群搭建

  • producer:消息的发送方,举例:发信人
  • consumer:消息的接收方,举例:收信人
  • broker:暂存和传输消息,举例:邮局
  • NameServer:管理broker,举例:各个邮局的管理机构
  • topic:区分消息的种类,一个发送者可以发送消息给一个或多个topic,一个接受者可以订阅一个或多个topic消息
  • message queue:相当于topic的分区,用于并行发送或接受消息

集群特点:

  • 1、nameserver 是一个几乎无状态节点,可集群部署,节点之间无任何信息同步;
  • 2、broker部署相对复杂,broker分为master和slave,一个master可以对应多个slave,但是一个slave只能对应一个master;
  • 3、producer与namespace集群中的其中一个节点(随机选择)建立长连接,定期从nameserver取topic路由信息,并向提供topic服务的master发送心跳。producer完全无状态,可集群部署。
  • 4、consumer与nameserver集群中的其中一个节点(随机选择)建立长连接,定期从nameserver取topic路由信息,并向提供topic服务的master,slave建立长连接,且定时向master,slave发送心跳,consumer既可以从master订阅消息,也可以从slave订阅消息,订阅规则由broker配置决定。

23、MQ的持久化存储方案(被政采云问到了)

ActiveMQ接收到Message后需要借助持久化方案来完成消息存储。可以通过多种介质完成存储:磁盘文件系统、ActiveMQ内置数据库或第三方关系型数据库
1、 使用自带的db KahaDB (默认的持久化存储方案)
KahaDB 文件所在位置是您的 ActiveMQ 安装路径下的/data/KahaDB 子目录下(没看到)
官方认为 KahaDB 使用了更少的文件描述符,设计目标是支持事务日志、可靠、可扩展、速度快等

2、 AMQ
不依赖于第三方数据库,用户能够快速启动和运行
在此方案下消息以日志的形式实现持久化,存放在 Data Log 里。

3、JDBC
支持使用关系型数据库进行持久化存储–通过JDBC实现的数据库连接。可以支持的关系型数据库包括:db2,mysql,oracle,sqlserver,sybase等
许多企业使用关系数据库作为存储,是因为他们更愿意充分利用这些数据库资源,比如已有的热备和负载方案

4、内存存储
内存消息存储器将所有持久消息保存在内存中。在仅存储有限数量message的情况下,内存消息存储会很有用,因为message通常会被快速消耗。
一般用于实时消息的缓存,只针对非持久订阅的消费者提供了5种订阅恢复策略,可以极大程度增强非持久订阅的可用性。


24、RocketMq的存储机制?(消息的存储和发送)

  • 1、使用顺序写,保证消息存储的速度
  • 2、MapppedByteBuffer 零拷贝技术(省略了内核态到用户态的复制过程) 默认commitLog的大小是1g(为了保证使用零拷贝)
  • commitLog:存储消息的元数据
  • consumerQueue:存储消息在commitLog的索引
  • indexFile:为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过indexFile来查询消息的方法不影响发送与消费消息的主流程。
  • 刷盘机制:同步刷盘(可靠性高)和异步刷盘(吞吐量高 推荐)
  • 高可用机制:broker双主双从保证高可用性(采用同步复制方式),name server通过集群保证高可用性

消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第1张图片
消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第2张图片


25、RabbitMQ 由Erlang语言开发 端口号5672 (out)

1、rabbitMQ基本概念

  • 1、broker:就是消息队列服务器实体
  • 2、producer:生产者,就是投递消息的程序
  • 3、consumer:消费者,就是接受消息的程序
  • 4、exchange:消息交换机,他指定消息按什么规则,路由到哪个队列
  • 5、routing key:路由关键字,由生产者封装在消息头中,exchange根据这个关键字进行消息投递
  • 6、message queue:消息队列载体,每个消息都会被投入到一个或多个队列
  • 7、binding:绑定,他的作用就是把exchange和Queue按照路由规则绑定起来
  • 8、binding key:message queue对接受消息的限制条件,有消费者在binding时指定

2、消息的分发流程

  • 1、productor发送消息
  • 2、exchangge接受Message,解析消息头得到routing key
  • 3、exchange 有exchange type决定routingkey和bindingkey匹配方式,或是否互虐routingkey,广播发送
  • 4、exchange决策完成后将消息发送给满足条件的队列
  • 5、监听该队列的Consumer读取消息

3、Exchange 类型
它是Exchange在路由消息时的分发策略,目前RabbitQueue常见的类型有三种:Direct Exchange,Fanout Exchange,Topic Exchange,
实际项目中会根据业务特点进行选型

  • 1、direct exchange
    简单的直接匹配,通过routingkey和bindingkey匹配,匹配不上所有routingkey的消息被丢弃
  • 2、Fanout Exchange
    不使用routingkey匹配,当向所有消费者广播消息时,只要绑定到exchange的队列都可以接受消息。
  • 3、topic exchange:
    在 Direct Exchange 基础上增加了模糊匹配,BindingKey 可以使用和#通配符,而RoutingKey 中的多个单词用“.”隔开。
    通配符“
    ”代表匹配一个单词,“#”代表匹配零个或多个单词。

4、rabbitMQ知识点

  • 1、rabbitMQ是一个由Erlang语言开发的基于AMQP标准的开源框架
  • 2、特点:
    1、保证可靠性:持久化、传输确认、发布确认
    2、灵活的路由功能:在消息进入队列之前,是通过Exchange来路由消息的
    3、支持消息集群
    4、具有高可用性
    5、提供跟踪机制:如消息异常

5、JMS和AMQP的区别?
JMS AMQP
1、定义 java api 网络线级协议
2、跨语言 否 是
3、跨平台 否 是
4、model 提供两种消息模式p2p pub/sub 提供了五种消息模型 direct交换机/fanout交换机/topic交换机/headers交换机/system交换机,本质上,后4中与jms的pub/sub模型没有太大区别
5、支持消息类型 多种消息类型textMessage/MapMessage/BytesMessage/StreamMessage/ObjectMessage byte[] 当实际应用时,有复杂的消息,可以将消息序列化后发送

6、综合评价 jms定义了javaapi层面的标准,在java体系中,多个client均可以通过jms进行交互,不需要修改代码,但是其对跨平台的支持较差;
AMQP定义wire-level层的协议标准,天然具有跨平台、跨语言特性。


26、Rocketmq的事务消息,问了TCC?

TCC编程模式(两阶段提交的变形)将业务逻辑分为三块:try预留业务资源(类似DML锁定资源),confirm确认执行业务操作(类似commit),cancel取消执行业务操作(类似rollback)以下单为例:try阶段会扣除库存,confirm阶段更新订单状态,如果更新订单失败,会进入cancel阶段,恢复库存。TCC开源框架:tcc-transaction (补偿性分布式事务框架)

后续补充该知识点


27、列举一个常用的消息中间件,如果消息要保序如何实现? 政采云问到了

activeMQ的幂等
接收端一定要控制消息的幂等性(某一消息的多次执行的结果与一次执行的结果没有差别,即是幂等)

保序的解决方案:
就是让订阅端先保存消息,之后再处理

后续补充该知识点

28、Mq的持久化机制

消息发送:发送同步消息,发送异步消息,单向发送消息
消费消息:负载均衡模式,广播模式

导入mq客户端依赖

<dependency>
 <groupId>org.apache.rocketmq groupId>
<aritfactId>rocketmq-clientaritfactId>
<version>4.4.0version>
dependency>

消息发送者步骤分析
1、创建消息生产者producer,并制定生产者组名
2、指定nameserver地址
3、启动producer
4、创建消息对象,指定主题topic,tag和消息体
5、发送消息,
6、关闭生产者producer

消息消费者步骤分析
1、创建消费者consumer,制定消费者组名;
2、制定namespace地址;
3、启动producer;
4、创建消息对象,指定主题topic,tag和消息体;
5、发送消息;
6、关闭生产者producer。

单向发送消息

消息重试
消息重试配置方式
普通消息

单向发送消息:不用关心发送结果的场景,例如:日志发送
消费消息:
1、负载均衡模式
多个消费者共同消费队列消息,每个消费者处理的消息不同
2、广播模式
每个消费者消费的消息都是相同的
(无序消息)普通,定时,延时,事务消息
默认允许每条消息最多重试16次(时间:4小时46分钟)
messageID不会被改变

想重试,三种方式:
1、返回Action.ReconsumeLater(推荐)
2、返回NUll
3、抛出异常
不想重试:
Action.CommitMessage
顺序消息
保证的是局部顺序,业务相关的消息放在一个队列,然后消费者针对一个队列的消息使用单线程来处理。

(顺序消息)不断进行消息重试,每次间隔1s,会出现消息消费被阻塞的情况 关注
死信队列
超过最大重试次数后还未被正常消费,进入死信队列(3天之内处理)
处理方式:排查造成死信队列的原因后,在消息队列RocketMq控制台重新发送该消息,让消费者重新消费
延时消息
RocketMQ并不支持任意时间的延迟,需要设置几个固定的延时等级(1s到2h)

技巧:使用system.in.read()可以保持线程处于运行状态,让子线程能正常运行

批量消息
批量发送消息能显著提高传递小消息的性能,限制是这些批量消息应该有相同的topic,相同的waitStoreMsgOK,而且不能是延时消息。
总大小不应超过4MB,超过4MB时,最好把消息进行分割

过滤消息
1、由tag过滤
2、根据sql过滤

事务消息
事务消息的流程:
正常事务消息的发送和提交,
1、发送消息(half消息)
2、服务端响应消息写入结果,
3、根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)
4、根据本地事务状态执行commit或者rollback(commit操作生成消息索引,消息对消费者可见)
事务消息的补偿流程
1、对没有commit,rollback的事务消息(pending状态的消息,从服务端发起一次”回查“)
2、producer收到回查消息,检查回查消息对应的本地事务的状态
3、根据本地事务状态,重新commit或者rollback
补偿阶段用于解决消息commit或rollback发生超时或失败的情况。

事务消息状态
transactionStatus.commitTransaction:提交事务,它允许消费者消费此消息
transactionStatus.rollbackTransaction 回滚事务,它代表该消息将被删除,不允许被消费
transactionStatus.unknown:中间状态,它代表需要检查消息队列来确定状态

消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第3张图片

事务消息核心流程如上图所示。
事务消息的使用限制:

  • 1、不支持延时消息和批量消息
  • 2、为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为15次,但是用户可以通过broker配置文件的transactionCheckMax参数来修改此限制,如果已经检查某条消息超过N次的话,则broker将丢弃此消息。
  • 3、事务消息将在broker配置文件中的参数transactionMsgTimeout这样的特定时间长度之后被检查,当发送事务消息后,用户还可以通过设置用户属性check_IMMUNITY_IN_SECONDS来改变这个限制,该参数优先于transactionMSGtimeout 参数
  • 4、事务性消息可能不止一次被检查或消费。
  • 5、提交给用户的目标主题消息可能会失效。
  • 6、事务消息的生产者ID不能与其他类型消息的ID共享。与其他类型消息不同,事务消息允许反向查询,MQ服务器能通过他们的生产者ID查询到消费者。

29、RocketMQ项目实战在这里插入代码片

项目实战(下单,支付)(使用技术:dubbo,RocketMQ,zookeeper,Springboot,MySql)
springboot整合rocketMQ
1、消息生产者
添加依赖
配置文件
启动类
测试类

rocketMQTemplate.convertAndSend(“springboot-rocketmq”,”hello springboot”,111);

2、消息消费者
消息监听器

@RocketMQMessageListener(topic=“springboot-mq”,consumerGoup= “”)
Public class Consumer implements RocketMQListener<String>{
     }

3 ZMQ调试技巧

  • 1、通过该链接 http://ipaas.cai-inc.com/zmq/#/msg_query 按topic查询消息体,如
    {“shopName”:“韦德测试供应商0126”,“userName”:“gys0125”,“userId”:10007398824,“orgId”:147381903370240}
    消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第4张图片
  • 2、点击消息发送填充topic,tag,消息体,然后发送消息
  • 3、在程序中将provider的version改为自己独有的版本号,以免影响其他人测试,然后启动应用
    以test环境为例,启动参数为:-Denv=FAT -Dapollo.cluster=test -Drebel.spring_plugin=true -Dapollo.meta=http://172.16.101.16:8080
  • 4、打断点调试,各个分支,各种数据流转都需要考虑清楚,确保万无一失。

30、高级功能和源码分析

后续补充

31、Kafka消息中间件+Flink实时计算框架

1、Kafka如何保证消息队列的高可用?

Kafka 一个最基本的架构认识:由多个 broker 组成,每个 broker 是一个节点;你创建一个 topic,这个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据。
这就是天然的分布式消息队列,就是说一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据。
实际上 RabbmitMQ 之类的,并不是分布式消息队列,它就是传统的消息队列,只不过提供了一些集群、HA(High Availability, 高可用性) 的机制而已,因为无论怎么玩儿,RabbitMQ 一个 queue 的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个 queue 的完整数据。
Kafka 0.8 以前,是没有 HA 机制的,就是任何一个 broker 宕机了,那个 broker 上的 partition 就废了,没法写也没法读,没有什么高可用性可言。比如说,我们假设创建了一个 topic,指定其 partition 数量是 3 个,分别在三台机器上。但是,如果第二台机器宕机了,会导致这个 topic 的 1/3 的数据就丢了,因此这个是做不到高可用的。

消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第5张图片
Kafka 0.8 以后,提供了 HA 机制,就是 replica(复制品) 副本机制。每个 partition 的数据都会同步到其它机器上,形成自己的多个 replica 副本。所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。只能读写 leader?很简单,要是你可以随意读写每个 follower,那么就要 care 数据一致性的问题,系统复杂度太高,很容易出问题。Kafka 会均匀地将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。

消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第6张图片
这么搞,就有所谓的高可用性了,因为如果某个 broker 宕机了,没事儿,那个 broker上面的 partition 在其他机器上都有副本的。如果这个宕机的 broker 上面有某个 partition 的 leader,那么此时会从 follower 中重新选举一个新的 leader 出来,大家继续读写那个新的 leader 即可。这就有所谓的高可用性了。
写数据的时候,生产者就写 leader,然后 leader 将数据落地写本地磁盘,接着其他 follower 自己主动从 leader 来 pull 数据。一旦所有 follower 同步好数据了,就会发送 ack 给 leader,leader 收到所有 follower 的 ack 之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)
消费的时候,只会从 leader 去读,但是只有当一个消息已经被所有 follower 都同步成功返回 ack 的时候,这个消息才会被消费者读到。

2、Kafka如何保证消息不重复消费(幂等性)?

所有的消息队列都会有这样重复消费的问题,因为这是不MQ来保证,而是我们自己开发保证的,我们使用Kakfa来讨论是如何实现的。

  • Kakfa有个offset的概念,就是每个消息写进去都会有一个offset值,代表消费的序号,然后consumer消费了数据之后,默认每隔一段时间会把自己消费过的消息的offset值提交,表示我已经消费过了,下次要是我重启啥的,就让我从当前提交的offset处来继续消费。
    但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次

其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性。

举个例子吧。假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。幂等性,通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错。

所以第二个问题来了,怎么保证消息队列消费的幂等性?
其实还是得结合业务来思考,这里给几个思路:

  • 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。
  • 比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。
  • 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
  • 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
    消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第7张图片
    当然,如何保证 MQ 的消费是幂等性的,需要结合具体的业务来看。

3、如何保证消息的可靠传输(不丢失)?

MQ的基本原则就是数据不能多一条,也不能少一条,不能多其实就是我们前面重复消费的问题。不能少,就是数据不能丢,像计费,扣费的一些信息,是肯定不能丢失的。
数据的丢失问题,可能出现在生产者、MQ、消费者中。

消费者丢数据

  • 唯一可能导致消费者弄丢数据的情况,就是说,你消费到了这个消息,然后消费者那边自动提交了 offset,让 Kafka 以为你已经消费好了这个消息,但其实你才刚准备处理这个消息,你还没处理,你自己就挂了,此时这条消息就丢咯。

  • 这不是跟 RabbitMQ 差不多吗,大家都知道 Kafka 会自动提交 offset,那么只要关闭自动提交 offset,在处理完之后自己手动提交 offset,就可以保证数据不会丢。但是此时确实还是可能会有重复消费,比如你刚处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了。

  • 生产环境碰到的一个问题,就是说我们的 Kafka 消费者消费到了数据之后是写到一个内存的 queue 里先缓冲一下,结果有的时候,你刚把消息写入内存 queue,然后消费者会自动提交 offset。然后此时我们重启了系统,就会导致内存 queue 里还没来得及处理的数据就丢失了。

Kafka丢数据

  • 这块比较常见的一个场景,就是 Kafka 某个 broker 宕机,然后重新选举 partition 的 leader。大家想想,要是此时其他的 follower 刚好还有些数据没有同步,结果此时 leader 挂了,然后选举某个 follower 成 leader 之后,不就少了一些数据?这就丢了一些数据啊。
    生产环境也遇到过,我们也是,之前 Kafka 的 leader 机器宕机了,将 follower 切换为 leader 之后,就会发现说这个数据就丢了。
    所以此时一般是要求起码设置如下 4 个参数:

我们生产环境就是按照上述要求配置的,这样配置之后,至少在 Kafka broker 端就可以保证在 leader 所在 broker 发生故障,进行 leader 切换时,数据不会丢失。
给 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本。
在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 吧。
在 producer 端设置 acks=all:这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。
在 producer 端设置 retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了。

生产者丢数据
如果按照上述的思路设置了 acks=all,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

4、如何保证消息的顺序性?

我举个例子,我们以前做过一个 mysql binlog 同步的系统,压力还是非常大的,日同步数据要达到上亿,就是说数据从一个 mysql 库原封不动地同步到另一个 mysql 库里面去(mysql -> mysql)。常见的一点在于说比如大数据 team,就需要同步一个 mysql 库过来,对公司的业务系统的数据做各种复杂的操作。
你在 mysql 里增删改一条数据,对应出来了增删改 3 条 binlog 日志,接着这三条 binlog 发送到 MQ 里面,再消费出来依次执行,起码得保证人家是按照顺序来的吧?不然本来是:增加、修改、删除;你楞是换了顺序给执行成删除、修改、增加,不全错了么。
本来这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。

Kafka:比如说我们建了一个 topic,有三个 partition。生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。消费者从 partition 中取出来数据的时候,也一定是有顺序的。到这里,顺序还是 ok 的,没有错乱。接着,我们在消费者里可能会搞多个线程来并发处理消息。因为如果消费者是单线程消费处理,而处理比较耗时的话,比如处理一条消息耗时几十 ms,那么 1 秒钟只能处理几十条消息,这吞吐量太低了。而多个线程并发跑的话,顺序可能就乱掉了。
消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第8张图片
Kafka解决方案
一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。
写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
消息中间件ActiveMQ/RabbitMQ/RocketMQ/Kafka从入门到精通_第9张图片

5、如何处理消息推积?

大量消息在 mq 里积压了几个小时了还没解决
一个消费者一秒是 1000 条,一秒 3 个消费者是 3000 条,一分钟就是 18 万条。所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概 1 小时的时间才能恢复过来。
一般这个时候,只能临时紧急扩容了,具体操作步骤和思路如下:

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

6、mq 中的消息过期失效了

假设你用的是 RabbitMQ,RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。
这个情况下,就不是说要增加 consumer 消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。也只能是这样了。
假设 1 万个订单积压在 mq 里面,没有处理,其中 1000 个订单都丢了,你只能手动写程序把那 1000 个订单给查出来,手动发到 mq 里去再补一次。

7、mq 都快写满了

如果消息积压在 mq 里,你很长时间都没有处理掉,此时导致 mq 都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。

8、kafka的持久化机制 kafka down掉会不会丢数据

写数据:过内存S大小(S可设置)的数据后,直接刷进磁盘,追加写入文件;
读数据:根据offset读取位置之后S大小的数据,进内存;
删数据:直接删磁盘文件(segment file),先删老文件(可设置)。

9、有哪几种持久化机制?区别

32、RocketMQ常见面试题

1、在RocketMQ中,消息丢失问题

  1. 当你系统需要保证百分百消息不丢失,你可以使用生产者每发送一个消息,Broker 同步返回一个消息发送成功的反馈消息
  2. 即每发送一个消息,同步落盘后才返回生产者消息发送成功,这样只要生产者得到了消息发送生成的返回,事后除了硬盘损坏,都可以保证不会消息丢失

2 同步落盘怎么才能快?

  1. 使用 FileChannel + DirectBuffer 池,使用堆外内存,加快内存拷贝
  2. 使用数据和索引分离,当消息需要写入时,使用 commitlog 文件顺序写,当需要定位某个消息时,查询index 文件来定位,从而减少文件IO随机读写的性能损耗

3 消息堆积的问题

  1. 后台定时任务每隔72小时,删除旧的没有使用过的消息信息
  2. 根据不同的业务实现不同的丢弃任务,具体参考线程池的 AbortPolicy,例如FIFO/LRU等(RocketMQ没有此策略)
  3. 消息定时转移,或者对某些重要的 TAG 型(支付型)消息真正落库

4 定时消息的实现

  1. 实际 RocketMQ 没有实现任意精度的定时消息,它只支持某些特定的时间精度的定时消息
  2. 实现定时消息的原理是:创建特定时间精度的 MessageQueue,例如生产者需要定时1s之后被消费者消费,你只需要将此消息发送到特定的 Topic,例如:MessageQueue-1 表示这个 MessageQueue 里面的消息都会延迟一秒被消费,然后 Broker 会在 1s 后发送到消费者消费此消息,使用 newSingleThreadScheduledExecutor 实现

5 顺序消息的实现

  1. 与定时消息同原理,生产者生产消息时指定特定的 MessageQueue ,消费者消费消息时,消费特定的 MessageQueue,其实单机版的消息中心在一个 MessageQueue 就天然支持了顺序消息
  2. 注意:同一个 MessageQueue 保证里面的消息是顺序消费的前提是:消费者是串行的消费该 MessageQueue,因为就算 MessageQueue 是顺序的,但是当并行消费时,还是会有顺序问题,但是串行消费也同时引入了两个问题:
    1. 引入锁来实现串行
    2. 前一个消费阻塞时后面都会被阻塞

6 分布式消息的实现(事务消息)

  1. 需要前置知识:2PC
  2. RocketMQ4.3 起支持,原理为2PC,即两阶段提交,prepared->commit/rollback
  3. 生产者发送事务消息,假设该事务消息 Topic 为 Topic1-Trans,Broker 得到后首先更改该消息的 Topic 为 Topic1-Prepared,该 Topic1-Prepared 对消费者不可见。然后定时回调生产者的本地事务A执行状态,根据本地事务A执行状态,来是否将该消息修改为 Topic1-Commit 或 Topic1-Rollback,消费者就可以正常找到该事务消息或者不执行等
    注意,就算是事务消息最后回滚了也不会物理删除,只会逻辑删除该消息

8 消息重复发送的避免

  1. RocketMQ 会出现消息重复发送的问题,因为在网络延迟的情况下,这种问题不可避免的发生,如果非要实现消息不可重复发送,那基本太难,因为网络环境无法预知,还会使程序复杂度加大,因此默认允许消息重复发送
  2. RocketMQ 让使用者在消费者端去解决该问题,即需要消费者端在消费消息时支持幂等性的去消费消息
  3. 最简单的解决方案是每条消费记录有个消费状态字段,根据这个消费状态字段来是否消费或者使用一个集中式的表,来存储所有消息的消费状态,从而避免重复消费
  4. 具体实现可以查询关于消息幂等消费的解决方案

9 RocketMQ 不使用 ZooKeeper 作为注册中心的原因,以及自制的 NameServer 优缺点?

  1. ZooKeeper 作为支持顺序一致性的中间件,在某些情况下,它为了满足一致性,会丢失一定时间内的可用性,RocketMQ 需要注册中心只是为了发现组件地址,在某些情况下,RocketMQ 的注册中心可以出现数据不一致性,这同时也是 NameServer 的缺点,因为 NameServer 集群间互不通信,它们之间的注册信息可能会不一致
  2. 另外,当有新的服务器加入时,NameServer 并不会立马通知到 Produer,而是由 Produer 定时去请求 NameServer 获取最新的 Broker/Consumer 信息(这种情况是通过 Producer 发送消息时,负载均衡解决)

10 加分项咯

  1. 包括组件通信间使用 Netty 的自定义协议
  2. 消息重试负载均衡策略(具体参考 Dubbo 负载均衡策略)
  3. 消息过滤器(Producer发送消息到 Broker,Broker 存储消息信息,Consumer消费时请求 Broker 端从磁盘文件查询消息文件时, 在 Broker 端就使用过滤服务器进行过滤)
  4. Broker 同步双写和异步双写中 Master 和 Slave 的交互
  5. Broker 在 4.5.0 版本更新中引入了基于 Raft 协议的多副本选举,之前这是商业版才有的特性 ISSUE-1046

11 关于消息队列其他一些常见的问题展望(后续补充

  1. 引入消息队列之后如何保证高可用性?
  2. 如何保证消息不被重复消费呢?
  3. 如何保证消息的可靠性传输(如何处理消息丢失的问题)?
  4. 我该怎么保证从消息队列里拿到的数据按顺序执行?
  5. 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
  6. 如果让你来开发一个消息队列中间件,你会怎么设计架构?

《寒山子诗集》:寒山与拾得两位大师是佛教史上著名的诗僧。唐代天台山国清寺隐僧寒山与拾得,行迹怪诞,相传是文殊菩萨与普贤菩萨的化身。寒山问曰:“世间有人谤我、欺我、辱我、笑我、轻我、贱我、恶我、骗我,该如何处之乎?”拾得答曰:“只需忍他、让他、由他、避他、耐他、敬他、不要理他、再待几年,你且看他。”这个绝妙的问答,蕴含了面对人我是非的处世之道,因此虽经一千多年,至今仍然脍炙人口。

你可能感兴趣的:(深入理解消息中间件,消息队列,消息中间件,RocketMQ,Kafka)