本文按照两部分来说明
java连接rabbitMQ的工作模式
基础部分
(1) pom依赖
<dependencies>
<!-- 这是java连接rabbitMQ的原生api依赖 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
(2) 基础连接对象
package com.gl.rabbitmq.original;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author gl
* @time 2020-06-21 15:53
* @function : 这是我们创建连接到rabbitMQ的工具类
* @step :
*/
public class ConnUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//1.连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置主机
factory.setHost("127.0.0.1");
//3.设置端口
factory.setPort(5672);
//4.设置vhost也就是连接哪个库,这里使用我们自定义的虚拟机
factory.setVirtualHost("/vhost_rabbitMQ");
//5.设置用户名,使用我们自定义的用户
factory.setUsername("rabbitMQ");
//6.设置密码
factory.setPassword("rabbitMQ");
//7.获取连接
Connection connection = factory.newConnection();
return connection;
}
}
1.work queues模式
1.1 架构
work queues模式是生产者生产消息发送到queue队列中,可以有一个或者多个消费者去读取这个queue,读取的方式可以为轮询等策略.注意,一条消息只会有一个消费者消费,不会同时发送给多个消费者.例如queue中有10条消息,以轮询的方式发送给两个消费者,则每个消费者5条消息.
1.2 应用场景
适用于处理复杂业务,相当于实现了一个消费端集群的模式,可以让多个消费者来处理共同的一项业务.
1.3 代码
package com.gl.rabbitmq.original;
import com.rabbitmq.client.*;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;
/**
* @author gl
* @time 2020-06-21 16:04
* @function : 第一种模式: 工作队列
* @step :
*/
public class WorkQueueDemo {
private static final String QUEUE_NAME = "queue_test1";
private CountDownLatch countDownLatch = new CountDownLatch(1);
/**
* 生产者
* @throws IOException
* @throws TimeoutException
*/
@Test
public void producer() throws IOException, TimeoutException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列
/**
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//4.发送消息
/**
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange: 交换机,如果传递""空串表示使用默认的交换机
* routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
* props: 消息的属性
* body: 消息的内容
*/
String msg = "hello world";
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
System.out.println("发送成功!!");
channel.close();
}
/**
* 消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
*/
@Test
public void consumer1() throws IOException, TimeoutException, InterruptedException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列
/**
* 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//声明一个消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
* 接收到消息时,调用此方法
* @param consumerTag 消费者标签
* @param envelope 信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
java.lang.String exchange = envelope.getExchange();
long deliveryTag = envelope.getDeliveryTag();
System.out.println("exchange:" + exchange);
System.out.println("deliveryTag:" + deliveryTag);
//接收消息
String msg = new String(body, "utf-8");
System.out.println("receive msg: "+ msg);
//使主线程执行,不阻塞了
countDownLatch.countDown();
}
};
//4.监听队列,消费消息
/**
* String queue, boolean autoAck, Consumer callback
* 参数明细:
* queue: 队列名
* autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
* callback: 消费方法,当前消费者受到消息后要执行的方法.
*/
channel.basicConsume(QUEUE_NAME,true,consumer);
//这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
//阻塞当前线程
countDownLatch.await();
while (true){
}
}
}
2.Publish/Subscribe(发布/订阅)模式
2.1 架构
生产者先将消息发送给交换机,交换机在发送给绑定这个交换机的队列,然后消费者去读取不同的queue,就可实现一条消息发给不同的用户.
发布订阅模式完全可以实现工作队列模式的功能.
2.2 应用场景
适用于一条消息需要交给两个不同的业务去处理,并且每个业务互不影响
2.3 代码
package com.gl.rabbitmq.original;
import com.rabbitmq.client.*;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;
/**
* @author gl
* @time 2020-06-21 22:14
* @function :第二种模式: 发布订阅模式
* @step : 相对于workqueue多了exchange
*/
public class PublishSubscribeDemo {
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
private static final String EXCHANGE_FANOUT_INFORM = "exchange_fanout_inform";
private CountDownLatch countDownLatch = new CountDownLatch(1);
/**
* 生产者
* @throws IOException
* @throws TimeoutException
*/
@Test
public void producer() throws IOException, TimeoutException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列,这里就需要声明两个队列了
/**
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
//4 声明交换机
/**
* 参数明细
* 参数1: 交换机名称
* 参数2: 交换机类型
* fanout: 对应发布订阅模式
* direct: 对应routing路由模式
* topic: 对应通配符模式
* header: 对应header模式
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM,BuiltinExchangeType.FANOUT);
//5.将2个队列绑定在交换机上面
/**
* 参数1: 队列名字
* 参数2: 交换机名字
* 路由key: 在发布订阅模式中使用空串;在路由模式和通配符模式中会根据路由key将消息发送到指定的queue中去
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
//6.发送消息
/**
* channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,msg.getBytes());
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange: 交换机,如果传递""空串表示使用默认的交换机
* routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
* props: 消息的属性
* body: 消息的内容
*/
String msg = "send message 消费成功";
for (int i = 0;i < 10 ; i ++){
//因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,(msg + i).getBytes());
System.out.println("发送成功!!消息内容:" + (msg + i));
}
channel.close();
}
/**
* email消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
* 这里可以同时开启两次测试
*/
@Test
public void consumerEmail() throws IOException, TimeoutException, InterruptedException {
publishSuvscirbeConsume(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM);
}
/**
* SMS消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
* 这里可以同时开启两次测试
*/
@Test
public void consumerSMS() throws IOException, TimeoutException, InterruptedException {
publishSuvscirbeConsume(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM);
}
/**
* 这是提取公共的发布订阅的consumer方法
*/
public void publishSuvscirbeConsume(String queue,String exchange) throws IOException, TimeoutException, InterruptedException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列
/**
* 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(queue,true,false,false,null);
//4.声明交换机
channel.exchangeDeclare(exchange,BuiltinExchangeType.FANOUT);
//5.绑定queue到exchange上
channel.queueBind(queue,exchange,"");
//声明一个消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
* 接收到消息时,调用此方法
* @param consumerTag 消费者标签
* @param envelope 信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
java.lang.String exchange = envelope.getExchange();
long deliveryTag = envelope.getDeliveryTag();
System.out.println("exchange:" + exchange);
System.out.println("deliveryTag:" + deliveryTag);
//接收消息
String msg = new String(body, "utf-8");
System.out.println("receive msg: "+ msg);
//使主线程执行,不阻塞了
countDownLatch.countDown();
}
};
//4.监听队列,消费消息
/**
* String queue, boolean autoAck, Consumer callback
* 参数明细:
* queue: 队列名
* autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
* callback: 消费方法,当前消费者受到消息后要执行的方法.
*/
channel.basicConsume(queue,true,consumer);
//这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
//阻塞当前线程
countDownLatch.await();
while (true){
}
}
}
3.Routing(路由)模式
3.1 架构
生产者先将消息发送给交换机,交换机再根据路由key相等发送到不同的queue中,然后消费者去读取不同的queue,就可实现一条消息发给不同的用户.
路由模式完全可以实现发布订阅模式的功能
3.2 应用场景
适用于一条消息,需要发送给不同的业务,并且根据业务的不同还需要对消息做不同的发送策略.
3.3 代码
package com.gl.rabbitmq.original;
import com.rabbitmq.client.*;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author gl
* @time 2020-06-21 22:14
* @function :第三种模式: routing路由模式
* @step : 相对于发布订阅模式多了routingKey
*/
public class RoutingDemo {
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
private static final String EXCHANGE_ROUTING_INFORM = "exchange_routing_inform";
private static final String ROUTING_EMAIL = "inform_email";
private static final String ROUTING_SMS = "inform_sms";
/**
* 生产者
* @throws IOException
* @throws TimeoutException
*/
@Test
public void producer() throws IOException, TimeoutException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列,这里就需要声明两个队列了
/**
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
//4 声明交换机
/**
* 参数明细
* 参数1: 交换机名称
* 参数2: 交换机类型
* fanout: 对应发布订阅模式
* direct: 对应routing路由模式
* topic: 对应通配符模式
* header: 对应header模式
*/
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM,BuiltinExchangeType.DIRECT);
//5.将2个队列绑定在交换机上面
/**
* 参数1: 队列名字
* 参数2: 交换机名字
* 路由key: 在发布订阅模式中使用空串;在路由模式和通配符模式中会根据路由key将消息发送到指定的queue中去
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTING_EMAIL);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,ROUTING_SMS);
//同一个queue可以绑定多个routingKey
//如果两个queue绑定相同的exchange交换机,相同的routingKey,则我们就可以实现发发布订阅的功能
//只需要我们发送消息时,指定发送到这个交换机的这个routingKey下节课
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,"inform");
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,"inform");
//6.发送消息
/**
* channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,msg.getBytes());
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange: 交换机,如果传递""空串表示使用默认的交换机
* routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
* props: 消息的属性
* body: 消息的内容
*/
for (int i = 0;i < 10 ; i ++){
if(i % 2 == 0){
String msg = "发送消息到email!!";
//因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTING_EMAIL,null,(msg + i).getBytes());
System.out.println("发送成功!!消息内容:" + (msg + i));
}else{
String msg = "发送消息到SMS!!";
//因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTING_SMS,null,(msg + i).getBytes());
System.out.println("发送成功!!消息内容:" + (msg + i));
}
}
channel.close();
}
/**
* email消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
* 这里可以同时开启两次测试
*/
@Test
public void consumerEmail() throws IOException, TimeoutException, InterruptedException {
publishSuvscirbeConsume(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTING_EMAIL);
}
/**
* SMS消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
* 这里可以同时开启两次测试
*/
@Test
public void consumerSMS() throws IOException, TimeoutException, InterruptedException {
publishSuvscirbeConsume(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,ROUTING_SMS);
}
/**
* 这是提取公共的发布订阅的consumer方法
*/
public void publishSuvscirbeConsume(String queue,String exchange,String routingKey) throws IOException, TimeoutException, InterruptedException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列
/**
* 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(queue,true,false,false,null);
//4.声明交换机
channel.exchangeDeclare(exchange,BuiltinExchangeType.DIRECT);
//5.绑定queue到exchange上
channel.queueBind(queue,exchange,routingKey);
//声明一个消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
* 接收到消息时,调用此方法
* @param consumerTag 消费者标签
* @param envelope 信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
String exchange = envelope.getExchange();
long deliveryTag = envelope.getDeliveryTag();
System.out.println("exchange:" + exchange);
System.out.println("deliveryTag:" + deliveryTag);
//接收消息
String msg = new String(body, "utf-8");
System.out.println("receive msg: "+ msg);
}
};
//4.监听队列,消费消息
/**
* String queue, boolean autoAck, Consumer callback
* 参数明细:
* queue: 队列名
* autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
* callback: 消费方法,当前消费者受到消息后要执行的方法.
*/
channel.basicConsume(queue,true,consumer);
//这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
//阻塞当前线程
while (true){
}
}
}
4.Topics(通配符)模式
4.1 架构
生产者先将消息发送给交换机,交换机再根据路由key匹配发送到不同的queue中,然后消费者去读取不同的queue,就可实现一条消息发给不同的用户.
路由模式完全可以实现发布订阅模式的功能
4.2 应用场景
适用于一条消息,需要发送给不同的业务,并且根据业务的不同还需要对消息做不同的发送策略.
4.3 Topics与Routing模式的区别
4.4 代码
package com.gl.rabbitmq.original;
import com.rabbitmq.client.*;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author gl
* @time 2020-06-21 22:14
* @function :第四种模式: topic通配符模式
* @step : 相对于路由模式,只是routingkey不是相等匹配了,而是通配符匹配了
*/
public class TopicDemo {
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
private static final String EXCHANGE_TOPIC_INFORM = "exchange_topic_inform";
private static final String ROUTING_EMAIL = "inform.#.email.#";
private static final String ROUTING_SMS = "inform.#.sms.#";
/**
* 生产者
* @throws IOException
* @throws TimeoutException
*/
@Test
public void producer() throws IOException, TimeoutException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列,这里就需要声明两个队列了
/**
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
//4 声明交换机
/**
* 参数明细
* 参数1: 交换机名称
* 参数2: 交换机类型
* fanout: 对应发布订阅模式
* direct: 对应routing路由模式
* topic: 对应通配符模式
* header: 对应header模式
*/
channel.exchangeDeclare(EXCHANGE_TOPIC_INFORM,BuiltinExchangeType.TOPIC);
//5.将2个队列绑定在交换机上面
/**
* 参数1: 队列名字
* 参数2: 交换机名字
* 路由key: 在发布订阅模式中使用空串;在路由模式和通配符模式中会根据路由key将消息发送到指定的queue中去
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPIC_INFORM,ROUTING_EMAIL);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPIC_INFORM,ROUTING_SMS);
//同一个queue可以绑定多个routingKey
//如果两个queue绑定相同的exchange交换机,相同的routingKey,则我们就可以实现发发布订阅的功能
//只需要我们发送消息时,指定发送到这个交换机的这个routingKey下节课
//channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,"inform");
//channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,"inform");
//6.发送消息
/**
* channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,msg.getBytes());
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange: 交换机,如果传递""空串表示使用默认的交换机
* routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
* props: 消息的属性
* body: 消息的内容
*/
for (int i = 0;i < 5 ; i ++){
String msg = "发送消息到email!!";
//因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
channel.basicPublish(EXCHANGE_TOPIC_INFORM,"inform.email",null,(msg + i).getBytes());
System.out.println("发送成功!!消息内容:" + (msg + i));
}
for (int i = 0;i < 5 ; i ++){
String msg = "发送消息到sms!!";
//因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
channel.basicPublish(EXCHANGE_TOPIC_INFORM,"inform.sms",null,(msg + i).getBytes());
System.out.println("发送成功!!消息内容:" + (msg + i));
}
for (int i = 0;i < 5 ; i ++){
String msg = "发送消息到email和sms!!";
//因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
channel.basicPublish(EXCHANGE_TOPIC_INFORM,"inform.email.sms",null,(msg + i).getBytes());
System.out.println("发送成功!!消息内容:" + (msg + i));
}
channel.close();
}
/**
* email消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
* 这里可以同时开启两次测试
*/
@Test
public void consumerEmail() throws IOException, TimeoutException, InterruptedException {
publishSuvscirbeConsume(QUEUE_INFORM_EMAIL,EXCHANGE_TOPIC_INFORM,ROUTING_EMAIL);
}
/**
* SMS消费者
* @throws IOException
* @throws TimeoutException
* @throws InterruptedException
* 这里可以同时开启两次测试
*/
@Test
public void consumerSMS() throws IOException, TimeoutException, InterruptedException {
publishSuvscirbeConsume(QUEUE_INFORM_SMS,EXCHANGE_TOPIC_INFORM,ROUTING_SMS);
}
/**
* 这是提取公共的发布订阅的consumer方法
*/
public void publishSuvscirbeConsume(String queue,String exchange,String routingKey) throws IOException, TimeoutException, InterruptedException {
//1.通过util对象创建connection对象
Connection connection = ConnUtil.getConnection();
//2.获取channel
Channel channel = connection.createChannel();
//3.声明队列
/**
* 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
* queue:声明队列的名称
* durable: 是否持久化
* exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
* autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
* arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
*/
channel.queueDeclare(queue,true,false,false,null);
//4.声明交换机
channel.exchangeDeclare(exchange,BuiltinExchangeType.TOPIC);
//5.绑定queue到exchange上
channel.queueBind(queue,exchange,routingKey);
//声明一个消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
* 接收到消息时,调用此方法
* @param consumerTag 消费者标签
* @param envelope 信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
String exchange = envelope.getExchange();
long deliveryTag = envelope.getDeliveryTag();
System.out.println("exchange:" + exchange);
System.out.println("deliveryTag:" + deliveryTag);
//接收消息
String msg = new String(body, "utf-8");
System.out.println("receive msg: "+ msg);
}
};
//4.监听队列,消费消息
/**
* String queue, boolean autoAck, Consumer callback
* 参数明细:
* queue: 队列名
* autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
* callback: 消费方法,当前消费者受到消息后要执行的方法.
*/
channel.basicConsume(queue,true,consumer);
//这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
//阻塞当前线程
while (true){
}
}
}
5.RPC模式
因为不太常用,故略.
6.Header模式
不常用:略
总结
生产者生产消息的步骤
- 获取connection对象
- 从connection对象中获取channel
- channel声明exchange对象
- channel声明queue对象
- channel将exchange对象和queue对象绑定在一起
- channel.basicPublish() 发送消息
消费者消费消息的步骤
- 获取connection对象
- 从connection对象中获取channel
- channel声明exchange对象
- channel声明queue对象
- channel将exchange对象和queue对象绑定在一起
- 声明consumer对象,并实现handleDelivery()回调方法,这个方法是收到queue中的消息,对这个消息做处理的方法.
- channel.basicConsume() 消费消息