RabbitMQ的模式介绍
图注解释
P:生产者
C:消费者
X:交换机
- 入门级程序
生产者->队列->消费者
下面开始简单生产者、消费者的消息发送与接收入,工作原理如下图
1.引入jar包
<!--rabbitmq-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.1.0</version>
</dependency>
2.生产者代码示例
/**
* 生产者
*/
public class ProducerConfig {
/**
* 简单模式生产
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 是否删除,当没有consumer时,自动删除
*
*/
//创建队列(如果没有一个名字叫hello-world的队列,则会创建该队列,如果有则不会创建)
channel.queueDeclare("hello_world", true, false, false, null);
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* exchange :交换机的名称,简单模式下交换机会使用默认的“”
* 2routingKey 路由名称
* props 配置信息
* body:发送消息数据
*/
String body = "你好" + 1;
//发送消息
channel.basicPublish("", "hello_world", null, body.getBytes());
channel.close();
conn.close();
}
}
代码运行结果可以在RabbitMQ的可视化界面看到一个相关队列
3.消息已经生成开始消费者准备消费消息(消费者)示例:
/**
* 消费者
*/
public class ConsumerConfig {
/**
* 简单模式的消费
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 是否删除,当没有consumer时,自动删除
*
*/
//创建队列(如果没有一个名字叫hello-world的队列,则会创建该队列,如果有则不会创建)
channel.queueDeclare("hello_world",true,false,false,null);
//接收消息
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerTag:"+consumerTag);
System.out.println("envelope:"+envelope);
System.out.println("routingkey:"+envelope.getRoutingKey());
System.out.println("properties:"+properties);
System.out.println("body:"+new String(body));
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对象
*/
channel.basicConsume("hello_world",true,consumer);
}
}
执行结果可以看出消息被消费的:
- Work queues工作队列模式
1.模式说明
work Queues :与入门程序的简单模式想比,多了一个或一些消费端,多个消费端共同消费同一个队列的消息(提高了消息的消费速率)
应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
1.生产者代码示例
public class ProducerWorkQueues {
/**
* 工作队列
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 是否删除,当没有consumer时,自动删除
*
*/
//创建队列(如果没有一个名字叫hello-world的队列,则会创建该队列,如果有则不会创建)
channel.queueDeclare("work_queues", true, false, false, null);
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* exchange :交换机的名称,简单模式下交换机会使用默认的“”
* 2routingKey 路由名称
* props 配置信息
* body:发送消息数据
*/
//发10条消息
for (int i=0;i<10;i++) {
String body = i+"你好";
//发送消息
channel.basicPublish("", "work_queues", null, body.getBytes());
}
channel.close();
conn.close();
}
}
2.消费者1
public class ConsumerWorkQueues {
/**
* 工作队列模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 是否删除,当没有consumer时,自动删除
*
*/
//创建队列(如果没有一个名字叫hello-world的队列,则会创建该队列,如果有则不会创建)
channel.queueDeclare("work_queues",true,false,false,null);
//接收消息
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对象
*/
channel.basicConsume("work_queues",true,consumer);
}
}
3.消费者2
public class ConsumerWorkQueuesNext {
/**
* 工作队列模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
/**
* queueDeclare(String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments)
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 是否删除,当没有consumer时,自动删除
*
*/
//创建队列(如果没有一个名字叫hello-world的队列,则会创建该队列,如果有则不会创建)
channel.queueDeclare("work_queues",true,false,false,null);
//接收消息
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对象
*/
channel.basicConsume("work_queues",true,consumer);
}
}
执行结果
消费者1
消费者2
- Pub/Sub订阅模式
1.模式说明
P 生产者:也就是发送消息的程序,但是不是再发送到队列中,而是发送到交换机中(X)
C 消费者:消息的接受者,会一直等待消息的到来。
Queue 队列:消息队列,接受消息,缓存消息。
Exchange 交换机(X):一方面,接受生产者发送的消息。另一方面,知道如何处理消息,列如传递消息到特定的队列、传递给所有队列、或者将消息丢弃。如何操作具体取决于交换机的类型,交换机的常用类型如下所示:
Fanout:广播模式,将消息交给所有绑定到交换的队列。
Direct:定向,把消息交给符合指定的routing key的队列
Topic:通配符,把消息交给符合routing pattern (路由模式) 的队列
交换机只负责转发消息,不具备存储消息的能力,如果没有任何队列与交换机进行绑定或者没有符合规则的路由键,者消息会丢失!
1.生产者示例
public class ProducerPubSub {
/**
* 简单模式生产
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection connection = factory.newConnection();
//获得信道
Channel channel = connection.createChannel();
/*
exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException {
exchange :交换机名称
BuiltinExchangeType:交换机类型
DIRECT("direct"), 定向
FANOUT("fanout"),扇形 (广播) 发送消息到每一个与之绑定队列
TOPIC("topic"), 通配符的方式
HEADERS("headers"); 参数匹配
durable:是否持久化
autoDelete: 自动删除
internal 内部使用 一般false
arguments参数
*/
String exachangeName="test_fanout";
//创建交换机
channel.exchangeDeclare(exachangeName, BuiltinExchangeType.FANOUT,true,false,false,null);
/**
* String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 参数
*
*/
//创建队列
String queue1Name="test_fanout_queue1";
String queue2Name="test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
/*
queueBind(String queue, String exchange, String routingKey) throws IOException {
queue :队列名称
exchange:交换机名称
routingKey:路由键 ,绑定规则
如果交换机类型为 FANOUT ,routingKey设置为“”
*/
//绑定队列和交换机
channel.queueBind(queue1Name,exachangeName,"");
channel.queueBind(queue2Name,exachangeName,"");
/**
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange :交换机的名称,简单模式下交换机会使用默认的“”
* 2routingKey 路由名称
* props 配置信息
* body:发送消息数据
*/
String body="日志信息:天气晴朗";
//发送消息
channel.basicPublish(exachangeName,"",null,body.getBytes());
//释放资源
channel.close(); //关闭信道
connection.close();//关闭连接
}
}
2.消费者1
public class ProducerPubSub {
/**
* 简单模式生产
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection connection = factory.newConnection();
//获得信道
Channel channel = connection.createChannel();
/*
exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException {
exchange :交换机名称
BuiltinExchangeType:交换机类型
DIRECT("direct"), 定向
FANOUT("fanout"),扇形 (广播) 发送消息到每一个与之绑定队列
TOPIC("topic"), 通配符的方式
HEADERS("headers"); 参数匹配
durable:是否持久化
autoDelete: 自动删除
internal 内部使用 一般false
arguments参数
*/
String exachangeName="test_fanout";
//创建交换机
channel.exchangeDeclare(exachangeName, BuiltinExchangeType.FANOUT,true,false,false,null);
/**
* String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 参数
*
*/
//创建队列
String queue1Name="test_fanout_queue1";
String queue2Name="test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
/*
queueBind(String queue, String exchange, String routingKey) throws IOException {
queue :队列名称
exchange:交换机名称
routingKey:路由键 ,绑定规则
如果交换机类型为 FANOUT ,routingKey设置为“”
*/
//绑定队列和交换机
channel.queueBind(queue1Name,exachangeName,"");
channel.queueBind(queue2Name,exachangeName,"");
/**
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange :交换机的名称,简单模式下交换机会使用默认的“”
* 2routingKey 路由名称
* props 配置信息
* body:发送消息数据
*/
String body="日志信息:天气晴朗";
//发送消息
channel.basicPublish(exachangeName,"",null,body.getBytes());
//释放资源
channel.close(); //关闭信道
connection.close();//关闭连接
}
}
在可视化rabbitMq中可以看到我们创建的交换机和绑定的两个队列
2.消费者1
public class ConsumerPubSub {
/**
* 简单模式的消费
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
String queue1Name="test_fanout_queue1";
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("消费者1:将日志打印到控制台。。。。。。。。。。。。。");
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对消
*/
//接收消息
channel.basicConsume(queue1Name,true,consumer);
}
}
消费者1控制台
3.消费者2
public class ConsumerPubSubNext {
/**
* 简单模式的消费
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
String queue2Name="test_fanout_queue2";
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("消费者2:将数据保存到数据库中。。。。。。。。。。");
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对消
*/
//接收消息
channel.basicConsume(queue2Name,true,consumer);
}
}
消费者2控制台
- Routing 路由模式
1.队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)
2.消息的发送方在指定交换机的时候也必须绑定路由键
3.交换机不能将消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的RoutingKey与消息的RoutingKey完全一致,才会接收到消息
注:上图的两个地方的RoutingKey一致的时候才能存放到队列中
1.生产者代码
public class ProducerRoutingKey {
/**
* 路由模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection connection = factory.newConnection();
//获得信道
Channel channel = connection.createChannel();
/*
exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException {
exchange :交换机名称
BuiltinExchangeType:交换机类型
DIRECT("direct"), 定向
FANOUT("fanout"),扇形 (广播) 发送消息到每一个与之绑定队列
TOPIC("topic"), 通配符的方式
HEADERS("headers"); 参数匹配
durable:是否持久化
autoDelete: 自动删除
internal 内部使用 一般false
arguments参数
*/
String exachangeName="test_direct";
//创建交换机
channel.exchangeDeclare(exachangeName, BuiltinExchangeType.DIRECT,true,false,false,null);
/**
* String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 参数
*
*/
//创建队列
String queue1Name="test_direct_queue1";
String queue2Name="test_direct_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
/*
queueBind(String queue, String exchange, String routingKey) throws IOException {
queue :队列名称
exchange:交换机名称
routingKey:路由键 ,绑定规则
如果交换机类型为 FANOUT ,routingKey设置为“”
*/
//绑定队列和交换机
//队列1的绑定
channel.queueBind(queue1Name,exachangeName,"error");
//队列2的绑定
channel.queueBind(queue2Name,exachangeName,"info");
channel.queueBind(queue2Name,exachangeName,"error");
channel.queueBind(queue2Name,exachangeName,"warning");
/**
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange :交换机的名称,简单模式下交换机会使用默认的“”
* 2routingKey 路由名称
* props 配置信息
* body:发送消息数据
*/
String body="日志信息:天气晴朗";
//发送消息
channel.basicPublish(exachangeName,"info",null,body.getBytes());
//释放资源
channel.close(); //关闭信道
connection.close();//关闭连接
}
}
生产者执行之后可以在可视化界面上看到
2.消费者1
public class ConsumerRoutingKey {
/**
* 路由模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
String queue1Name="test_direct_queue1";
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("消费者1:将日志打印到控制台。。。。。。。。。。。。。");
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对消
*/
//接收消息
channel.basicConsume(queue1Name,true,consumer);
}
}
3.消费者2
public class ConsumerRoutingKeyNext {
/**
* 路由模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
String queue1Name="test_direct_queue2";
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("消费者1:将日志打印到控制台。。。。。。。。。。。。。");
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对消
*/
//接收消息
channel.basicConsume(queue1Name,true,consumer);
}
}
分别执行消费者1和消费者2
只有消费者2的控制台打印了消息,说明路由器将消息通过路由转发到了消费者队列2中
- Topics 通配符模式
topic模式也称为主题模式,其实他相对于routing模式最大的好处就是他多了一种匹配模式的路由,怎么理解匹配呢,其实就相当于我们之前正则的.*这种,不过他的匹配机制可能不是这种(其实除了匹配规则外,他的作用就和routing模式一样 ),而他的工作流程图如下:
他的匹配规则:
绑定键binding key也必须是这种形式。以特定路由键发送的消息将会发送到所有绑定键与之匹配的队列中。但绑定键有两种特殊的情况:
①*(星号)仅代表一个单词
②#(井号)代表任意个单词
示例:
以上图为例:
.orange. : 匹配以 任意一个单词字符开头中间包含 .orange. 以任意一个单词字符结尾 的字符串。比如 a.orange.b, sdfsd.orange.fdsfsdf 等(注意是一个单词)。
lay.# :只要一lay.开头的都匹配,他可以匹配lay.a, lay.a.b, lay.b.c等。
这样是不是很方便,比如我们想将log的发给q1队列,其他的发给q2,那么我们只需要定义log.#、或者log.*,那么你发送给q1队列的数据就是log日志的消息。
1.生产者
public class ProducerTopics {
/**
* 通配符模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection connection = factory.newConnection();
//获得信道
Channel channel = connection.createChannel();
/*
exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException {
exchange :交换机名称
BuiltinExchangeType:交换机类型
DIRECT("direct"), 定向
FANOUT("fanout"),扇形 (广播) 发送消息到每一个与之绑定队列
TOPIC("topic"), 通配符的方式
HEADERS("headers"); 参数匹配
durable:是否持久化
autoDelete: 自动删除
internal 内部使用 一般false
arguments参数
*/
String exachangeName="test_topic";
//创建交换机
channel.exchangeDeclare(exachangeName, BuiltinExchangeType.TOPIC,true,false,false,null);
/**
* String queue, boolean durable, boolean exclusive,
* boolean autoDelete, Map<String, Object> arguments
*
* 1queue 队列名称
* 2durable 是否持久化
* 3exclusive 是否独占 ,只能有一个消费者监听这个队列
* 当connection 关闭时,是否删除队列
* 4autoDelete 参数
*
*/
//创建队列
String queue1Name="test_topics_queue1";
String queue2Name="test_topics_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
/*
queueBind(String queue, String exchange, String routingKey) throws IOException {
queue :队列名称
exchange:交换机名称
routingKey:路由键 ,绑定规则
如果交换机类型为 FANOUT ,routingKey设置为“”
*/
//绑定队列和交换机
//routing key 系统名称,日志的级别
//需求 :所有error级别的日志存入数据库,所有的order系统的日志存入数据库
channel.queueBind(queue1Name,exachangeName,"#.error");
channel.queueBind(queue1Name,exachangeName,"order.*");
channel.queueBind(queue2Name,exachangeName,"*.*");
/**
* String exchange, String routingKey, BasicProperties props, byte[] body
* exchange :交换机的名称,简单模式下交换机会使用默认的“”
* 2routingKey 路由名称(队列名称)
* props 配置信息
* body:发送消息数据
*/
String body="日志信息:天气晴朗 日志级别 info";
//发送消息
channel.basicPublish(exachangeName,"order.info",null,body.getBytes());
//释放资源
channel.close(); //关闭信道
connection.close();//关闭连接
}
}
可视化RabbitMq可以看到绑定的
2.消费者
public class ConsumerTopics {
/**
* 通配符模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
String queue1Name="test_topics_queue1";
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("消费者1:将数据保存到数据库中。。。。。。。。。。");
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对消
*/
//接收消息
channel.basicConsume(queue1Name,true,consumer);
}
}
3.消费者2
public class ConsumerTopicsNext {
/**
* 通配符模式
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672); //端口默认值5672
//设置 RabbitMQ 地址
factory.setHost("127.0.0.1");
//建立到代理服务器到连接
Connection conn = factory.newConnection();
//获得信道
Channel channel = conn.createChannel();
String queue2Name="test_topics_queue2";
Consumer consumer=new DefaultConsumer(channel){
//回调方法 ,当收到消息后,会自动执行该方法
/**
*
* @param consumerTag 标识
* @param envelope 获取一些信息,交换机,路由。。。
* @param properties 配置信息
* @param body 数据
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("消费者2:将数据保存到数据库中。。。。。。。。。。");
}
};
/**
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue 队列名称
* autoAck 是否自动确认
* callback 回调对消
*/
//接收消息
channel.basicConsume(queue2Name,true,consumer);
}
}
控制台
消费者1
消费者2
总结:
简单模式
生产者->队列->消费者(其实有一个默认的交换机)
工作队列模式
生产者->队列->消费者1(多个)默认一个交换机
消费的时候是轮询消费
订阅模式
FANOUT(广播)模式
生产者->交换机->队列->消费者
可以指定交换机(交换机类型不同)
不需要指定key
交换机会通过路由传递到绑定的队列中
RoutingKey(路由模式)
只有生参者与交换机绑定的routingKey和交换机与队列绑定的routingKey一致时才能传递到队列中
Toupic(通配符模式)
有两种符号
"*"表示匹配一个单词
"#"表示匹配多个单词