一个生产者一个消费者
RabbitMQ支持多种协议, 本讲解使用 AMQP 协议, AMQP协议是一种开放性消息服务, RabbitMQ支持多种语言, 使用 JAVA 语言进行讲解下载客户端库即要依赖 SLF4J API 和 SLF4J Simple;
我们称其为消息发布者(发送者)Send和我们的消息消费者(接收者) Recv。发布者将连接到RabbitMQ,发送一条消息,然后退出。
注意 : 安装RabbitMQ的服务器官方建议大于200M
我们的消费者正在侦听RabbitMQ发出的消息,因此与发布消息的生产者不同,消费者将使其继续运行以侦听消息并打印出来(请注意接收和发送必须和生产者队列匹配)
添加依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
步骤 :
1. 创建连接工厂类
2. 设置RabbitMQ服务器地址
3. 设置 AMQP端口号,注意不是管控台端口号
4. 设置 vhost, 虚拟数据库,RabbitMQ默认保存到Erlang自带的Mnesia数据库中
5. 设置用户名以及密码
6. 创建连接
// 创建链接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 设置地址
connectionFactory.setHost("127.0.0.1");
// 设置 AMQP 端口号
connectionFactory.setPort(5672);
// 设置 vhost (虚拟数据库) 不指定默认 /
connectionFactory.setVirtualHost("/");
// 设置用户名
connectionFactory.setUsername("guest");
// 设置密码
connectionFactory.setPassword("guest");
Connection connection = null;
try {
// 获取 RabbitMQ 连接
connection = connectionFactory.newConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
if(connection == null){
throw new RuntimeException("创建 RabbitMQ 连接失败");
}
return connection;
步骤 :
1. 获取连接
2. 创建通道
3. 创建队列
4. 创建生产者发送消息(注意:发送消息使用 byte 形式 )
//声明队列名
private static final String HELLOWORDQUEUENAME = "hello_word_queue";
// 获取连接
Connection connection = ConnectionFactoryUtlis.getConnection();
// 创建一个通道
Channel channel = connection.createChannel();
// 创建一个队列
channel.queueDeclare(HELLOWORDQUEUENAME, false, false, false, null);
String msg = "hello_word_queue_msg";
// 创建一个生产者
channel.basicPublish("", HELLOWORDQUEUENAME, null, msg.getBytes());
System.out.println("发送消息成功 msgBody: " + msg);
channel.close();
connection.close();
创建一个队列
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map arguments)
名称 | 类型 | 说明 |
---|---|---|
queue | Sting | 生产者发送消息到哪队列名称 |
durable | Boolean | 是否持久化, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中, 当rabbitmq重启之后会读取该数据库(生产环境建议使用) |
exclusive | Boolean | 是否排外,有两个作用 1. 当连接关闭时connection.close()该队列是否会自动删除; 2.该队列是否是私有的 |
autoDelete | Boolean | 是否自动删除队列, 当最后一个消费者断开连接之后队列是否自动被删除, 可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0 时队列就会自动删除 |
arguments | Map | 队列的其他属性,设置为 null |
创建一个生产者
basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
名称 | 类型 | 说明 |
---|---|---|
exchange | String | 交换机,简单工作模式设置 “” |
routingKey | String | 路由key, 因为简单模式不存在交换机,所以此处是 队列名称 |
mandatory | Boolean | 如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue, 那么会调用basic.return方法将消息返还给生产者。false:出现上述情形broker会直接将消息扔掉 |
immediate | Boolean | 如果exchange在将消息route到queue(s)时发现对应的queue上没有消费者,那么这条消息不会放入队列中。 当与消息routeKey关联的所有queue(一个或多个)都没有消费者时,该消息会通过basic.return方法返还给生产者。 (简单模式了解即可) |
props | BasicProperties | 需要注意的是 BasicProperties.deliveryMode, 1:不持久化 2:持久化 这里指的是消息的持久化,配合channel(durable=true),queue(durable)可以实现,即使服务器宕机,消息仍然保留 |
body | byte[] | 发送的消息体 |
// 声明队列
private static final String HELLOWORDQUEUENAME = "hello_word_queue";
// 编码
private static final String UTF_8 = "utf-8";
// 获取连接
Connection connection = ConnectionFactoryUtlis.getConnection();
// 创建连接通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(HELLOWORDQUEUENAME, false, false, false, null);
// 创建 RabbitMQ 消息监听类, 监听 RPC 连接通道
DefaultConsumer consumer = new DefaultConsumer(channel) {
// 回调函数
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, UTF_8);
System.out.println("msg = " + msg);
}
};
// 开启一个消费者,监听队列,收到消息触发 handleDelivery 方法(回调)
channel.basicConsume(HELLOWORDQUEUENAME, true, consumer);
声明消费者
basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
名称 | 类型 | 说明 |
---|---|---|
queue | Stirng | 监听队列名 |
autoAck | Boolean | 是否自动应答, 如果设置会自动应答后生产者发送给消费者后将在自己的存在中删除, 如果设置会手动应答将在消费者回答生产者已经消费完消息了,生产者才会才会在内存中删除消息 ; true : 自动应答 ; 手动应答 : false |
callback | Consumer | 接收到消息时触发的回调函数 |
消息者的回调函数
handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
名称 | 类型 | 说明 |
---|---|---|
consumerTag | String | 标记消费者在队列中的标签, 此标记可在 RabbitMQ管控台查看 |
envelope | Envelope | 消息的封装体, 可以查看到队列信息 |
properties | AMQP.BasicProperties | 消息内容头数据 |
body | byte[] | 消息体 |
// 获取连接
Connection connection = ConnectionFactoryUtlis.getConnection();
// 创建连接通道
Channel channel = null;
channel = connection.createChannel();
// 队列绑定通道
QueueingConsumer consumer = new QueueingConsumer(channel);
// 老的 API 创建一个监听队列
channel.basicConsume(HELLOWORDQUEUENAME, true, "", true, true, null, null, null, null);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("message = " + message);
break;
}
声明消费者
basicConsume(String queue, boolean autoAck,
String consumerTag, boolean noLocal,
boolean exclusive, Map arguments,
DeliverCallback deliverCallback, CancelCallback cancelCallback,
ConsumerShutdownSignalCallback shutdownSignalCallback);
名称 | 类型 | 说明 |
---|---|---|
queue | Sting | 队列名称 |
autoAck | Boolean | 是否自动应答 |
exclusive | Boolean | 是否排外的,有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除;二:该队列是否是私有的 |
consumerTag | String | 连接中消费者标签,用来区分多个消费者 |
noLocal | Boolean | 设置为true,表示 不能将同一个Conenction中生产者发送的消息传递给这个Connection中 的消费者 在此通道的连接上发布的消息。API解释 :RabbitMQ服务器不支持此标记。 |
arguments | Map | 设置消费者一些参数 |
deliverCallback | DeliverCallback | 当一个消息发送过来后的回调接口 |
cancelCallback | CancelCallback | 当一个消费者取消订阅时的回调接口;取消消费者订阅队列时除了使用{@link Channel#basicCancel}之外的所有方式都会调用该回调方法 |
shutdownSignalCallback | ConsumerShutdownSignalCallback | 当channel/connection 关闭后回调 |
老API缺点
QueueingConsumer内部其实是一个LinkBlockingQueue,
它将从broker端接受到的信息先暂存到这个LinkBlockingQueue中,
然后消费端程序在从这个LinkBlockingQueue中take出消息。
试想一下,如果我们不take消息或者说take的非常慢,那么LinkBlockingQueue中的消息就会越来越多,最终造成内存溢出。
简单队列,同时可以存在多个生产、消费者, 生产的的同一个消息只能由一个消费者消费;
简单队列, 一一对应
因为简单队列无交换机 路由key 就是队列名
作者简洁
作者:大家好,我是你是高山我是峰, 专业于写这些入门到深层知识,提升我们的基本功,期待你的关注,和我一起学习
转载说明:未获得授权,禁止转载