【原创】使用 rabbitmq 中 heartbeat 功能可能会遇到的问题

【问题场景】
      客户端以 consumer 身份订阅到 rabbitmq server 上的 queue 上,客户端侧在 AMQP 协议的 Connection.Tune-Ok 信令中,设置 heartbeat 为 0,即要求服务器侧不启用 heartbeat 功能。服务器由于异常断电原因停止服务,结果客户端在短时间内无法感知到服务器端已经异常。 

       刚刚出现这个问题时,就有测试人员和业务人员找到我这边说:经过改造的 rabbitmq-c 库可能存在重大 bug,服务器都关闭了,客户端怎么还那像什么都没发生一样继续工作着呢?听到这种疑问,我只问了两个问题就想到了答案: 

  • 业务中是不是仅仅作为 consumer 运行的?
  • 服务器能否确认是因为异常断电导致停止服务?
  • 服务器和业务程序之间是否还有中间路由设备?

业务人员告诉我上述问题的答案分别是:是的、是的、没有。呵呵~~所以答案就已经确定了,你想到了么? 

【问题分析】 
这个问题可以从以下两个层面进行分析: 
1. TCP 协议层面 
      在此层面上讲,上述问题属于典型的 TCP 协议中的“半打开”问题,典型描述如下: 

如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的 TCP 连接称为半打开(Half-Open)的。任何一端的主机异常都可能导致发生这种情况。只要不打算在半打开连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。

半打开连接的一个常见原因是,当客户主机突然掉电,而不是正常的结束客户应用程序后再关机。当然这里所谓的客户机并不是仅仅表示客户端。 
      在这种情况发生时,作为 TCP 链路上只接收不发送数据的一方,只能依靠 TCP 协议本身的 keepalive 机制来检查链路是否处于正常状态。而通常 keepalive 机制下,需要大约 2 个小时时间才能触发。 

2. AMQP 协议层面 
      在此层面上讲,客户端由于是作为 consumer 订阅到 queue 上的,所以在该 AMQP/TCP 连接上客户端不会主动发送数据到 rabbitmq server 侧。当服务器由于异常断电停止服务后,consumer 不会接收到 AMQP 协议层面的终止信令,所以无法感知对端的情况。 
      一种可能的解决办法是客户端侧在接收 N 次超时后,通过发送 AMQP 协议中的 Heartbeat 信令检测服务器端是否处于正常状态。 

      在场景描述中说道“客户端侧在 AMQP 协议的 Connection.Tune-Ok 信令中,设置 heartbeat 为 0”,如果是将 heartbeat 设置为 30 会如何?答案是会同时触发服务器端和客户端的 heartbeat 功能,即服务器端会在一段时间内没有数据需要发送给客户端的情况下,发送一个心跳包给客户端;或者一段时间内没有收到任何数据,则判定为心跳超时,最终会关闭tcp连接(参考这里)。而客户端侧同样会触发对发送和接收 heartbeat 计时器的维护,分别用于判定发送和接收的超时情况。 

在 amqp.h 头文件中可以看到目前 rabbitmq-c 对 heartbeat 的支持情况: 

?


1

2

3

4

5

* \param [in] heartbeat the number of seconds between heartbeat frame to

*             request of the broker. A value of 0 disables heartbeats.

*             Note rabbitmq-c only has partial support for hearts, as of

*             v0.4.0 heartbeats are only serviced during amqp_basic_publish(),

*             and amqp_simple_wait_frame()/amqp_simple_wait_frame_noblock()

目前 github 上的 rabbitmq-c 0.4.1 版本在 heartbeat 功能上的支持仅限上述 3 种 API。 

      所以,需要解决的问题可以描述为: 客户端作为 consumer 订阅到服务器上的 queue 后,在无业务数据需要处理时,需要通过检测 Heartbeat 帧(信令)来判定服务器是否处于异常状态(换句话说,自己是否已经是“半打开”的 TCP 连接)。 

【解决办法】
建议的解决办法如下: 

  • 客户端必须启用 heartbeat 功能(解决“半打开”问题的基础); 
  • 客户端需要支持在发送空闲时,发送 heartbeat 的功能(因为目前客户端作为 producer 是长连接到 rabbitmq server 上的); 
  • 客户端需要支持在接收空闲时,通过检测服务器端发送来的 heartbeat 帧来判定服务器端(或网络)是否处于正常状态(因为客户端作为 consumer 也是长连接到 rabbitmq server 上的,同时不会主动向 rabbitmq server 发送数据)。 

总结: 
      只要客户端启用 heartbeat ,那么服务器就会在满足“一定条件”时,定时向客户端发送 heartbeat 信令,同时也会检测在空闲状态达到规定时间后是否收到 heartbeat 信令;而客户端侧作为 consumer 时,需要判定是否接收到数据(无论是常规数据还是 heartbeat 信令),若在一定时间内没有接收到数据,则认为当前链路可能存在问题。后续可以从业务上触发 consume 关系的重新建立。 

      如下为使能了 heartbeat 功能后的打印输出: 
【 作为 consumer 的情况下出现网络断开时的打印 】 

?


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

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

[warn] evsignal_init: socketpair: No error

drive_machine: [conn_init]  ---  TCP 3-way handshake start! --> [172.16.81.111:5672][s:53144]

drive_machine: [conn_connecting]  ---  connection timeout 1 time on socket(53144)

drive_machine: [conn_connected]  ---  connected on socket(53144)

53144: conn_state change   connected ==> snd_protocol_header

  --> Send Protocol.Header!

53144: conn_state change   snd_protocol_header ==> rcv_connection_start_method

[53144] drive_machine: wait for Connection.Start method another 10 seconds!!

  <-- Recv Connection.Start Method frame!

53144: conn_state change   rcv_connection_start_method ==> snd_connection_start_rsp_method

  --> Send Connection.Start-Ok Method frame!

53144: conn_state change   snd_connection_start_rsp_method ==> rcv_connection_tune_method

  <-- Recv Connection.Tune Method frame!

53144: conn_state change   rcv_connection_tune_method ==> snd_connection_tune_rsp_method

  --> Send Connection.Tune-Ok Method frame!

53144: conn_state change   snd_connection_tune_rsp_method ==> snd_connection_open_method

  --> Send Connection.Open Method frame!

53144: conn_state change   snd_connection_open_method ==> rcv_connection_open_rsp_method

  <-- Recv Connection.Open-Ok Method frame!

53144: conn_state change   rcv_connection_open_rsp_method ==> snd_channel_open_method

  --> Send Channel.Open Method frame!

53144: conn_state change   snd_channel_open_method ==> rcv_channel_open_rsp_method

[53144] drive_machine: wait for Channel.Open-Ok method another 10 seconds!!

  <-- Recv Channel.Open-Ok Method frame!

53144: conn_state change   rcv_channel_open_rsp_method ==> idle

[53144] drive_machine: [conn_idle]  ---  [CONSUMER]: Queue Declaring!

53144: conn_state change   idle ==> snd_queue_declare_method

  --> Send Queue.Declare Method frame!

53144: conn_state change   snd_queue_declare_method ==> rcv_queue_declare_rsp_method

[53144] drive_machine: wait for Queue.Declare-Ok method another 10 seconds!!

  <-- Recv Queue.Declare-Ok Method frame!

53144: conn_state change   rcv_queue_declare_rsp_method ==> idle

[53144] drive_machine: [conn_idle]  ---  [CONSUMER]: Queue Binding!

53144: conn_state change   idle ==> snd_queue_bind_method

  --> Send Queue.Bind Method frame!

53144: conn_state change   snd_queue_bind_method ==> rcv_queue_bind_rsp_method

[53144] drive_machine: wait for Queue.Bind method another 10 seconds!!

  <-- Recv Queue.Bind Method frame!

need to code something!

53144: conn_state change   rcv_queue_bind_rsp_method ==> idle

[53144] drive_machine: [conn_idle]  ---  [CONSUMER]: Basic QoS!

53144: conn_state change   idle ==> snd_basic_qos_method

  --> Send Basic.Qos Method frame!

53144: conn_state change   snd_basic_qos_method ==> rcv_basic_qos_rsp_method

  <-- Recv Queue.Qos-Ok Method frame!

need to code something!

53144: conn_state change   rcv_basic_qos_rsp_method ==> idle

[53144] drive_machine: [conn_idle]  ---  [CONSUMER]: Basic Consuming!

53144: conn_state change   idle ==> snd_basic_consume_method

  --> Send Basic.Consume Method frame!

53144: conn_state change   snd_basic_consume_method ==> rcv_basic_consume_rsp_method

  <-- Recv Basic.Consume-Ok Method frame!

need to code something!

53144: conn_state change   rcv_basic_consume_rsp_method ==> idle

[53144] drive_machine: [conn_idle]  ---  [CONSUMER]: Start waiting to recv!

53144: conn_state change   idle ==> rcv_basic_deliver_method

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

      ### Recv AMQP_FRAME_HEARTBEAT frame! ###

  <-- Recv Heartbeat frame!

53144: conn_state change   rcv_basic_deliver_method ==> snd_heartbeat

  --> Send Heartbeat frame!

53144: conn_state change   snd_heartbeat ==> rcv_basic_deliver_method

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

      ### Recv AMQP_FRAME_HEARTBEAT frame! ###

  <-- Recv Heartbeat frame!

53144: conn_state change   rcv_basic_deliver_method ==> snd_heartbeat

  --> Send Heartbeat frame!

53144: conn_state change   snd_heartbeat ==> rcv_basic_deliver_method

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

      ### Recv AMQP_FRAME_HEARTBEAT frame! ###

  <-- Recv Heartbeat frame!

53144: conn_state change   rcv_basic_deliver_method ==> snd_heartbeat

  --> Send Heartbeat frame!

53144: conn_state change   snd_heartbeat ==> rcv_basic_deliver_method

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: wait for Basic.Deliver method another 10 seconds!!

[53144] drive_machine: Recv nothing for 60s!

[53144] drive_machine: Maybe network broken or rabbitmq server fucked! Plz retry consuming!

53144: conn_state change   rcv_basic_deliver_method ==> close

[53144] drive_machine: [conn_close]  ---  Connection Disconnect!

### CB: Connection Disconnect!    Msg : [Connection Disconnect]

【 作为 producer 的情况下出现网络断开时的打印 】 

?


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

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

[warn] evsignal_init: socketpair: No error

drive_machine: [conn_init]  ---  TCP 3-way handshake start! --> [172.16.81.111:5672][s:12184]

drive_machine: [conn_connecting]  ---  connection timeout 1 time on socket(12184)

drive_machine: [conn_connected]  ---  connected on socket(12184)

12184: conn_state change   connected ==> snd_protocol_header

  --> Send Protocol.Header!

12184: conn_state change   snd_protocol_header ==> rcv_connection_start_method

  <-- Recv Connection.Start Method frame!

12184: conn_state change   rcv_connection_start_method ==> snd_connection_start_rsp_method

  --> Send Connection.Start-Ok Method frame!

12184: conn_state change   snd_connection_start_rsp_method ==> rcv_connection_tune_method

[12184] drive_machine: wait for Connection.Tune method another 10 seconds!!

  <-- Recv Connection.Tune Method frame!

12184: conn_state change   rcv_connection_tune_method ==> snd_connection_tune_rsp_method

  --> Send Connection.Tune-Ok Method frame!

12184: conn_state change   snd_connection_tune_rsp_method ==> snd_connection_open_method

  --> Send Connection.Open Method frame!

12184: conn_state change   snd_connection_open_method ==> rcv_connection_open_rsp_method

[12184] drive_machine: wait for Connection.Open-Ok method another 10 seconds!!

  <-- Recv Connection.Open-Ok Method frame!

12184: conn_state change   rcv_connection_open_rsp_method ==> snd_channel_open_method

  --> Send Channel.Open Method frame!

12184: conn_state change   snd_channel_open_method ==> rcv_channel_open_rsp_method

[12184] drive_machine: wait for Channel.Open-Ok method another 10 seconds!!

  <-- Recv Channel.Open-Ok Method frame!

12184: conn_state change   rcv_channel_open_rsp_method ==> snd_channel_confirm_select_method

  --> Send Confirm.Select Method frame!

12184: conn_state change   snd_channel_confirm_select_method ==> rcv_channel_confirm_select_rsp_method

  <-- Recv Confirm.Select-Ok Method frame!

Channel in Confirm Mode!

12184: conn_state change   rcv_channel_confirm_select_rsp_method ==> idle

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find msg to send!

12184: conn_state change   idle ==> snd_basic_publish_method

  --> Send Basic.Publish Method frame!

12184: conn_state change   snd_basic_publish_method ==> snd_basic_content_header

  --> Send Content-Header frame!

12184: conn_state change   snd_basic_content_header ==> snd_basic_content_body

  --> Send Content-Body frame!

12184: conn_state change   snd_basic_content_body ==> rcv_basic_ack_method

  <-- Recv Basic.Ack Method frame!

### CB: Publisher Confirm -- [Basic.Ack]  Delivery_Tag:[1]  multiple:[0]

12184: conn_state change   rcv_basic_ack_method ==> idle

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

12184: conn_state change   idle ==> snd_heartbeat

  --> Send Heartbeat frame!

12184: conn_state change   snd_heartbeat ==> idle

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

[12184] drive_machine: [conn_idle]  ---  [PRODUCER]: Find no msg to send! wait another 1 seconds

12184: conn_state change   idle ==> snd_heartbeat

[12184] drive_machine: Send Heartbeat failed! status = -9

12184: conn_state change   snd_heartbeat ==> close

[12184] drive_machine: [conn_close]  ---  Connection Disconnect!

### CB: Connection Disconnect!    Msg : [Connection Disconnect]

时间: 2025-01-30 03:54:07

【原创】使用 rabbitmq 中 heartbeat 功能可能会遇到的问题的相关文章

【原创】RabbitMQ 的 shovel 插件使用

      从之前的两篇文章< [原创]RabbitMQ 之 Shovel(翻译) >和< [原创]RabbitMQ 之 Dynamic Shovel(翻译) >中,我们已经知道 shovel 插件的使用存在 static 和 dynamic 两种形式,其主要差异如下  Static Shovels Dynamic Shovels 基于 broker 的配置文件进行定义 基于 broker 的 parameter 参数进行定义 Require a restart of the ho

【原创】RabbitMQ 之 Shovel(翻译)

      为了方便工作中使用,对 RabbitMQ 的[Shovel]文档进行了翻译,鉴于自己水平有限,翻译中难免有纰漏产生,如果疑问,欢迎指出探讨.此文以中英对照方式呈现.      官方原文:http://www.rabbitmq.com/shovel.html ==============================  Shovel pluginShovel 插件 The Shovel plugin allows you to configure a number of shovel

【原创】RabbitMQ 相关问题汇总

[面向对象和免责声明]      本文不是面向初级 RabbitMQ 的使用者,本文面向的是对 RabbitMQ 有一定的研究,对使用中的细节问题有一定的思考,对各种模型背后的原因有强烈的探究欲望的人.本文的所有内容不保证 100% 正确,但至少是我目前为止认为正确的结论,如果您有任何高见,敬请赐教,不甚感激. [RabbitMQ 问答]       本章节主要解答一些在 RabbitMQ 使用过程中,经常被问到的问题.其实很多问题的答案都可以在各类文档里找到,建议多翻阅参考资料中给出的文档. 

【原创】RabbitMQ之PublisherConfirm实战问题总结

如何理解Publisher Confirm机制       Publisher Confirm机制(又称为Confirms或Publisher Acknowledgements)是作为解决事务机制性能开销大(导致吞吐量下降)而提出的另外一种保证消息不会丢失的方式. Publisher Confirm的协议交互过程 (补充说明:上图中未显示出info信息的两条交互对应的就是 Confirm.Select 和 Confirm.Select-ok ,显示不出来是因为 wireshark 没有对该扩展进

【原创】RabbitMQ官网文档翻译 -- Clustering Guide

      为了方便工作中使用,自己花费了周末空闲的时间对 RabbitMQ 的集群配置相关文档进行了翻译,鉴于自己水平有限,翻译中难免有纰漏产生,如果疑问,欢迎指出探讨.此文以中英对照方式呈现. 官方原文:http://www.rabbitmq.com/clustering.html ============== 我是分割线 ================ Clustering Guide集群配置 A RabbitMQ broker is a logical grouping of one

Javascript中暂停功能的实现

javascript <script language="javascript"> /*Javascript中暂停功能的实现 Javascript本身没有暂停功能(sleep不能使用)同时 vbscript也不能使用doEvents,故编写此函数实现此功能. javascript作为弱对象语言,一个函数也可以作为一个对象使用. 比如: function Test(){  alert("hellow");  this.NextStep=function()

网站设计中的功能与美

设计 从网络的发展来看,网页设计从无到有,从低级到高级,是基于技术的原因而受到客观的限制的,因为受传输带宽的限制,最早的网页是纯字母和数字的,没有图片和声音更别说是动画了,只能将占用空间很小的数字和字母来进行传输. 由此看来网页最初就是在功能性的基础之上发展起来的.到今天同样上网也离不开使用它的功能,所以网页的审美就首先要考虑其功能的要素(例如很多国外的大公司的网站也是这样做的).那么从功能出发来看待网页设计,我们就必须考虑,使用者如何从网页中得到功能,如何更好的得到功能.这就需要站在使用者的角

使用win7系统中语音识别功能操作电脑

Win7作为微软新一代操作系统,不仅有炫酷的桌面设计,也有新颖便利的功能,当然也有更智能化的功能设计,其中,语音控制就是一 个比较智能化的功能,动动嘴就可以实现对电脑的控制操作,抛开传统的鼠标和键盘,无疑也是人与电脑的更加智能化的"交流". 单位小李在得知win7语音识别的功能,非常想体验这种与电脑智能化的"交流",经过同事小王讲解后,很快就了解了这项智能化 的操作.原来,win7系统中语音识别功能,主要是通过给计算机设置麦克风,将语音指令与电脑中的操作指令相匹配,

WPS演示中插件功能及使用方法

  WPS演示中插件功能及使用方法 1.单击菜单栏的"工具"--"插件平台",弹出插件平台对话框,如图. 2.在"插件列表"选项卡中可以看到本机WPS已安装的插件,选中某个插件后会出现"卸载"和"禁用"按钮选项,"禁用"可暂时禁用该插件功能,"卸载"可以从本机删除该插件,删除后需要重新启动计算机才能从插件列表中清除. 3.切换到"推荐插件"选项卡中