一、简单队列模型
- P(producer):生产者——发送消息
- Q(Queue): 消息队列(图中红色方形)——存储消息
- C(consumer): 消费者——接收消息
二、Java编程实现
1、导入AMQP协议jar包(RabbitMQ是基于AMQP通讯协议的);
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
2、创建RabbitMQ连接工具类;
package com.rabbitMQ.util;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class ConnectionUtils {
/**
* 获取RabbitMQ的连接
* @return Connection
* @author zhoujin
* @throws TimeoutException
* @throws IOException
* @date 2019-1-13
*/
public static Connection getConnection() throws IOException, TimeoutException{
// 1.定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2.设置连接参数
factory.setHost("127.0.0.1"); // 连接地址
factory.setPort(5672); // 监听端口
factory.setVirtualHost("/vhost_study"); // vhost
factory.setUsername("study_user"); // 用户名
factory.setPassword("123"); // 密码
// 3.返回连接对象
return factory.newConnection();
}
/**
* 关闭连接
* @param connection
* @throws IOException
* @return void
* @author zhoujin
* @data 2019-1-13
*/
public static void closeConnection(Connection connection) throws IOException{
if (connection != null) {
connection.close();
}
}
/**
* 关闭通道以及连接(先后顺序不能颠倒)
* @param connection
* @throws IOException
* @return void
* @author zhoujin
* @throws TimeoutException
* @data 2019-1-13
*/
public static void closeConnection(Channel channel, Connection connection) throws IOException, TimeoutException{
if (channel != null) {
channel.close();
}
if (connection != null) {
connection.close();
}
}
}
3、创建生产者发送消息;
package com.rabbitMQ.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitMQ.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* 消息生产者
* @author zhoujin
* @data 2019-1-13
*/
public class SimpleProducer {
private static final String QUEUE_NAME = "simple_queue";
public static void main(String[] args) {
Connection conn = null;
Channel channel = null;
try {
// 1.获取连接
conn = ConnectionUtils.getConnection();
// 2.从连接中获取通道
channel = conn.createChannel();
/*
* 3.声明队列
* param_1:队列名称
* param_2:是否持久化(重启是否消失)
* param_3:是否独占(仅创建者Connection私有,断开连接后自动删除)
* param_4:没有连接时是否自动删除
* param_5:此队列的其他属性设置
*/
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
/*
* 4.发送消息到队列中
* param_1:交换机名称(若没有定义交换机,则此处填"",使用默认交换机)
* param_2:Routing Key(路由键)
* param_3:此消息的其他属性设置
* param_4:需要发送的消息内容
* ★ 注:默认交换机(AMQP default),它是direct类型的。默认交换机隐式地绑定到每个队列,Routing Key(路由键)等于队列名称。
*/
String message = "This is simple MQ!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("======================= Simple MQ send message end! 【Content:" + message + "】 =======================");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
try {
ConnectionUtils.closeConnection(channel, conn);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
}
4、创建消费者接收消息。
package com.rabbitMQ.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitMQ.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import com.rabbitmq.client.ShutdownSignalException;
/**
* 消息消费者(接收者)
* @author zhoujin
* @data 2019-1-13
*/
public class SimpleConsumer {
private static final String QUEUE_NAME = "simple_queue";
public static void main(String[] args) {
// 4.x以前的版本使用旧方法
// oldMethod();
// 4.x以后的版本使用新方法
newMethod();
}
public static void newMethod(){
try {
// 1.获取连接
Connection conn = ConnectionUtils.getConnection();
// 2.从连接中获取通道
Channel channel = conn.createChannel();
/*
* 3.声明队列
* param_1:队列名称
* param_2:是否持久化(重启是否消失)
* param_3:是否独占(仅创建者Connection私有,断开连接后自动删除)
* param_4:没有连接时是否自动删除
* param_5:此队列的其他属性设置
*/
channel.queueDeclareNoWait(QUEUE_NAME, false, false, false, null);
// 4.定义消费者获取消息(将客户端与队列绑定,若该队列中有消息,便会执行回调函数handleDelivery)
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override // envelope对象中是生产者相关信息(比如交换机名、路由键等),body是消息实体
public void handleDelivery(String consumerTag,
Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("=======================NewMethod: Simple MQ received a message! 【Content:" + message + "】 =======================");
}
};
/*
* 5.监听队列,回复消息接收反馈,通知将此消息在队列中移出(RabbitMQ中的消息确认机制)
* param_1: 队列名称
* param_2: autoAck:是否自动ack。如果不自动ack,需要使用channel.ack、channel.nack、channel.basicReject进行消息应答。
* param_3: 消费者对象
*/
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
/**
* 4.x版本之前
* @return void
* @author zhoujin
* @data 2019-1-13
*/
public static void oldMethod(){
try {
// 1.获取连接
Connection conn = ConnectionUtils.getConnection();
// 2.从连接中获取通道
Channel channel = conn.createChannel();
// 3.定义队列的消费者(4.x版本开始废弃)
QueueingConsumer consumer = new QueueingConsumer(channel);
// 4.监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
// 5.获取消息
while(true){
Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody(), "UTF-8");
System.out.println("======================= Simple MQ received a message! 【Content:" + message + "】 =======================");
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (ShutdownSignalException e) {
e.printStackTrace();
} catch (ConsumerCancelledException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、运行代码以及控制台输出
1、运行生产者代码;
1.1、 控制台输出
1.2、在web管理页面查看消息
1.3、在web管理页面查看获取消息
2、运行消费者程序。
2.1、消费者控制台输出(旧方法)
2.2、新方法控制台输出
四、简单队列(Simple Queue)的不足
耦合性高,生产者一一对应消费者,若想有多个消费者获取队列中的消息,就无法获取。 队列名变更,就需要生产者和消费者两边同时变更。