RabbitMQ工作模式
- Work queues
- Publish/Subscribe
- Routing
- Topics
- Header
- RPC
Work queues
work queues工作模式是一个服务端,两个消费端共同消费同一个队列中的消息
应用场景
对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度
需求
使用一个服务端发送消息,多个消费端接收
工作模式
代码演示
- 一个生产者
- 两个消费者
生产者代码
public class Producer01 {
//队列名称
private static final String QUEUE = "helloworld";
@SneakyThrows
public static void main(String[] args) {
//创建连接工厂对象
ConnectionFactory factory = new ConnectionFactory();
//链接信息
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务 器
factory.setVirtualHost("/");
//创建于rabbitMQ服务得tcp连接
Connection connection = null;
Channel channel = null;
try {
connection= factory.newConnection();
//创建与exchange的通道,每个连接可以创建多个通道,每个通道代表一个会话任务
channel = connection.createChannel();
/**
* 声明队列,如果rabbit中没有次队列将自动创建
* param1:队列名称
* param2:是否持久化,若持久化,mq重启后该队列仍然存在
* param3:队列是否独占此连接,队列只允许在该连接中访问,如果连接时关闭,则队列自动删除
* param4:队列不再使用时是够自动删除次队列
* param5:扩展参数
*/
channel.queueDeclare(QUEUE,true,false,false,null);
String message = "hello world"+System.currentTimeMillis();
/**
* 消息发布方法
* param1:exchange的名称,如果没有指定,则使用default exchange
* param2:routinKey,消息的路由key,是用于Exchange(交换机)将消息转发到指定的消息
* param3:消息包含的属性
* param4:消息体
*/
channel.basicPublish("",QUEUE,null,message.getBytes());
System.out.println("Send Message is:"+message+"'");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if (channel!= null){
channel.close();
}
if (connection != null){
connection.close();
}
}
}
}
消费端代码
可以编写多个消费端来接收服务端发送的消息,这里用了两个一样的消费端来做测试
public class Consumer02 {
private static final String QUEUE = "helloworld";
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitMQ所在服务器的ip和端口
factory.setHost("127.0.0.1");
factory.setPort(5672);
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
//声明一个队列
channel.queueDeclare(QUEUE,true,false,false,null);
//定义消费方法
DefaultConsumer consumer = new DefaultConsumer(channel) {
/**
* 消费者接收消息调用此方法
* @param consumerTag 消费者的标签,在channel.baseicConsumer()去指定
* @param envelope 消息包的内容,可从中获取消息id,消息routingkey,叫花鸡,消息和重传标志
* (收到消息失败后是否需要冲洗发送)
* @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();
//路由key
String routingKey = envelope.getRoutingKey();
//消息id
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String msg = new String(body, "utf-8");
System.out.println("receive message..."+msg);
}
};
/**
* 监听队列String queue,boolean autoAck,Consumer callback
* String queue:队列名称
* boolean autoAck:设置为true表示消息接收自动向mq回复收到了,mq接收到回复会删除消息,设置为false则需要手动回复
* Consumer callback:消费消息的方法,消费者接收到消息后调用此方法
*/
channel.basicConsume(QUEUE,true,consumer);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
结果展示
一条消息只会被一个消费者接收
rabbitmq采用轮询的方式将消息平均发送给消费者的
消费者在处理完某条消息后,才会收到下一条消息
Publish/subscribe模式
发布订阅模式就是每个消费者监听自己的队列
生产者将消息消息发送给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都会接收到消息
发布订阅模式
生产者
声明exchange_fanout_inform交换机
声明两个队列并绑定到此交换机,绑定时不需要指定routingkey
发送消息时不需要指定routingkey
public class Producer02_publish {
//队列名称
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";
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
//创建连接
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("guest");
factory.setUsername("guest");
factory.setVirtualHost("/");
connection = factory.newConnection();
//创建与交换机的通道
channel = connection.createChannel();
//声明交换机
/**
* Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type)
* exchange:交换机名称
* type:交换机类型(fanout,topic,direct,headers)
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
//声明队列
/**
* Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
* Map<String, Object> arguments)
* queue:代表队列
* durable:是否持久化
* exclusive:是否独占此队列
* autoDelete:队列不用时是够自动删除
* arguments:额外参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
//交换机和队列绑定
/**
* Queue.BindOk queueBind(String queue, String exchange, String routingKey)
* queue:队列名称
* exchange:交换机名称
* routingKey:路由key
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
//发送消息
String message = "producer 发送一条数据";
/**
* void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* exchange :交换机名称
* routingKey :路由key根据key名称将消息转发到具体的队列,这里填写队列名称表示消息将发到此队列
* props :配置参数
* body :发送消息体
*/
channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,message.getBytes());
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if (channel != null){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
public class Producer02_publish {
//队列名称
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";
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
//创建连接
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("guest");
factory.setUsername("guest");
factory.setVirtualHost("/");
connection = factory.newConnection();
//创建与交换机的通道
channel = connection.createChannel();
//声明交换机
/**
* Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type)
* exchange:交换机名称
* type:交换机类型(fanout,topic,direct,headers)
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
//声明队列
/**
* Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
* Map<String, Object> arguments)
* queue:代表队列
* durable:是否持久化
* exclusive:是否独占此队列
* autoDelete:队列不用时是够自动删除
* arguments:额外参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
//交换机和队列绑定
/**
* Queue.BindOk queueBind(String queue, String exchange, String routingKey)
* queue:队列名称
* exchange:交换机名称
* routingKey:路由key
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
//发送消息
String message = "producer 发送一条数据";
/**
* void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* exchange :交换机名称
* routingKey :路由key根据key名称将消息转发到具体的队列,这里填写队列名称表示消息将发到此队列
* props :配置参数
* body :发送消息体
*/
channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,message.getBytes());
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if (channel != null){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class Consumer02_subscribe_sms {
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
//交换机
private static final String EXCHANGE_FANOUT_INFORM = "exchange_fanout_inform";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个与MQ连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("guest");
factory.setUsername("guest");
factory.setVirtualHost("/");
//创建一个连接
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
System.out.println(message);
}
};
channel.basicConsume(QUEUE_INFORM_SMS,true,defaultConsumer);
}
}
结果
发布订阅模式就是将生产者发送的消息通过一个交换机转发到每个队列中
Publish/subscibe与work queues的区别
不同点
work queues不用定义交换机,而publish/subscribe需要定义交换机
publish/subscribe的生产方是面向交换机发送消息,work queues的生产方是面向队列发送消息(底层是默认使用交换机)
相同点
两者实现的发布/订阅的效果是一样的,多个消费端监听同一个队列不会重复消费消息
工作中比较常用publish/subscribe,发布订阅模式比工作队列模式更强大,并且发布订阅模式可以制定自己专有的交换机
Routing工作模式
每个消费者监听自己的队列,并且设置routingkey
生产者将消息发给交换机,由交换机根据routing来转发消息到指定的队列
工作模式
生产者
声明exchange_routing_inform交换机
声明两个队列并且绑定到此交换机,绑定时需要指定routingkey
发送消息时需要指定routingkey
public class Producer03_routing {
//队列名称
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";
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
channel = connection.createChannel();
//声明交换机 String exchange, BuiltinExchangeType type
/**
*参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
//声明队列
//channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
//交换机和队列绑定String queue, String exchange, String routingKey
/**
*参数明细
*1、队列名称
*2、交换机名称
*3、路由key
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,QUEUE_INFORM_EMAIL);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,QUEUE_INFORM_SMS);
//发送邮件消息
for (int i=0;i<3;i++) {
String message = "email inform to user";
//向交换机发送消息 String exchange, String routingKey, BasicProperties props,byte[] body
/**
*参数明细
*1、交换机名称,不指令使用默认交换机名称 Default Exchange
*2、routingKey(路由key),根据key名称将消息转发到具体的队列,这里填写队列名称表示消
息将发到此队列
*3、消息属性
*4、消息内容
*/
channel.basicPublish(EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL, null, message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
//发送短信消息
for (int i=0;i<3;i++) {
String message = "sms inform to user";
//向交换机发送消息 String exchange, String routingKey, BasicProperties props,byte[] body
channel.basicPublish(EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_SMS, null, message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally{
if(channel!=null){ try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(connection!=null){ try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
public class Consumer03_routing_email {
//队列名称
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String EXCHANGE_ROUTING_INFORM="inform_exchange_routing";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory(); factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
Connection connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
Channel channel = connection.createChannel();
//声明交换机 String exchange, BuiltinExchangeType type
/**
*参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
//声明队列
// channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
//交换机和队列绑定String queue, String exchange, String routingKey
/**
*参数明细
*1、队列名称
*2、交换机名称
*3、路由key
*/ channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,QUEUE_INFORM_EMAIL);
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body) throws IOException {
long deliveryTag = envelope.getDeliveryTag(); String exchange = envelope.getExchange();
//消息内容
String message = new String(body, "utf-8");
System.out.println(message);
}
};
/**
*监听队列String queue, boolean autoAck,Consumer callback
*参数明细
*1、队列名称
*2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置 为false则需要手动回复
*3、消费消息的方法,消费者接收到消息后调用此方法
*/
channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);
}
}
public class Consumer03_routing_sms {
//队列名称
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
private static final String EXCHANGE_ROUTING_INFORM="inform_exchange_routing";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory(); factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
Connection connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
Channel channel = connection.createChannel();
//声明交换机 String exchange, BuiltinExchangeType type
/**
*参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
//声明队列
// channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
//交换机和队列绑定String queue, String exchange, String routingKey
/**
*参数明细
*1、队列名称
*2、交换机名称
*3、路由key
*/ channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,QUEUE_INFORM_SMS);
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body) throws IOException {
long deliveryTag = envelope.getDeliveryTag(); String exchange = envelope.getExchange();
//消息内容
String message = new String(body, "utf-8"); System.out.println(message);
}
};
/**
*监听队列String queue, boolean autoAck,Consumer callback
*参数明细
*1、队列名称
*2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置 为false则需要手动回复
*3、消费消息的方法,消费者接收到消息后调用此方法
*/
channel.basicConsume(QUEUE_INFORM_SMS, true, defaultConsumer);
}
}
结果
使用生产者发送若干条消息,交换机根据routingkey转发消息到指定的队列。
Routingkey和Publish/subscribe的区别
Routing模式要求队列绑定交换机是要指定routingkey,消息会转发到符合routingkey的队列。
Topics
每个消费者监听自己的队列,并且设置带通配符的routingkey
生产者将消息发给broker,由交换机格局routingkey来转发消息到指定的队列
工作模式
生产者
声明交换机,指定topics类型
public class Producer04_topics {
//队列名称
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_TOPICS_INFORM="exchange_topics_inform";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = null;
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory(); factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
Channel channel = connection.createChannel();
//声明交换机 String exchange, BuiltinExchangeType type
/**
* 参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_TOPICS_INFORM, BuiltinExchangeType.TOPIC);
//声明队列
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null); channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
//发送邮件消息
for (int i=0;i<3;i++){
String message = "email inform to user"+i;
//向交换机发送消息 String exchange, String routingKey, BasicProperties props,byte[] body
/**
*参数明细
*1、交换机名称,不指令使用默认交换机名称 Default Exchange
*2、routingKey(路由key),根据key名称将消息转发到具体的队列,这里填写队列名称表示消息将发到此队列
*3、消息属性
*4、消息内容
*/
channel.basicPublish(EXCHANGE_TOPICS_INFORM, "inform.email", null,
message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
//发送短信消息
for (int i=0;i<3;i++){
String message = "sms inform to user"+i;
channel.basicPublish(EXCHANGE_TOPICS_INFORM, "inform.sms", null,message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
//发送短信和邮件消息
for (int i=0;i<3;i++){
String message = "sms and email inform to user"+i;
channel.basicPublish(EXCHANGE_TOPICS_INFORM, "inform.sms.email", null,
message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
if (channel!=null){
channel.close();
}
if (connection!=null){
connection.close();
}
}
}
消费端
队列绑定交换机指定通配符
通配符规则:中间以“.”分割
符号#可以匹配多个词,符号*可以匹配一个词语
public class Consumer04_topics_email {
//队列名称
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_TOPICS_INFORM="exchange_topics_inform";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
Connection connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
Channel channel = connection.createChannel();
//声明队列
// channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
//声明交换机 String exchange, BuiltinExchangeType type
/**
*参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_TOPICS_INFORM, BuiltinExchangeType.TOPIC);
//绑定email通知队列
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPICS_INFORM,"inform.#.email.#");
//绑定sms通知队列
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPICS_INFORM,"inform.#.sms.#");
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body) throws IOException {
long deliveryTag = envelope.getDeliveryTag(); String exchange = envelope.getExchange();
//消息内容
String message = new String(body, "utf-8");
System.out.println(message);
}
};
/**
*监听队列String queue, boolean autoAck,Consumer callback
*参数明细
*1、队列名称
*2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置 为false则需要手动回复
*3、消费消息的方法,消费者接收到消息后调用此方法
*/
channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);
channel.basicConsume(QUEUE_INFORM_SMS, true, defaultConsumer);
}
}
结果
使用生产者发送若干条信息,交换机根据routingkey通配符匹配并转发消息到指定的队列。
Header模式
header模式与routing不同的地方在于,header模式取消了routingkey,使用header中的key/value(键值对)佩佩队列
生产者
public class Producer05_header {
//队列名称
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_HEADER_INFORM="exchange_header_inform";
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
channel = connection.createChannel();
//声明交换机 String exchange, BuiltinExchangeType type
/**
*参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_HEADER_INFORM, BuiltinExchangeType.HEADERS);
//声明队列
//channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
Map<String,Object> headers_eamil = new HashMap<>();
headers_eamil.put("inform_type","email");
HashMap<String, Object> headers_sms = new HashMap<>();
headers_sms.put("inform_type","sms");
//交换机和队列绑定String queue, String exchange, String routingKey
/**
*参数明细
*1、队列名称
*2、交换机名称
*3、路由key
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_HEADER_INFORM,"",headers_eamil);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_HEADER_INFORM,"",headers_sms);
//发送邮件消息
for (int i=0;i<3;i++) {
String message = "email inform to user";
Hashtable<String, Object> headers = new Hashtable<>();
headers.put("inform_type","email");//匹配email通知消费者绑定的header
headers.put("inform_type","sms");//匹配sms通知消费者绑定的header
AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder();
properties.headers(headers);
//email通知
channel.basicPublish(EXCHANGE_HEADER_INFORM,"",properties.build(),message.getBytes());
//向交换机发送消息 String exchange, String routingKey, BasicProperties props,byte[] body
/**
*参数明细
*1、交换机名称,不指令使用默认交换机名称 Default Exchange
*2、routingKey(路由key),根据key名称将消息转发到具体的队列,这里填写队列名称表示消
息将发到此队列
*3、消息属性
*4、消息内容
*/
// channel.basicPublish(EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL, null, message.getBytes());
System.out.println("Send Message is:'" + message + "'");
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally{
if(channel!=null){ try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(connection!=null){ try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
public class Consumer05_header {
//队列名称
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_HEADER_INFORM="exchange_header_inform";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory(); factory.setHost("127.0.0.1");
factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
Connection connection = factory.newConnection();
//创建与交换机的通道,每个通道代表一个会话
Channel channel = connection.createChannel();
//声明交换机 String exchange, BuiltinExchangeType type
/**
*参数明细
*1、交换机名称
*2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_HEADER_INFORM, BuiltinExchangeType.HEADERS);
Hashtable<String, Object> headers_email = new Hashtable<>();
headers_email.put("inform_email","email");
//声明队列
// channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
*参数明细:
*1、队列名称
*2、是否持久化
*3、是否独占此队列
*4、队列不用是否自动删除
*5、参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, headers_email);
//交换机和队列绑定String queue, String exchange, String routingKey
/**
*参数明细
*1、队列名称
*2、交换机名称
*3、路由key
*/
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_HEADER_INFORM,QUEUE_INFORM_EMAIL);
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body) throws IOException {
long deliveryTag = envelope.getDeliveryTag(); String exchange = envelope.getExchange();
//消息内容
String message = new String(body, "utf-8");
System.out.println(message);
}
};
/**
*监听队列String queue, boolean autoAck,Consumer callback
*参数明细
*1、队列名称
*2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置 为false则需要手动回复
*3、消费消息的方法,消费者接收到消息后调用此方法
*/
channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);
}
}
结果
RPC
RPC即客户端远程调用服务端的方法,使用MQ可以实现RPC的异步调用,基于Direct交换机实现
实现流程
- 客户端即是生产者又是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC相应队列
- 服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果
- 服务端将RPC方法的结果发送到RPC响应队列
- 客户端(RPC调用方法)监听RPC相应队列,接收到RPC调用结果。
总结
以上的工作模式都是将不同的请求消息通过消息队列来对消息进行异步处理,以此来提高程序的响应时间,提高程序的效率,不同的是在声明交换机时,BuiltinExchangeType的不同,其他的也大差不差。