一、Simple Queue:简单队列模式
1.1 基础
介绍
最简单的工作队列,其中一个生产者,一个消费者,一个队列,也称为点对点模式。(用的是默认交换机
)
图示
1.2 代码
生产者
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
// 设置虚拟主机
connectionFactory.setVirtualHost("/");
// 创建Connection
Connection connection = connectionFactory.newConnection();
// 创建Channel
Channel channel = connection.createChannel();
// 通过通道创建队列,后续所有的操作都是基于channel实现。(队列也可以由消费方创建)
channel.queueDeclare("queueName",false,false,false,null);
// 发送消息
String msg = "RabbitMQ Simple Queue TEST";
// 向队列中发送消息:
// 参数1:为" "表示为default exchange
// 参数2:(当声明为默认交换机时)队列的名称(因为当使用默认交换机时,生产者是通过指定队列的名称直接发送的)
channel.basicPublish("","queueName",null,msg.getBytes());
channel.close();
connection.close();
}
消费者
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 监听队列中的消息(消费的是队列,而不是交换机)
channel.basicConsume("queueName", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
二、Work Queues:工作队列模式
2.1 基础
介绍
一个生产者,一个消息队列,多个消费者。同样也称为点对点模式。(用的是默认交换机
)
一个消息只能被一个消费者消费,资源的竞争。(通过轮询或其他方式分配给消费者)
默认情况下,rabbitmq将会按顺序派发
每个任务给下一个消费者,平均而言,每个消费者将获得相同数量的消息,这种分发消息的方式称为轮询。
图示
特点
Work Queue相比于简单队列模式,在于工作队列模式有多个消费者,但是一个消息只能被一个消费者消费
,而简单队列模式只有一个消费者。
2.2 代码
生产者
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
// 设置虚拟主机
connectionFactory.setVirtualHost("/");
// 创建Connection
Connection connection = connectionFactory.newConnection();
// 创建Channel
Channel channel = connection.createChannel();
// 通过通道创建队列,后续所有的操作都是基于channel实现。(队列也可以由消费方创建)
channel.queueDeclare("queueName", false, false, false, null);
// 向队列中发送消息:
// 参数1:为" "表示为default exchange
// 参数2:(当声明为默认交换机时)队列的名称(因为当使用默认交换机时,生产者是通过指定队列的名称直接发送的)
for (int i = 1; i <= 10; i++) {
channel.basicPublish("", "queueName", null, ("Hello RabbitMQ!!!" + i).getBytes());
}
channel.close();
connection.close();
}
消费者1
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 监听队列中的消息(消费的是队列,而不是交换机)
channel.basicConsume("queueName", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
消费者2
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 监听队列中的消息(消费的是队列,而不是交换机)
channel.basicConsume("queueName", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
三、Publish/Subscribe:发布与订阅模式
3.1 基础
介绍
将消息发送给不同类型的消费者。做到发布一次,消费多个。(用的是Fanout Exchange交换机
)
场景举例
用户注册:用户在注册完后一般都会发送消息通知用户注册成功(失败)。如果在一个系统中,用户注册信息有邮箱、手机号,那么在注册完后会向邮箱和手机号都发送注册完成信息。利用MQ实现业务异步处理,如果是用工作队列的话,就会声明一个注册信息队列。注册完成之后生产者会向队列提交一条注册数据,消费者取出数据同时向邮箱以及手机号发送两条消息。但是实际上邮箱和手机号信息发送实际上是不同的业务逻辑,不应该放在一块处理。这个时候就可以利用发布/订阅模式将消息发送到转换机(EXCHANGE),声明两个不同的队列(邮箱、手机),并绑定到交换机。这样生产者只需要发布一次消息,两个队列都会接收到消息发给对应的消费者。
图示
3.2 代码
生产者
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
// 设置虚拟主机
connectionFactory.setVirtualHost("/");
// 创建Connection
Connection connection = connectionFactory.newConnection();
// 创建Channel
Channel channel = connection.createChannel();
// 创建交换机(交换机没有存储数据的能力,数据存储在队列上。如果有交换机没队列的情况下,数据会丢失。)
// 参数一:交换机名称;参数二:交换机类型。
channel.exchangeDeclare("fanout_exchange_name", "fanout");
// 向队列中发送消息
for (int i = 1; i <= 10; i++) {
// 因为fanout exchange与routing key无关,所以设置为空字符串。
channel.basicPublish("fanout_exchange_name", "", null, ("Hello RabbitMQ!!!" + i).getBytes());
}
channel.close();
connection.close();
}
消费者1
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建队列
channel.queueDeclare("fanout_queue1_name", false, false, false, null);
// 给队列绑定交换机
channel.queueBind("fanout_queue1_name", "fanout_exchange_name", "");
// 监听队列中的消息
channel.basicConsume("fanout_queue1_name", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
消费者2
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建队列
channel.queueDeclare("fanout_queue2_name", false, false, false, null);
// 给队列绑定交换机
channel.queueBind("fanout_queue2_name", "fanout_exchange_name", "");
// 监听队列中的消息
channel.basicConsume("fanout_queue2_name", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
四、Route:路由模式
4.1 基础
介绍
在发布订阅模式的基础上,根据routing key有选择地转发消息到匹配的queue。(用的是Direct Exchange交换机
)
图示
4.2 代码
生产者
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
// 设置虚拟主机
connectionFactory.setVirtualHost("/");
// 创建Connection
Connection connection = connectionFactory.newConnection();
// 创建Channel
Channel channel = connection.createChannel();
// 创建交换机(交换机没有存储数据的能力,数据存储在队列上。如果有交换机没队列的情况下,数据会丢失)
// 参数一:交换机名称;参数二:交换机类型。
channel.exchangeDeclare("direct_exchange_name", "direct");
// 向队列中发送消息
for (int i = 1; i <= 10; i++) {
// 设置路由键为"insert"
channel.basicPublish("direct_exchange_name", "insert", null, ("Hello RabbitMQ!!!" + i).getBytes());
}
channel.close();
connection.close();
}
消费者1
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建队列
channel.queueDeclare("direct_queue1_name", false, false, false, null);
// 给队列绑定交换机,并设置路由键(binding key)。
channel.queueBind("direct_queue1_name", "direct_exchange_name", "select");
channel.queueBind("direct_queue1_name", "direct_exchange_name", "insert");
// 监听队列中的消息
channel.basicConsume("direct_queue1_name", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
消费者2
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建队列
channel.queueDeclare("direct_queue2_name", false, false, false, null);
// 给队列绑定交换机,并设置路由键(binding key)。
channel.queueBind("direct_queue2_name", "direct_exchange_name", "select");
channel.queueBind("direct_queue2_name", "direct_exchange_name", "delete");
// 监听队列中的消息
channel.basicConsume("direct_queue2_name", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
五、Topic:主题模式
5.1 基础
介绍
主题模式可以指定queue的binding key规则,消息携带的routing key与binding key进行模式匹配
,消息将会转发到匹配的queue。(用的是Topic Exchange交换机
)
图示
5.2 代码
生产者
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
// 设置虚拟主机
connectionFactory.setVirtualHost("/");
// 创建Connection
Connection connection = connectionFactory.newConnection();
// 创建Channel
Channel channel = connection.createChannel();
// 创建交换机(交换机没有存储数据的能力,数据存储在队列上。如果有交换机没队列的情况下,数据会丢失。)
// 参数一:交换机名称;参数二:交换机类型。
channel.exchangeDeclare("topic_exchange_name", "topic");
// 向队列中发送消息
for (int i = 1; i <= 10; i++) {
// 设置路由键为"emp.hello world"
// #:匹配0-n个单词(之间以.区分,两点之间算一个单词,可以匹配hello world空格的情况。)
// *:匹配一个单词
channel.basicPublish("topic_exchange_name", "emp.hello world", null, ("Hello RabbitMQ!!!" + i).getBytes());
}
channel.close();
connection.close();
}
消费者1
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建队列
channel.queueDeclare("topic_queue1_name", false, false, false, null);
// 绑定交换机(routing key:路由键)
// #:匹配0-n个单词(之间以.区分,两点之间算一个单词。)
channel.queueBind("topic_queue1_name", "topic_exchange_name", "emp.#");
// 监听队列中的消息
channel.basicConsume("topic_queue1_name", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
消费者2
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建队列
channel.queueDeclare("topic_queue2_name", false, false, false, null);
// 绑定交换机(routing key:路由键)
// *:匹配1个单词(之间以.区分,两点之间算一个单词。)
channel.queueBind("topic_queue2_name", "topic_exchange_name", "emp.*");
// 监听队列中的消息
channel.basicConsume("topic_queue2_name", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2获得消息为:" + new String(body, "utf-8"));
}
});
// 消费方不需要关闭连接,保持一直监听队列状态。
//connection.close();
}
六、RPC:远程调用模式
6.1 基础
介绍
RPC即客户端远程调用服务端的方法 ,使用MQ可以实现RPC的异步调用。(用的是Direct Exchange交换机
)
-
客户端即是生产者也是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
-
服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果。
-
服务端将RPC方法的结果发送到RPC响应队列
-
客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。