大伙可以到我的RabbitMQ专栏获取更多信息
demo示例这里拿
模式说明
以日志存储使用场景为例,日志分为不同级别:error、info、warning、debug、trace等,不同日志级别的日志量也是不一样的,级别越高日志量相对越小。
通常情况下因为不同级别日志的数量级不一样,其对应的处理方式也不一样:
有些情况下日志需要入库,数据库不能承受大量日志存储,一般来说数量级较小的日志界别可以直接入库,例如error级别的日志;info级别的日志由于数量级较大可能会先暂存于缓存中,之后再对其做细致的处理。
在此场景下就要使用MQ中的交换机对不同的log message做不同的转发,将error级别的日志传递给入库系统对应的队列,将info级别的日志传递给缓存系统对应的队列,简单的理解就是交换机会将不同的message根据路由规则“分门别类”,这就是Routing模式的典型使用场景。
Routing Key
交换机是怎么进行message的“分门别类”呢?
就需要用到Routing Key,Routing key是队列和交换机绑定的时候的一个标识,生产者在向MQ push message的时候需要带上Routing key,这样交换机可以根据不同message的Routing key来与队列进行匹配,当匹配成功的时候,交换机就将该message传递给该队列,进而达到了message按照规则分发的效果。
生产者
与订阅模式基本相似,只不过订阅模式没有设置routing key,并且设置交换机模式为 DIRECT 类型
在绑定队列到路由器的时候指定该队列的routing key,发布消息的时候也给该消息执行routing key,当消息进入路由器之后开始匹配routing key,当成功匹配到队列后,将该消息传递给该队列。
package com.leolee.rabbitmq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @ClassName ProducerPubSub
* @Description: Rounting模式
* @Author LeoLee
* @Date 2020/11/6
* @Version V1.0
**/
public class ProducerRouting {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//2.设置参数
connectionFactory.setHost("127.0.0.1");//默认值为localhost
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/LeoLee");//默认值为:/
connectionFactory.setUsername("LeoLee");
connectionFactory.setPassword("lyl512240816");
//3.创建连接 connection
Connection connection = connectionFactory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建交换机
/*
String exchange:交换机名称
BuiltinExchangeType type:交换机类型,枚举,
boolean durable:是否持久化
boolean autoDelete:是否自动删除,当没有消费者的时候自动删除
boolean internal:是否内部使用,一般都是false,true代表给MQ内部使用的,比如给MQ开发的插件使用
Map<String, Object> arguments:相关参数
*/
String exchangerName = "test_direct";
channel.exchangeDeclare(exchangerName, BuiltinExchangeType.DIRECT, false, false, false, null);
//6.创建队列
/*
String queue:队列名称
boolean durable:是否持久化
boolean exclusive: 有如下两个意义
是否独占,只有一个消费者监听这个队列
当connection关闭时,是否删除队列
boolean autoDelete:是否自动删除,当没有消费者的时候自动删除
Map<String, Object> arguments: 一些配置参数
*/
//如果没有一个名字叫xxx的队列,则会创建,如果存在该队列,则复用
String queueName1 = "test_direct_queue1";
String queueName2 = "test_direct_queue2";
channel.queueDeclare(queueName1, false, false, false, null);
channel.queueDeclare(queueName2, false, false, false, null);
//7.绑定队列和交换机
/*
String queue:队列名称
String exchange:交换机名称
String routingkey:路由键,绑定规则
如果交换机的类型为 fanout,routingkey设置为空值""
*/
channel.queueBind(queueName1, exchangerName, "errorLog");
channel.queueBind(queueName2, exchangerName, "infoLog");
channel.queueBind(queueName2, exchangerName, "warningLog");
//8.发送消息
String body1 = "日志信息[error]:又是不想当社畜的一天";
String body2 = "日志信息[info]:又是不想当社畜的一天";
String body3 = "日志信息[warning]:又是不想当社畜的一天";
channel.basicPublish(exchangerName, "infoLog", null, body2.getBytes());//第二个参数routingkey为空
channel.basicPublish(exchangerName, "errorLog", null, body1.getBytes());//第二个参数routingkey为空
channel.basicPublish(exchangerName, "warningLog", null, body3.getBytes());//第二个参数routingkey为空
//9.释放资源
channel.close();
connection.close();
}
}
运行生产者:
消息已经成功发送到了对应的routing key的队列
消费者
绑定对应的队列即可
以下是其中一个消费者的代码,另一个一摸一样,改一下绑定队列
package com.leolee.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @ClassName ConsumerPubSub1
* @Description: Routing模式
* @Author LeoLee
* @Date 2020/11/6
* @Version V1.0
**/
public class ConsumerRouting1 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//2.设置参数
connectionFactory.setHost("127.0.0.1");//默认值为localhost
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/LeoLee");//默认值为:/
connectionFactory.setUsername("LeoLee");
connectionFactory.setPassword("lyl512240816");
//3.创建连接 connection
Connection connection = connectionFactory.newConnection();
//4.创建channel
Channel channel = connection.createChannel();
//5.创建队列queue(由于简单模式不需要Exchange,其实是使用默认的交换机,所以消息直接入队列),消费者可以不需要创建队列,但是创建了也不影响
/*
String queue:队列名称
boolean durable:是否持久化
boolean exclusive: 有如下两个意义
是否独占,只有一个消费者监听这个队列
当connection关闭时,是否删除队列
boolean autoDelete:是否自动删除,当没有消费者的时候自动删除
Map<String, Object> arguments: 一些配置参数
*/
//如果没有一个名字叫Hello_world的队列,则会创建,如果存在该队列,则复用
//生产者已经生命过了队列,可以忽略该步骤
//channel.queueDeclare("workQueues", false, false, false, null);
String queueName1 = "test_direct_queue1";
//6.接收消息
/*
* String queue:队列名称
* boolean autoAck:是否自动确认,当消费者收到消息之后会自动给MQ一个回执,告诉MQ消息已经收到
* Consumer callback:回调方法
*/
Consumer consumer = new DefaultConsumer(channel){
/*
* 功能描述: <br>
* 〈回调方法〉当客户端收到消息并向MQ确认消息已经收到,将回调该方法
* @Param: [consumerTag消息唯一标识,
* envelope获取一些信息,包含交换机信息、routing key...等,
* properties配置信息,生产者发送消息时候的配置,
* body数据]
* @Return: void
* @Author: LeoLee
* @Date: 2020/11/5 11:56
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
/*System.out.println("consumerTag:" + consumerTag);
System.out.println("envelope.exchange:" + envelope.getExchange());
System.out.println("envelope.routingKey:" + envelope.getRoutingKey());
System.out.println("properties:" + properties);*/
System.out.println("消费者1从队列" + queueName1 + "接收到body:" + new String(body));
}
};
channel.basicConsume(queueName1, true, consumer);
//7.消费者不需要关闭资源,不然无法完成自动确认
}
}
运行两个绑定不同队列的消费者
可以看出,消息确实在交换机根据其绑定的routing key被分发到了指定的队列中