1、消息模型概述
RabbitMQ提供几种消息模型,如下图
其中最后一种是RPC,不是MQ。此处不予讨论。下面用例子说明每种模式。
2、Demo准备工作
首先新建一个springboot项目,引入依赖。
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
为了方便操作,先写一个连接MQ的工具类
public class ConnectionUtil {
/**
* 建立与RabbitMQ的连接
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("127.0.0.1");
//端口
factory.setPort(5672);
//设置账号信息,用户名、密码、vhost
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("guest");
Connection connection = factory.newConnection();
return connection;
}
}
3、simple
官网对RabbitMQ简介:RabbitMQ是一个消息代理:它接受和转发消息。 你可以把它想象成一个邮局:当你把邮件放在邮箱里时,你可以确定邮差先生最终会把邮件发送给你的收件人。 在这个比喻中,RabbitMQ是邮政信箱,邮局和邮递员。RabbitMQ与邮局的主要区别是它不处理纸张,而是接受,存储和转发数据消息的二进制数据块。
术语
- 生产者(producer ),生产仅意味着发送。发送消息的程序是生产者:
- 队列(queue ),队列是RabbitMQ内部的邮箱的名称。尽管消息流经RabbitMQ和您的应用程序,但它们只能存储在队列中。队列仅由主机的存储器&磁盘限制约束,它本质上是一个大的消息缓冲器。许多生产者可以将消息发送到一个队列,许多消费者可以尝试从一个队列接收数据。这就是我们表示队列的方式:
- 消费者(consumer ),消费与接收具有相似的含义。一个消费者是一个程序,主要是等待接收信息:
最终
生产者生产(发送)消息:
/**
* 生产者
*/
public class Send {
private final static String QUEUE_NAME = "simple_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接
Connection connection = ConnectionUtil.getConnection();
// 从连接中创建通道,使用通道才能完成消息相关的操作
Channel channel = connection.createChannel();
// 声明(创建)队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 消息内容
String message = "Hello World!";
// 向指定的队列中发送消息
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
//关闭通道和连接
channel.close();
connection.close();
}
}
执行send.main方法,打开RabbitMQ控制台Queues标签页可以看到队列里已经有这条消息了。
消费者消费(接收)消息
public class Recv {
private final static String QUEUE_NAME = "simple_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接
Connection connection = ConnectionUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 定义队列的消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
// 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
// body 即消息体
String msg = new String(body);
System.out.println(" [x] received : " + msg + "!");
}
};
// 监听队列,第二个参数:是否自动进行消息确认。
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
运行消费者
发现已经接收到消息了,再次查看控制台也没消息了。但是程序依旧没有停止,消费者依然在监听队列中是否有新的消息。一旦有新的消息进入队列,就会立即接收(消费)。
4、消息确认机制(ACK)
如果消息到了消费者那没有执行,消费者就宕机了。或者消费者在执行的时候有异常等情况,RabbitMQ就需要知道是否消费正常消费了消息。
因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:
- 自动ACK:消息一旦被接收,消费者自动发送ACK(消息不重要时,丢失没什么影响)
- 手动ACK:消息接收后,不会发送ACK,需要手动调用(消息重要时)
上面那个例子是自动ACK,下面写一个手动ACK例子。
public class Recv {
private final static String QUEUE_NAME = "simple_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接
Connection connection = ConnectionUtil.getConnection();
// 创建通道
final Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 定义队列的消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
// 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
// body 即消息体
String msg = new String(body);
System.out.println(" [x] received : " + msg + "!");
// 手动进行ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列,第二个参数false,手动进行ACK
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
如何演示两者的区别呢?在代码里故意添加的一个异常。 在两个消费者中加入
int i =1/0;
先运行生产者,再运行自动ACK消费者可以查看控制台发现消息被消费了(虽然消费者出事了)。
关闭自动ACK的消费者,再次运行生产者,运行手动ACK的消费者如图
虽然依旧打印了消息但是消息没有被消费,查看控制台可见。消息依旧还在,虽然程序出事了,但是消息没有丢失。ACK也算是消息持久化的手段之一。
乏了,后面会继续介绍其他种类的消息机制。
来源:oschina
链接:https://my.oschina.net/u/4303989/blog/4461009