我的上一篇RabbitMQ初识文章:RabbitMQ实战之初识篇
RabbitMQ官方讲解教程(推荐学习):RabbitMQ Tutorials
RabbitMQ-Tutorials官方GitHub代码仓库(多种语言均有):rabbitmq-tutorials
=》RabbitMQ常用的几种Queue(消息队列)教程
我的实验是基于 [ JDK1.8 + Maven + IDEA ],所以基于Maven的基础上以下队列项目的workspace需要引入如下的 pom.xml
中的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx</groupId>
<artifactId>RabbitMQ</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- configuration配置跟JDK版本相关联 -->
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- rabbitmq客户端依赖 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.1</version>
</dependency>
<!-- rabbitmq客户端依赖(上面的)依赖于slf4j依赖 -->
<!-- 但在生产环境中(线上)应该引入更加成熟的日志依赖库,如Logback日志库 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.26</version>
</dependency>
</dependencies>
</project>
一、 Hello World
P:Producer - 生产者:生产msg并发布到队列(queue)中 ;
C:Consumer - 消费者:保持运行 && 监听队列 中的msg,拉取msg并消费它 ;
- 生产者发送msg到mq的代码:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Send {
private static final String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
// mq的连接工厂
ConnectionFactory factory = new ConnectionFactory();
// mq的服务器地址,这里是本机IP 127.0.0.1
// 也可以设置其他信息:端口port、用户名、密码passwd等
factory.setHost("localhost");
// 从factory中获取连接 + 在连接中创建管道 channel
// channel: 完成工作的大多数API所在的位置
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 在管道中声明连接的队列 queue
// Declaring a queue is idempotent[声明队列是幂等的,仅当队列不存在时才创建]
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "HELLO MQ-queue, i am hengx's Producer";
// 发布(Publish) msg 到 queue
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("[x] Send '" + message + "'");
}
}
}
- 消费者从mq中拉取并消费msg的代码:
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// TODO
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
// 从队列(queue)监听、拉取并消费(Consume) msg
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {
});
}
}
运行生产者后管理界面查看:
点击队列的名字即可查看详细的信息:
1) 为什么不使用try语句自动关闭管道Channel跟连接Connection呢?(即调用chanel.close();connection.close();
)
如果这样,我们的程序将继续运行,然而关闭了所有内容 (管道 + 连接) 并退出!这将很尴尬,因为我们希望在消费者 异步侦听消息到达 时,该过程保持有效。
2) queueDeclare() 参数解析:
我的下一篇博文:channel.queueDeclare()参数解析
二、 Work queues
一个生产者,多个消费者 ;
一个msg只能被一个消费者消费 ;
msg在消费者之间是共享的 ;
1. 目的:
To avoid doing a resource-intensive(资源密集型) task immediately and having to wait for it to complete. Instead we schedule the task to be done later. [ 就是避免立即处理资源密集型的任务而是在后期处理它 ,异步处理达到削峰的目的],比如在一个HTTP短连接中要处理一个长时间的任务是不可能的,特别是在Web应用中.
- 生产者代码:
public class Send_WorkQueue {
private static final String QUEUE_NAME = "work-queue";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
args = new String[]{
"Java",".",".","."};
String message = String.join(" ",args);
// 发送20条消息到 mq
for (int i=0;i<20;i++){
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
}
System.out.println("[x] Send '" + message + "'");
}
}
}
- 消费者-1 代码:
public class Recv1_WorkQueue {
private final static String QUEUE_NAME = "work-queue";
private static int count = 0;
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x1] Received '" + message + "'");
try {
// 模拟耗时工作
doWork(message);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("[x1] Done,count : " + (++count));
}
};
// false关闭自动确认ack
boolean autoAck = true; // acknowledgment is covered below
// 监听队列,false表示手动返回完成状态,true表示自动
channel.basicConsume(QUEUE_NAME, autoAck, deliverCallback, consumerTag -> {
});
}