【原创】RabbitMQ 之 no_ack 分析

简介:

      熟悉 RabbitMQ 的人肯定知道    no_ack 属性是在调用 Basic.Consume 方法时可以设置的一个重要参数。本文主要针对 no_ack 设置的两种情况,通过抓包分析的形式讲解下实际应用中的异同,并总结一下相关的处理经验。

============ 我是分隔线 =============

      no_ack 的用途:确保 message 被 consumer “成功”处理了。这里“成功”的意思是,(在设置了 no_ack=false 的情况下)只要 consumer 手动应答了 Basic.Ack ,就算其“成功”处理了。  

情况一no_ack=true (此时为自动应答  
      在这种情况下,consumer 会在接收到 Basic.Deliver + Content-Header + Content-Body 之后,立即回复 Ack 。而这个 Ack 是 TCP 协议中的 Ack 。此 Ack 的回复不关心 consumer 是否对接收到的数据进行了处理,当然也不关心处理数据所需要的耗时。  

图1:(Producer+Consumer)  
 

图2:(Consumer)
 

图3:(Producer)  
 

情况二:no_ack=false (此时为手动应答  
      在这种情况下,要求 consumer 在处理完接收到的 Basic.Deliver + Content-Header + Content-Body 之后才回复 Ack 。而这个 Ack 是 AMQP 协议中的 Basic.Ack 。此 Ack 的回复是和业务处理相关的,所以具体的回复时间应该要取决于业务处理的耗时。

图4:(Producer+Consumer)  
 

图5:(Consumer)
 
 

图6:(Producer)  
 

总结:  
  • Basic.Ack 发回给 RabbitMQ 以告知,可以将相应 message 从 RabbitMQ 的消息缓存中移除。
  • Basic.Ack 未被 consumer 发回给 RabbitMQ 前出现了异常,RabbitMQ 发现与该 consumer 对应的连接被断开,之后将该 message 以轮询方式发送给其他 consumer (假设存在多个 consumer 订阅同一个 queue)。
  • 在 no_ack=true 的情况下,RabbitMQ 认为 message 一旦被 deliver 出去了,就已被确认了,所以会立即将缓存中的 message 删除。所以在 consumer 异常时会导致消息丢失。
  • 来自 consumer 侧的 Basic.Ack 与 发送给 Producer 侧的 Basic.Ack 没有直接关系。


============ 我是分隔线 =============  

最后贴上自己改造的、基于 libevent 实现的 rabbitmq-c 的测试打印。  

情况一:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
[warn] evsignal_init: socketpair: No error
drive_machine: [conn_init]  ---  in TCP 3-way connecting!
drive_machine: [conn_connecting]  ---  connection timeout 1 time on socket(6040)
drive_machine: [conn_connected]  ---  connected on socket(6040)
 
6040: conn_state change   connected ==> snd_protocol_header
   --> Send Protocol.Header!
6040: conn_state change   snd_protocol_header ==> rcv_connection_start_method
   <-- Recv Connection.Start Method frame!
6040: conn_state change   rcv_connection_start_method ==> snd_connection_start_rsp_method
   --> Send Connection.Start-Ok Method frame!
6040: conn_state change   snd_connection_start_rsp_method ==> rcv_connection_tune_method
   <-- Recv Connection.Tune Method frame!
6040: conn_state change   rcv_connection_tune_method ==> snd_connection_tune_rsp_method
   --> Send Connection.Tune-Ok Method frame!
6040: conn_state change   snd_connection_tune_rsp_method ==> snd_connection_open_method
   --> Send Connection.Open Method frame!
6040: conn_state change   snd_connection_open_method ==> rcv_connection_open_rsp_method
   <-- Recv Connection.Open-Ok Method frame!
6040: conn_state change   rcv_connection_open_rsp_method ==> snd_channel_open_method
   --> Send Channel.Open Method frame!
6040: conn_state change   snd_channel_open_method ==> rcv_channel_open_rsp_method
   <-- Recv Channel.Open-Ok Method frame!
6040: conn_state change   rcv_channel_open_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Queue Declaring!
6040: conn_state change   idle ==> snd_queue_declare_method
   --> Send Queue.Declare Method frame!
6040: conn_state change   snd_queue_declare_method ==> rcv_queue_declare_rsp_method
   <-- Recv Queue.Declare-Ok Method frame!
6040: conn_state change   rcv_queue_declare_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Queue Binding!
6040: conn_state change   idle ==> snd_queue_bind_method
   --> Send Queue.Bind Method frame!
6040: conn_state change   snd_queue_bind_method ==> rcv_queue_bind_rsp_method
   <-- Recv Queue.Bind-Ok Method frame!
6040: conn_state change   rcv_queue_bind_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Basic QoS!
6040: conn_state change   idle ==> snd_basic_qos_method
   --> Send Basic.Qos Method frame!
6040: conn_state change   snd_basic_qos_method ==> rcv_basic_qos_rsp_method
   <-- Recv Basic.Qos-Ok Method frame!
6040: conn_state change   rcv_basic_qos_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Basic Consuming!
6040: conn_state change   idle ==> snd_basic_consume_method
   --> Send Basic.Consume Method frame!
6040: conn_state change   snd_basic_consume_method ==> rcv_basic_consume_rsp_method
   <-- Recv Basic.Consume-Ok Method frame!
6040: conn_state change   rcv_basic_consume_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Start waiting to recv!
6040: conn_state change   idle ==> rcv_basic_deliver_method
drive_machine: wait for Basic.Deliver method another 10 seconds!!
drive_machine: wait for Basic.Deliver method another 10 seconds!!
 
   <-- Recv Basic.Deliver Method frame!
6040: conn_state change   rcv_basic_deliver_method ==> rcv_basic_content_header
   <-- Recv Content.Header frame!
6040: conn_state change   rcv_basic_content_header ==> rcv_basic_content_body
   <-- Recv Content.Body frame!
Content Body is [Hello World betty].
@@@ CB: body len : [17]    body : [Hello World betty]
6040: conn_state change   rcv_basic_content_body ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Start waiting to recv!
6040: conn_state change   idle ==> rcv_basic_deliver_method
drive_machine: wait for Basic.Deliver method another 10 seconds!!
drive_machine: wait for Basic.Deliver method another 10 seconds!!

情况二:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
[warn] evsignal_init: socketpair: No error
drive_machine: [conn_init]  ---  in TCP 3-way connecting!
drive_machine: [conn_connecting]  ---  connection timeout 1 time on socket(6040)
drive_machine: connected on socket(6040)
6040: conn_state change   connected ==> snd_protocol_header
   --> Send Protocol.Header!
6040: conn_state change   snd_protocol_header ==> rcv_connection_start_method
   <-- Recv Connection.Start Method frame!
6040: conn_state change   rcv_connection_start_method ==> snd_connection_start_rsp_method
   --> Send Connection.Start-Ok Method frame!
6040: conn_state change   snd_connection_start_rsp_method ==> rcv_connection_tune_method
   <-- Recv Connection.Tune Method frame!
6040: conn_state change   rcv_connection_tune_method ==> snd_connection_tune_rsp_method
   --> Send Connection.Tune-Ok Method frame!
6040: conn_state change   snd_connection_tune_rsp_method ==> snd_connection_open_method
   --> Send Connection.Open Method frame!
6040: conn_state change   snd_connection_open_method ==> rcv_connection_open_rsp_method
   <-- Recv Connection.Open-Ok Method frame!
6040: conn_state change   rcv_connection_open_rsp_method ==> snd_channel_open_method
   --> Send Channel.Open Method frame!
6040: conn_state change   snd_channel_open_method ==> rcv_channel_open_rsp_method
   <-- Recv Channel.Open-Ok Method frame!
6040: conn_state change   rcv_channel_open_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Queue Declaring!
6040: conn_state change   idle ==> snd_queue_declare_method
   --> Send Queue.Declare Method frame!
6040: conn_state change   snd_queue_declare_method ==> rcv_queue_declare_rsp_method
   <-- Recv Queue.Declare-Ok Method frame!
6040: conn_state change   rcv_queue_declare_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Queue Binding!
6040: conn_state change   idle ==> snd_queue_bind_method
   --> Send Queue.Bind Method frame!
6040: conn_state change   snd_queue_bind_method ==> rcv_queue_bind_rsp_method
   <-- Recv Queue.Bind-Ok Method frame!
6040: conn_state change   rcv_queue_bind_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Basic QoS!
6040: conn_state change   idle ==> snd_basic_qos_method
   --> Send Basic.Qos Method frame!
6040: conn_state change   snd_basic_qos_method ==> rcv_basic_qos_rsp_method
   <-- Recv Basic.Qos-Ok Method frame!
6040: conn_state change   rcv_basic_qos_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Basic Consuming!
6040: conn_state change   idle ==> snd_basic_consume_method
   --> Send Basic.Consume Method frame!
6040: conn_state change   snd_basic_consume_method ==> rcv_basic_consume_rsp_method
   <-- Recv Basic.Consume-Ok Method frame!
6040: conn_state change   rcv_basic_consume_rsp_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Start waiting to recv!
6040: conn_state change   idle ==> rcv_basic_deliver_method
drive_machine: wait for Basic.Deliver method another 10 seconds!!
drive_machine: wait for Basic.Deliver method another 10 seconds!!
drive_machine: wait for Basic.Deliver method another 10 seconds!!
 
   <-- Recv Basic.Deliver Method frame!
6040: conn_state change   rcv_basic_deliver_method ==> rcv_basic_content_header
   <-- Recv Content.Header frame!
6040: conn_state change   rcv_basic_content_header ==> rcv_basic_content_body
   <-- Recv Content.Body frame!
Content Body is [Hello World betty].
@@@ CB: body len : [17]    body : [Hello World betty]
6040: conn_state change   rcv_basic_content_body ==> snd_basic_ack_method
   --> Send Basic.Ack Method frame!
6040: conn_state change   snd_basic_ack_method ==> idle
 
drive_machine: [conn_idle]  ---  [CONSUMER]: Start waiting to recv!
6040: conn_state change   idle ==> rcv_basic_deliver_method
drive_machine: wait for Basic.Deliver method another 10 seconds!!
drive_machine: wait for Basic.Deliver method another 10 seconds!!



相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
3月前
|
消息中间件 运维 Java
RabbitMQ快速入门(简单收发消息)
RabbitMQ快速入门(简单收发消息)
RabbitMQ快速入门(简单收发消息)
|
5月前
|
消息中间件
消息中间件系列教程(16) -RabbitMQ-应答模式
消息中间件系列教程(16) -RabbitMQ-应答模式
35 0
|
9月前
|
消息中间件
rabbitmq消息的确认机制ack
rabbitmq消息的确认机制ack
|
消息中间件 中间件
RabbitMQ学习(六):发布确认
confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信 道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调 方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消 息,生产者应用程序同样可以在回调方法中处理该 nack 消息。
110 0
RabbitMQ学习(六):发布确认
|
消息中间件 存储 前端开发
硬核!SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
硬核!SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
382 0
硬核!SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
|
消息中间件 Java
RabbitMQ 实战教程(三) 发布/订阅
RabbitMQ 实战教程(三) 发布/订阅
123 0
RabbitMQ 实战教程(三) 发布/订阅
|
消息中间件 JavaScript 小程序
SpringBoot+RabbitMQ 实现手动消息确认(ACK)上
SpringBoot+RabbitMQ 实现手动消息确认(ACK)
SpringBoot+RabbitMQ 实现手动消息确认(ACK)上
|
消息中间件
SpringBoot+RabbitMQ 实现手动消息确认(ACK)下
SpringBoot+RabbitMQ 实现手动消息确认(ACK)下
SpringBoot+RabbitMQ 实现手动消息确认(ACK)下