1、RabbitMQ教程
《RabbitMQ Tutorials》https://www.rabbitmq.com/tutorials
2、环境准备
参考:《RabbitMQ练习(Hello World)》和《RabbitMQ练习(Work Queues)》。
确保RabbitMQ、Sender、Receiver、Receiver2容器正常安装和启动。
root@k0test1:~# docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management
root@k0test1:~# docker start sender
root@k0test1:~# docker start receiver
root@k0test1:~# docker start receiver2
root@k0test1:~# docker network inspect bridge
3、Routing练习
3.1 概述
之前练习了一个简单的日志系统,能够将日志消息广播给多个接收者。本练习中,将订阅消息的子集。例如,只将关键的错误消息定向到日志文件,同时在控制台上打印所有日志消息。
3.2 绑定(bindings)
绑定是连接交换机(exchanges)和队列(queues)的规则,它们定义了消息如何从一个交换机路由到一个或多个队列。之前的练习中创建过队列和交换机绑定:
channel.queue_bind(exchange=exchange_name, queue=queue_name)
绑定还可以包含一个额外的 routing_key
参数。为了避免与 basic_publish
参数里的routing_key混淆,我们将它称为binding key。以下是创建带有绑定键的绑定的方法:
channel.queue_bind(exchange=exchange_name, queue=queue_name, routing_key='black')
binding key的含义取决于exchange的类型。之前使用的fanout exchange会忽略binding key。
3.3 Direct exchange
之前的简单日志系统练习中,将所有消息广播给所有消费者。本练习中将根据消息的严重程度来过滤消息。例如,只将"critical errors"日志消息写入磁盘,“warning or info”日志消息不写入磁盘,从而节约磁盘空间。
fanout exchange只能进行无差别的广播,缺少灵活性。
本练习将使用direct exchange。direct exchange背后的路由算法很简单——消息会发送到其绑定键与消息的路由键完全匹配的队列(划重点!!)。
以下为举例说明:
上图中direct exchange 有两个队列绑定到它上面。第一个队列有一个绑定,绑定键是 orange,第二个队列有两个绑定,一个绑定键是 black,另一个是 green。
在这样一个设置中,一个带有路由键 orange 发布到交换机的消息将会被路由到队列 Q1。带有路由键 black 或 green 的消息将会发送到队列 Q2。所有其他消息将会被丢弃。
3.4 Multiple bindings
相同的绑定键完全可以同时绑定多个队列。比如在 direct exchange和 Q1 之间添加一个带有绑定键 black 的绑定。在这种情况下,direct exchange 将和fanout exchange一样,将消息广播到所有匹配的队列,带有路由键 black 的消息将被传递到 Q1 和 Q2。
3.5 代码说明
3.5.1 Sending
进入sender容器,vi编写emit_log_direct.py:
root@sender:/# vi emit_log_direct.py
root@sender:/# cat emit_log_direct.py
import pika
import sysconnection = pika.BlockingConnection(pika.ConnectionParameters(host='172.17.0.2'))
channel = connection.channel()channel.exchange_declare(exchange='direct_logs', exchange_type='direct')severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
channel.basic_publish(exchange='direct_logs', routing_key=severity, body=message)
print(f" [x] Sent {severity}:{message}")
connection.close()
root@sender:/#
这段代码是一个使用
pika
库的 Python 脚本,用于与 RabbitMQ 消息队列进行交互。它演示了如何将消息发布到一个名为direct_logs
的直接交换机(direct exchange)。下面是对代码的详细说明:
导入模块:
import pika
:导入pika
模块,这是一个用于 RabbitMQ 的 Python 客户端库。import sys
:导入sys
模块,用于访问由命令行参数传递的参数。建立连接:
connection = pika.BlockingConnection(pika.ConnectionParameters(host='172.17.0.2'))
:创建一个连接到 RabbitMQ 服务器,服务器的 IP 地址是172.17.0.2
。创建通道:
channel = connection.channel()
:创建一个新的通道(channel),用于在客户端和消息队列之间发送和接收消息。声明交换:
channel.exchange_declare(exchange='direct_logs', exchange_type='direct')
:声明一个名为direct_logs
的直接交换。直接交换根据消息的路由键(routing key)将消息路由到一个或多个绑定的队列。获取命令行参数:
severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
:获取第一个命令行参数作为消息的严重性级别(severity),如果没有提供,则默认为'info'
。message = ' '.join(sys.argv[2:]) or 'Hello World!'
:获取第二个及之后的命令行参数,并将其连接成一个字符串作为消息内容,如果没有提供,则默认为'Hello World!'
。发布消息:
channel.basic_publish(exchange='direct_logs', routing_key=severity, body=message)
:使用basic_publish
方法发布消息到direct_logs
交换。消息的路由键设置为severity
,消息体设置为message
。打印确认信息:
print(f" [x] Sent {severity}:{message}")
:打印一条消息,确认消息已经发送,并显示发送的严重性级别和消息内容。关闭连接:
connection.close()
:关闭与 RabbitMQ 的连接。这个脚本通常作为消息生产者使用,它将消息发送到消息队列中,然后由消费者根据路由键从相应的队列中接收并处理这些消息。
3.5.2 Receiving
进入receiver/receiver2容器,vi编写receive_logs_direct.py:
root@receiver:/# vi receive_logs_direct.py
root@receiver:/# cat receive_logs_direct.py
import pika
import sysconnection = pika.BlockingConnection(pika.ConnectionParameters(host='172.17.0.2'))
channel = connection.channel()channel.exchange_declare(exchange='direct_logs', exchange_type='direct')result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queueseverities = sys.argv[1:]
if not severities:sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])sys.exit(1)for severity in severities:channel.queue_bind(exchange='direct_logs', queue=queue_name, routing_key=severity)print(' [*] Waiting for logs. To exit press CTRL+C')def callback(ch, method, properties, body):print(f" [x] {method.routing_key}:{body}")channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True)channel.start_consuming()
root@receiver:/#
这段代码是一个 Python 脚本,使用
pika
库来与 RabbitMQ 消息队列进行交互。它演示了如何创建一个消费者,该消费者订阅了具有不同严重性级别的日志消息。下面是对代码的详细说明:
导入模块:
import pika
:导入pika
模块,这是用于 RabbitMQ 的 Python 客户端库。import sys
:导入sys
模块,用于访问命令行参数。建立连接:
connection = pika.BlockingConnection(pika.ConnectionParameters(host='172.17.0.2'))
:创建一个连接到 RabbitMQ 服务器,服务器的 IP 地址是172.17.0.2
。创建通道:
channel = connection.channel()
:创建一个新的通道,用于在客户端和消息队列之间发送和接收消息。声明交换:
channel.exchange_declare(exchange='direct_logs', exchange_type='direct')
:声明一个名为direct_logs
的直接交换,用于根据消息的路由键将消息路由到队列。声明队列:
result = channel.queue_declare(queue='', exclusive=True)
:声明一个非持久的、独占的队列。exclusive=True
意味着这个队列只对连接它的用户可用,并且在连接关闭时队列将被删除。queue_name = result.method.queue
:从声明的结果中获取队列名称。获取命令行参数作为严重性级别:
severities = sys.argv[1:]
:获取所有命令行参数(除了脚本名称),这些参数代表不同的日志严重性级别。if not severities:
:如果没有提供严重性级别,打印用法信息并退出程序。绑定队列到交换:
for severity in severities:
:遍历所有提供的严重性级别。channel.queue_bind(...)
:为每个严重性级别创建一个绑定,将队列绑定到direct_logs
交换,使用相应的严重性级别作为路由键。等待日志消息:
print(' [*] Waiting for logs. To exit press CTRL+C')
:打印一条消息,告知用户程序正在等待日志消息,并说明如何退出程序。定义回调函数:
def callback(ch, method, properties, body):
:定义一个回调函数,当消息到达时会被调用。print(f" [x] {method.routing_key}:{body}")
:在控制台打印出消息的路由键和消息体。设置消息消费:
channel.basic_consume(...)
:设置消息消费,指定队列名称、回调函数和自动确认消息。auto_ack=True
:设置为自动确认消息,一旦消息被传递给回调函数,RabbitMQ 就会认为消息已被处理。开始消费:
channel.start_consuming()
:启动消息消费过程,直到程序被外部中断(如用户按下 CTRL+C)。这个脚本通常作为消息消费者使用,它订阅了具有特定严重性级别的日志消息,并将这些消息打印到控制台。通过命令行参数指定感兴趣的日志级别,可以灵活地选择接收哪些类型的日志消息。
3.6 开始测试
1、保存特定级别日志到文件
- 进入receiver容器
- 执行以下命令以仅保存'warning'和'error'级别的日志到文件
logs_from_rabbit.log
:
root@receiver:/# python3 receive_logs_direct.py warning error > logs_from_rabbit.log
2. 在屏幕上显示所有日志消息
- 进入receiver2容器
- 执行以下命令以在屏幕上显示所有级别的日志消息:
root@receiver2:/# python3 receive_logs_direct.py info warning error[*] Waiting for logs. To exit press CTRL+C
3、在rabbitmq容器上查看队列绑定信息
root@48a7ed758af4:/# rabbitmqctl list_bindings
Listing bindings for vhost /...
source_name source_kind destination_name destination_kind routing_key argumentsexchange amq.gen-_ZUcAq9DRQigdae9-eBtCQ queue amq.gen-_ZUcAq9DRQigdae9-eBtCQ []exchange amq.gen-xNR7HxvupGNgQN0pdDdZ_w queue amq.gen-xNR7HxvupGNgQN0pdDdZ_w []
direct_logs exchange amq.gen-_ZUcAq9DRQigdae9-eBtCQ queue error []
direct_logs exchange amq.gen-xNR7HxvupGNgQN0pdDdZ_w queue error []
direct_logs exchange amq.gen-xNR7HxvupGNgQN0pdDdZ_w queue info []
direct_logs exchange amq.gen-_ZUcAq9DRQigdae9-eBtCQ queue warning []
direct_logs exchange amq.gen-xNR7HxvupGNgQN0pdDdZ_w queue warning []
root@48a7ed758af4:/#
4. 发送错误日志消息
- 进入sender容器。
- 执行以下命令以发送一条错误级别的日志消息:
root@sender:/# python3 emit_log_direct.py error "Run. Run. Or it will explode."[x] Sent error:Run. Run. Or it will explode.
root@sender:/# python3 emit_log_direct.py info "This is info log"[x] Sent info:This is info log
root@sender:/# python3 emit_log_direct.py warning "This is warning log"[x] Sent warning:This is warning log
root@sender:/#
- 观察发送命令的终端,确认输出显示消息已发送。
5. 验证日志接收
- 查看receiver容器,验证文件
logs_from_rabbit.log
是否已创建,并且只包含'warning'和'error'级别的日志条目。
root@receiver:/# cat logs_from_rabbit.log [*] Waiting for logs. To exit press CTRL+C[x] error:b'Run. Run. Or it will explode.'[x] warning:b'This is warning log'
root@receiver:/#
- 查看receiver2容器,检查是否接收到了步骤3中发送的错误日志消息,并显示在屏幕上。
root@receiver2:/# python3 receive_logs_direct.py info warning error[*] Waiting for logs. To exit press CTRL+C[x] error:b'Run. Run. Or it will explode.'[x] info:b'This is info log'[x] warning:b'This is warning log'
4、Wireshark抓包
4.1 抓包方式
参考:《RabbitMQ练习(Hello World)》
4.2 抓包信息
4.2.1 消费者开始接收消息
典型数据包:
1、bindings —— Frame 20
Packet comments
Frame 20: 136 bytes on wire (1088 bits), 136 bytes captured (1088 bits) on interface docker0, id 0
Ethernet II, Src: 02:42:ac:11:00:04 (02:42:ac:11:00:04), Dst: 02:42:ac:11:00:02 (02:42:ac:11:00:02)
Internet Protocol Version 4, Src: 172.17.0.4 (172.17.0.4), Dst: 172.17.0.2 (172.17.0.2)
Transmission Control Protocol, Src Port: 34112, Dst Port: 5672, Seq: 415, Ack: 634, Len: 70
Advanced Message Queuing ProtocolType: Method (1)Channel: 1Length: 62Class: Queue (50)Method: Bind (20)ArgumentsTicket: 0Queue: amq.gen-b1hFBxHkj-Ee4gbS3ZmIZwExchange: direct_logsRouting-Key: warning.... ...0 = Nowait: FalseArguments
这段文本描述的是一个 AMQP (Advanced Message Queuing Protocol) 协议的帧,具体是关于队列绑定的一个方法调用。以下是对这段文本的详细解读:
AMQP 类型:
Type: Method (1)
: 这表明帧是一个方法调用,用于执行AMQP协议中定义的操作。通道号:
Channel: 1
: 这个帧在AMQP的会话中的通道号是1。长度:
Length: 62
: 帧的总长度是62字节,包括方法帧头和参数。AMQP 类:
Class: Queue (50)
: 这表明帧属于队列类,队列类用于处理队列相关的操作。方法:
Method: Bind (20)
: 这是bind
方法,用于将队列绑定到一个交换机上,这样交换机就可以将消息路由到这个队列。参数:
Ticket: 0
: 访问权限的票据,0通常表示使用默认的票据。Queue: amq.gen-b1hFBxHkj-Ee4gbS3ZmIZw
: 要绑定的队列名称。Exchange: direct_logs
: 队列要绑定到的交换机名称。Routing-Key: warning
: 路由键(绑定键),用于指定当消息发送到交换机时,如何根据这个键将消息路由到队列。在这个例子中,路由键是warning
,意味着只有带有warning
路由键的消息会被路由到这个队列。选项标志:
Nowait: False
: 表示客户端期望服务器对这个请求做出响应。如果设置为True
,则客户端不期望响应。其他参数:
Arguments
: 这通常包含额外的参数,但在这段文本中没有提供具体的参数值。这个
bind
方法调用的目的是将一个队列绑定到一个直接交换机上,并且指定了路由键为warning
。这意味着任何发送到direct_logs
交换机并带有warning
路由键的消息都会被路由到amq.gen-b1hFBxHkj-Ee4gbS3ZmIZw
队列。这是一种常见的消息路由机制,用于确保消息根据特定的业务逻辑或条件被正确地分发到不同的队列。
2、开始接收消息 —— Frame 24
Frame 24: 155 bytes on wire (1240 bits), 155 bytes captured (1240 bits) on interface docker0, id 0
Ethernet II, Src: 02:42:ac:11:00:04 (02:42:ac:11:00:04), Dst: 02:42:ac:11:00:02 (02:42:ac:11:00:02)
Internet Protocol Version 4, Src: 172.17.0.4 (172.17.0.4), Dst: 172.17.0.2 (172.17.0.2)
Transmission Control Protocol, Src Port: 34112, Dst Port: 5672, Seq: 553, Ack: 658, Len: 89
Advanced Message Queuing ProtocolType: Method (1)Channel: 1Length: 81Class: Basic (60)Method: Consume (20)ArgumentsTicket: 0Queue: amq.gen-b1hFBxHkj-Ee4gbS3ZmIZwConsumer-Tag: ctag1.711eb50abcc64a798dbf5d6ae61cb296.... ...0 = No-Local: False.... ..1. = No-Ack: True.... .0.. = Exclusive: False.... 0... = Nowait: FalseFilter
这段文本描述的是一个 AMQP (Advanced Message Queuing Protocol) 协议的帧,具体是关于消息消费请求的一个方法调用。以下是对这段文本的详细解读:
AMQP 类型:
Type: Method (1)
: 这表明帧是一个方法调用,用于执行AMQP协议中定义的操作。通道号:
Channel: 1
: 这个帧在AMQP会话中的通道号是1。长度:
Length: 81
: 帧的总长度是81字节,包括方法帧头和参数。AMQP 类:
Class: Basic (60)
: 这表明帧属于基础类,基础类用于处理消息的发布和订阅。方法:
Method: Consume (20)
: 这是consume
方法,用于建立消费者,以便从队列中接收消息。参数:
Ticket: 0
: 访问权限的票据,0通常表示使用默认的票据。Queue: amq.gen-b1hFBxHkj-Ee4gbS3ZmIZw
: 消费者要订阅的队列名称。Consumer-Tag: ctag1.711eb50abcc64a798dbf5d6ae61cb296
: 消费者的唯一标识符。选项标志:
No-Local: False
: 表示消费者可以接收到发送者自己发送的消息。No-Ack: True
: 表示消费者接收到消息后不需要发送确认。这通常用于性能优化,但需要注意,如果消费者异常终止,未确认的消息可能会丢失。Exclusive: False
: 表示队列可以被多个消费者共享。Nowait: False
: 表示客户端期望服务器对这个请求做出响应。如果设置为True
,则客户端不期望响应。Filter:
- 这个参数可能表示一个消息筛选器,用于决定哪些消息应该被消费者接收。然而,这段文本中的
Filter
后面没有提供具体的参数值,可能是信息被截断了。这个
consume
方法调用的目的是让消费者订阅一个队列,并开始接收消息。由于No-Ack
被设置为True
,消费者在接收到消息后不需要发送回执,这可以提高消息处理的吞吐量,但牺牲了消息的可靠性保证。如果对消息的可靠性有要求,应该将No-Ack
设置为False
,这样消费者在处理完每条消息后都需要发送一个确认回执给服务器。
4.2.2 生产者发送消息
典型数据包:
1、消息发布到交换机 —— Frame 83
Frame 83: 99 bytes on wire (792 bits), 99 bytes captured (792 bits) on interface docker0, id 0
Ethernet II, Src: 02:42:ac:11:00:03 (02:42:ac:11:00:03), Dst: 02:42:ac:11:00:02 (02:42:ac:11:00:02)
Internet Protocol Version 4, Src: 172.17.0.3 (172.17.0.3), Dst: 172.17.0.2 (172.17.0.2)
Transmission Control Protocol, Src Port: 46386, Dst Port: 5672, Seq: 395, Ack: 583, Len: 33
Advanced Message Queuing ProtocolType: Method (1)Channel: 1Length: 25Class: Basic (60)Method: Publish (40)ArgumentsTicket: 0Exchange: direct_logsRouting-Key: error.... ...0 = Mandatory: False.... ..0. = Immediate: False
这段文本描述的是一个 AMQP (Advanced Message Queuing Protocol) 协议的帧,具体是关于消息发布请求的一个方法调用。以下是对这段文本的详细解读:
AMQP 类型:
Type: Method (1)
: 这表明帧是一个方法调用,用于执行AMQP协议中定义的操作。通道号:
Channel: 1
: 这个帧在AMQP会话中的通道号是1。长度:
Length: 25
: 帧的总长度是25字节,包括方法帧头和参数。AMQP 类:
Class: Basic (60)
: 这表明帧属于基础类,基础类用于处理消息的发布和订阅。方法:
Method: Publish (40)
: 这是publish
方法,用于将消息发布到一个交换机。参数:
Ticket: 0
: 访问权限的票据,0通常表示使用默认的票据。Exchange: direct_logs
: 消息要发布的交换机名称。在这个例子中,交换机的类型是直接交换机(direct exchange),它根据路由键将消息路由到一个或多个队列。Routing-Key: error
: 路由键,用于指定消息的路由。在这个例子中,路由键是error
,意味着消息将被路由到绑定了error
路由键的队列。选项标志:
Mandatory: False
: 表示如果消息不能被路由到任何队列,服务器不会返回一个否定的确认(通常是一个返回码)。如果设置为True
,则在没有队列绑定到指定的路由键时,服务器会返回一个否定的确认。Immediate: False
: 表示消息应该被路由到队列,即使没有消费者正在监听。如果设置为True
,则只有当队列上至少有一个活跃的消费者时,消息才会被投递。这个
publish
方法调用的目的是将消息发布到direct_logs
交换机,并指定路由键为error
。消息将根据这个路由键被路由到一个或多个绑定了相应路由键的队列。由于Mandatory
和Immediate
都被设置为False
,这表明发布者对消息的路由和队列上是否有消费者不作特别要求。
2、发送的具体消息 —— Frame 85
Frame 85: 103 bytes on wire (824 bits), 103 bytes captured (824 bits) on interface docker0, id 0
Ethernet II, Src: 02:42:ac:11:00:03 (02:42:ac:11:00:03), Dst: 02:42:ac:11:00:02 (02:42:ac:11:00:02)
Internet Protocol Version 4, Src: 172.17.0.3 (172.17.0.3), Dst: 172.17.0.2 (172.17.0.2)
Transmission Control Protocol, Src Port: 46386, Dst Port: 5672, Seq: 450, Ack: 583, Len: 37
Advanced Message Queuing ProtocolType: Content body (3)Channel: 1Length: 29Payload: 52756e2e2052756e2e204f722069742077696c6c206578706c6f64652e
这段文本描述的是 AMQP (Advanced Message Queuing Protocol) 协议中的一个内容体帧(Content body frame)。以下是对这段文本的详细解读:
AMQP 类型:
Type: Content body (3)
: 这表明帧是消息内容体的一部分,用于传输消息的实际数据。通道号:
Channel: 1
: 这个帧使用的是通道号1,与之前的Method帧相同,表示这是同一会话的一部分。长度:
Length: 29
: 帧的总长度是29字节,包括帧头和有效载荷。有效载荷(Payload):
Payload: 52756e2e2052756e2e204f722069742077696c6c206578706c6f64652e
: 这是消息内容的二进制表示,通常以十六进制格式显示。在这个例子中,它看起来像是一个字符串,但以十六进制编码。内容体帧是AMQP协议中消息发布机制的一部分。当客户端使用
Publish
方法将消息发送到交换机时,消息内容会通过一个或多个内容体帧进行传输。这些帧会按照顺序发送,并且在接收端重新组装成完整的消息。在这个例子中,消息内容是一个警告或指示性的文本,可能是一个日志消息或某种形式的状态更新。消息通过AMQP协议的通道1发送,并且由于使用了内容体帧,它可能是一个多帧消息的一部分。如果消息较长,它可能会分布在多个内容体帧中。
4.2.3 RabbitMQ 向消费者发送消息
Frame 87: 203 bytes on wire (1624 bits), 203 bytes captured (1624 bits) on interface docker0, id 0
Ethernet II, Src: 02:42:ac:11:00:02 (02:42:ac:11:00:02), Dst: 02:42:ac:11:00:05 (02:42:ac:11:00:05)
Internet Protocol Version 4, Src: 172.17.0.2 (172.17.0.2), Dst: 172.17.0.5 (172.17.0.5)
Transmission Control Protocol, Src Port: 5672, Dst Port: 38582, Seq: 721, Ack: 709, Len: 137
Advanced Message Queuing ProtocolType: Method (1)Channel: 1Length: 70Class: Basic (60)Method: Deliver (60)ArgumentsConsumer-Tag: ctag1.da86a92a85bc4dc48ea8bb0bbb143580Delivery-Tag: 1.... ...0 = Redelivered: FalseExchange: direct_logsRouting-Key: error
Advanced Message Queuing ProtocolType: Content header (2)Channel: 1Length: 14Class ID: Basic (60)Weight: 0Body size: 29Property flags: 0x0000Properties
Advanced Message Queuing ProtocolType: Content body (3)Channel: 1Length: 29Payload: 52756e2e2052756e2e204f722069742077696c6c206578706c6f64652e
这段文本描述了通过 AMQP 协议传输的一系列消息帧,具体包括方法帧、内容头帧和内容体帧。以下是对这些帧的详细解读:
AMQP Method Frame:
Type: Method (1)
: 表示这是一个方法帧,用于执行AMQP协议中定义的操作。Channel: 1
: 帧使用的通道号为1。Length: 70
: 方法帧的总长度为70字节。Class: Basic (60)
: 属于AMQP的基础类,用于消息的发布和订阅。Method: Deliver (60)
: 这是一个deliver
方法,用于将消息传递给消费者。Deliver Method Arguments:
Consumer-Tag: ctag1.da86a92a85bc4dc48ea8bb0bbb143580
: 消费者的唯一标识符。Delivery-Tag: 1
: 消息的唯一标识符,通常是一个序列号。Redelivered: False
: 表示这条消息不是重新投递的。Exchange: direct_logs
: 消息通过的交换机名称。Routing-Key: error
: 消息的路由键,用于将消息路由到正确的队列。AMQP Content Header Frame:
Type: Content header (2)
: 表示这是一个内容头帧,包含了消息的元数据。Channel: 1
: 使用的通道号为1。Length: 14
: 内容头帧的长度为14字节。Class ID: Basic (60)
: 消息的类别ID,这里是基础类。Weight: 0
: 消息的优先级权重,这里设置为0。Body size: 29
: 消息体的大小为29字节。Property flags: 0x0000
: 属性标志,这里没有设置任何属性。AMQP Content Body Frame:
Type: Content body (3)
: 表示这是一个内容体帧,包含了消息的实际数据。Channel: 1
: 使用的通道号为1。Length: 29
: 内容体帧的长度为29字节。Payload: 52756e2e2052756e2e204f722069742077696c6c206578706c6f64652e
: 消息内容的二进制数据,以十六进制表示。消息内容解码:
- 将十六进制的
Payload
解码为字符串,我们得到:"
Run. Run. Or it will explode."
这是消息的实际内容,看起来像是一个警告或指示性的消息。总结来说,这些帧表示通过AMQP协议发送给了一个消费者(由
Consumer-Tag
标识)一条消息,这条消息最初是发布到direct_logs
交换机上,并使用error
路由键路由。消息的内容是一个文本字符串,被编码并包含在内容体帧中。