前言: 这篇文章主要讲述如何使用RabbitMQ创建消息应用程序 。 RabbitMQ是一个消息代理,它接受和转发消息。官网建议把它想象成邮局:当你写好收件人并把要邮寄的邮件放在一个邮箱里后,邮局会搜集邮件,并把邮件交给你的收件人。在这个类比中,RabbitMQ是一个邮箱也是一个邮局,同时也是一个邮递员,RabbitMQ和邮局的主要区别在于,它不处理实体邮件,而是接受、存储和转发二进制数据信息
RabitMQ文章目录
9-RabbitMQ教程发布确认PublishConfirm
图例
P(生产者): 生产者属于数据的发送方 ,发送的消息被放入队列里
C(消费者): 消费者属于数据的接收方,发现队列里的消息,将消息从队列中读取出来做业务操作
Queue(队列): RabbitMQ的作用是存储消息,队列的特性是先进先出
第一个应用程序
本示例是一个最简单的示例, 实现 一个发送单一信息的生产者发送消息到RabbitMQ中的队列里,一个接收信息并打印出消息信息,示意图如下:
RabbitMQ讲多种协议。本教程使用AMQP 0-9-1,需要引用amqp-client
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
生产者 发送消息参考代码
public class Producer {
public final static String QUEUE_NAME = "rabbitMQ.test";
public static void main(String[] args) throws IOException, TimeoutException {
//使用工厂创建1个连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); //设置RabbitMQ 服务器地址
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
try {
//创建1个通道,声明要关注的队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送消息
String message = "Hello world ";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
System.out.println("Producer Send +'" + message + "'");
} finally {
//关闭通道和连接
channel.close();
connection.close();
}
}
}
运行后执行监控http://localhost:15672/可以看到声明的消息队列
1. Channel
Connection可以用来创建多个Channel实例,Channel与线程一一对应Channel实例不能在线程间共享:使用Channel的场景通常为在客户端每个线程使用一个独立的Channel实例来进行数据传输,这样就实现了不同线程之间的隔离。不过由于所有线程都共用一个TCP连接进行数据传输,如果传输的数据量小则问题不大,如果需要进行大数据量传输,则该TCP连接的带宽就会成为性能瓶颈
2. channel#queueDeclare用于声明队列,接口签名如下
/**
* Declare a queue
* @param queue 队列名字
* @param durable 是否持久化队列 ,持久化的队列会存盘,在服务器重启的时候可以保证不丢失相关信息
* @param exclusive 是否为排他。设置是否排他。为true 则设置队列为排他的。如果一个队列被声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除
* @param autoDelete 长时间不使用的时候是否自动删除
* @param arguments 设置队列的其他一些参数,如x-message=ttl、x-expires、x-max-length、x-max-length-bytes、x-dead-letter-exchange、x-dead-letter-routing-key、x-max-priority等
*/
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
3. channel#basicPublis用于发布1条消息,接口签名如下
/**
* Publish a message.
* @param exchange交换器的名称。如果为空字符串,消息会被发送到RabbitMQ默认交换器中
* @param routingKey 路由键,交换器根据路由键将消息存储到相应的队列之中
* @param props 消息属性 - routing headers 等
* @param body 消息体,真正需要发送的消息
* @throws java.io.IOException if an error is encountered
*/
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
接收消息参考代码
/**
* 消费者
*/
public class Customer {
private final static String QUEUE_NAME = "rabbitMQ.test";
public static void main(String[] args) throws IOException, TimeoutException {
System.out.println("Customer PROCESS");
// 创建连接工厂,设置RabbitMQ地址
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
System.out.println("connection...");
//创建一个新的连接
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
try {
//声明要关注的队列
//channel.queueBind(QUEUE_NAME, null, null);
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println("Customer Waiting Received messages");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDeliveryå
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
//envelope主要存放生产者相关信息(比如交换机、路由key等)body是消息实体
String message = new String(body, "UTF-8");
System.out.println("Customer Received '" + message + "'");
}
};
//自动回复队列应答 -- RabbitMQ中的消息确认机制
channel.basicConsume(QUEUE_NAME, true, consumer);
} finally {
//关闭通道和连接
//channel.close();
//connection.close();
}
}
}
执行结果
Customer PROCESS
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
connection...
Customer Waiting Received messages
Customer Received 'Hello world '
1.处理消息channel#basicConsume
RabbitMQ 消费模式分推/拉两种模式, 推模式是最长用的, 接收消息可通过实现Consumer接口或者继承DefaultConsumer类来实现, 当接收到一条消息后就会调用Consumer#handleDelivery
/**
* @param 队列名称
* @param 设置是否自动确认。建议设成false,即不自动确认;
* @param 设置消费者的回调函数。用来处理RabbitMQ 推送过来的消息,比如DefaultConsumer,使用时需要客户端重写(override) 其中的方法
*/
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
官网实例中建议使用DeverCallBack接口,参考代码如下
public class Customer2 {
private final static String QUEUE_NAME = "rabbitMQ.test";
public static void main(String[] args) throws IOException, TimeoutException {
System.out.println("Customer PROCESS");
// 创建连接工厂,设置RabbitMQ地址
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
System.out.println("connection...");
//创建一个新的连接
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
try {
//声明要关注的队列
//channel.queueBind(QUEUE_NAME, null, null);
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println("Customer Waiting Received messages");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println("Customer Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
} finally {
//关闭通道和连接
//channel.close();
//connection.close();
}
}
}
DeviverCallback接口是一个回调接口 ,当队列中有消息的时候会执行回调接口实现处理消息
/**
* Callback interface to be notified when a message is delivered.
* Prefer it over {@link Consumer} for a lambda-oriented syntax,
* if you don't need to implement all the application callbacks.
*/
@FunctionalInterface
public interface DeliverCallback {
/**
* Called when a <code><b>basic.deliver</b></code> is received for this consumer.
* @param consumerTag the <i>consumer tag</i> associated with the consumer
* @param message the delivered message
* @throws IOException if the consumer encounters an I/O error while processing the message
*/
void handle(String consumerTag, Delivery message) throws IOException;
}
参考文献:https://www.rabbitmq.com/tutorials/tutorial-one-java.html