
本文介绍了RabbitMQ的Java客户端实现,包含生产者和消费者代码示例。生产者通过建立连接、创建信道、声明队列,循环发送10条消息到"hello"队列;消费者同样建立连接后订阅该队列,通过DefaultConsumer接收并打印消息。文章重点解析了RabbitMQ的两个核心元数据:Envelope(包含投递标签、重投标志、交换机和路由键信息)和AMQP.BasicProperties(消息属性头,如内容类型、优先级等),详细说明了各参数含义及实际应用场景。这些元数据对消息路由、业务追踪和RPC通信等高级功能至关重要。
⽣产者代码
java
package rabbitmq.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ProducerDemo {
public static void main(String[] args) throws IOException, TimeoutException {
//1. 建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("自己的服务器地址");
connectionFactory.setPort(5672); //需要提前开放端口号
connectionFactory.setUsername("账号名字");//账号
connectionFactory.setPassword("密码"); //密码
connectionFactory.setVirtualHost("主机名"); //虚拟主机
Connection connection = connectionFactory.newConnection();
//2. 开启信道
Channel channel = connection.createChannel();
//3. 声明交换机 使用内置的交换机
//4. 声明队列
/**
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
* Map<String, Object> arguments)
* 参数说明:
* queue: 队列名称
* durable: 可持久化
* exclusive: 是否独占
* autoDelete: 是否自动删除
* arguments: 参数
*/
channel.queueDeclare("hello", true, false, false, null);
//5. 发送消息
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* 参数说明:
* exchange: 交换机名称
* routingKey: 内置交换机, routingkey和队列名称保持一致
* props: 属性配置
* body: 消息
*/
for (int i = 0; i < 10; i++) {
String msg = "hello rabbitmq~"+i;
channel.basicPublish("","hello", null, msg.getBytes());
}
System.out.println("消息发送成功~");
//6. 资源释放
channel.close();
connection.close();
}
}
消费者代码
java
package rabbitmq.simple;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConsumerDemo {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//1. 创建连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("自己的服务器地址");
connectionFactory.setPort(5672); //需要提前开放端口号
connectionFactory.setUsername("账号名字");//账号
connectionFactory.setPassword("密码"); //密码
connectionFactory.setVirtualHost("主机名"); //虚拟主机
Connection connection = connectionFactory.newConnection();
//2. 创建Channel
Channel channel = connection.createChannel();
//3. 声明队列(可以省略)
channel.queueDeclare("hello",true, false, false, null);
//4. 消费消息
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* 参数说明:
* queue: 队列名称
* autoAck: 是否自动确认
* callback: 接收到消息后, 执行的逻辑
*/
DefaultConsumer consumer = new DefaultConsumer(channel){
//从队列中收到消息, 就会执行的方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收到消息:"+ new String(body));
// System.out.println("接收到消息:"+ consumerTag);
// System.out.println("接收到消息:"+ (String.valueOf(envelope)));
// System.out.println("接收到消息:"+ properties );
}
};
channel.basicConsume("hello", true, consumer);
//等待程序执行完成
Thread.sleep(2000);
//5. 释放资源
channel.close();
connection.close();
}
}

接收到消息:hello rabbitmq~9
接收到消息:amq.ctag-eK9h2CCuGOsETN7QBqO2xw
接收到消息:Envelope(deliveryTag=10, redeliver=false, exchange=, routingKey=hello)
接收到消息:#contentHeader<basic>(content-type=null, content-encoding=null, headers=null, delivery-mode=null, priority=null, correlation-id=null, reply-to=null, expiration=null, message-id=null, timestamp=null, type=null, user-id=null, app-id=null, cluster-id=null)
RabbitMQ 消息元数据详解
本文档详细介绍了 RabbitMQ 中两个核心的元数据对象:Envelope(物流信息)和 AMQP.BasicProperties(消息属性头)。
一、Envelope(信封)对象
Envelope 包含了 RabbitMQ 消息的"物流信息",用于追踪消息的投递状态和路径。
|-----------------|---------|------------------------------------------------------------------------------------------------------|
| 参数 | 值 | 含义解释 |
| deliveryTag | 10 | 消息编号 这是第 10 条投递给你的消息。就像快递单号,用于确认收货(ACK)。 |
| redeliver | false | 是否重投 false = 这是第一次投递给你 true = 这条消息之前给过别的消费者(或之前给你但你没确认),现在重新投递 |
| exchange | ""(空) | 交换机名称 空字符串表示使用的是 内置默认交换机 (AMQP default)。生产者代码里 basicPublish("", "hello", ...) 第一个参数就是空字符串。 |
| routingKey | hello | 路由键 决定消息要送到哪个队列。在简单模式下,routingKey 通常等于队列名(hello)。就像写地址:"送到 hello 队列"。 |
🔍 详细解读
1. deliveryTag = 10
-
作用:RabbitMQ 给这条消息打的序号(从 1 开始累加)。
-
应用场景 :当你使用 手动确认 (
autoAck=false)时,需要用这个编号告诉服务器:"第 10 条消息我处理完了,可以删了"。
java
// 手动确认示例
channel.basicAck(envelope.getDeliveryTag(), false);
2. redeliver = false
-
意味着什么:这条消息是"新鲜"的,第一次发给你。
-
如果为 true:说明这条消息被"退件重发"了。可能原因:
-
之前的消费者处理超时/崩溃,没有发送确认(ACK)。
-
RabbitMQ 把消息重新发给其他消费者(或再次发给你)。
-
3. exchange = ""
-
""(空字符串)表示 默认交换机(Default Exchange)。 -
这是 RabbitMQ 内置的直连交换机(direct exchange)。
-
规则 :
routingKey必须完全匹配队列名才能路由。
4. routingKey = hello
-
路由键:决定消息走哪条路。
-
在默认交换机下,
routingKey= 队列名。 -
生产者代码里
basicPublish("", "hello", ...)的第二个参数"hello"就是routingKey。
💡 一句话总结
Envelope 就是消息的"快递面单",告诉你:这是第 10 个包裹(deliveryTag=10),是第一次派送(redeliver=false),从默认站点发出(exchange=),目的地是 hello 队列(routingKey=hello)。而真正的"包裹内容"在 body 字节数组里!
二、AMQP.BasicProperties(消息属性头)
AMQP.BasicProperties 包含了消息的元数据信息(关于消息的数据,而非消息本身内容)。
如果在发送消息时第 3 个参数传了 null:
java
channel.basicPublish("", "hello", null, msg.getBytes());
那么这些属性都会是空的(null)。
📋 每个参数详解
|----------------------|---------|---------------|---------------------------------------------------|
| 参数名 | 当前值 | 含义 | 实际应用场景 |
| content-type | null | 内容类型(MIME类型) | 如 application/json、text/plain。消费者收到后知道用什么格式解析 |
| content-encoding | null | 内容编码 | 如 gzip(压缩)、UTF-8。告诉消费者如何解码 |
| headers | null | 自定义头信息(Map类型) | 存自定义键值对,用于业务路由或过滤 |
| delivery-mode | null | 投递模式 | 1=非持久化(重启丢失),2=持久化(存磁盘,重启还在) |
| priority | null | 优先级 | 数字越大越优先(0-9)。VIP客户的消息插队处理 |
| correlation-id | null | 关联ID | 用于 RPC 模式。客户端发请求时带个唯一ID,服务端回复时带同样ID |
| reply-to | null | 回复队列名 | RPC场景中,告诉服务端:"处理完后把结果发到这个队列" |
| expiration | null | 过期时间(毫秒) | 如 60000 表示 1 分钟后若还没被消费,自动删除 |
| message-id | null | 消息唯一ID | 业务层面的消息编号,用于幂等性(防止重复消费) |
| timestamp | null | 时间戳 | 消息发送时间,用于日志追踪或延时计算 |
| type | null | 消息类型 | 业务类型标识,如 order.created、user.registered |
| user-id | null | 发送者ID | 验证消息发送者的身份 |
| app-id | null | 应用ID | 标识哪个应用发送的,如 order-service |
| cluster-id | null | 集群ID | 已废弃,基本不用 |
💡 实际使用示例
场景 1:发送 JSON 数据 + 自定义业务头
java
// 生产者
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.contentType("application/json") // 内容是 JSON
.deliveryMode(2) // 持久化消息(重启不丢)
.priority(5) // 优先级 5(较高)
.messageId("ORDER-20240315-001") // 业务消息ID
.expiration("300000") // 5 分钟后过期
.headers(Map.of("userId", "1001", // 自定义头
"orderType", "vip"))
.build();
channel.basicPublish("", "hello", props, jsonBytes);
场景 2:消费者读取这些属性
java
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) {
// 1. 判断内容类型
if ("application/json".equals(properties.getContentType())) {
// 按 JSON 解析
}
// 2. 获取业务 ID(幂等性校验)
String msgId = properties.getMessageId();
// 3. 获取自定义头
Map<String, Object> headers = properties.getHeaders();
if (headers != null) {
String orderType = (String) headers.get("orderType");
}
}
🎯 总结
这些属性都是消息的"标签",主要用于:
-
路由决策(headers、type)
-
消息特性(priority、expiration、delivery-mode)
-
业务追踪(message-id、correlation-id、timestamp)
-
RPC 通信(reply-to、correlation-id)
入门示例传 null 是因为简单场景不需要高级特性,但在分布式微服务系统中,这些属性至关重要!