发布订阅模式与之前案例的区别就是允许将一个消息发送给多个消费者,实现方式时加入exchange(交换机)
常见exchange类型包括:
-
Fanout:广播
-
Direct:路由
-
Topic:话题
注意:exchange负责消息路由,而不是存储,路由失败则消息丢失
consumer服务:消息的消费者
publisher服务:消息的发送者
发布订阅-Fanout Exchange(路由)
Fanout Exchange 会将接收到的消息路由到每一个跟其绑定的queue,称为广播模式
案例 利用SpringAMQP演示Fanout Exchange的使用
实现思路如下:
1.在consumer服务中,利用代码声明队列,交换机,并将两者绑定
2.在consumer服务中,编写两个消费者方法,分别监听fanout.queue1和fanout.queue2
3.在publisher中编写测试方法,向jhj.fanout(交换机)发送消息
步骤1:consumer服务声明Exchange,Queue,Binding
在consumer服务常见一个类,添加@Configuration,并声明FanoutExchange,Queue和绑定关系对象Binding,代码如下:
@Configuration
public class FanoutConfig {
//jhj.fanout 交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("jhj.fanout");
}
//fanout.queue1 消息队列
@Bean
public Queue fanoutQueue1(){
return new Queue("fanout.queue1");
}
//绑定队列1到交换机
@Bean
public Binding fanoutBinding1(Queue fanoutQueue1,FanoutExchange fanoutExchange){
return BindingBuilder
.bind(fanoutQueue1)
.to(fanoutExchange);
}
//fanout.queue2
@Bean
public Queue fanoutQueue2(){
return new Queue("fanout.queue2");
}
//绑定队列2到交换机
@Bean
public Binding fanoutBinding2(Queue fanoutQueue2,FanoutExchange fanoutExchange){
return BindingBuilder
.bind(fanoutQueue2)
.to(fanoutExchange);
}
}
步骤2:在consumer服务声明两个消费者
@RabbitListener(queues = "fanout.queue1")
public void listenFandQueue1(String msg) {
System.err.println("欢迎紬紬到来(fandout.queue1): " + msg + "耗时: " + LocalTime.now());
}
@RabbitListener(queues = "fanout.queue2")
public void listenFandQueue2(String msg) {
System.err.println("欢迎紬紬到来(fandout.queue2): " + msg + "耗时: " + LocalTime.now());
}
步骤3:在publisher服务发送消息到FanoutExchange
@Test
public void testSendFanoutExchange(){
// 交换机名称
String exchangeName = "jhj.fanout";
// 消息
String message = "hello,k-on!";
// 发送消息,参数分别为:交互机名称,RoutingKey(暂时为空),消息
rabbitTemplate.convertAndSend(exchangeName,"",message);
}
交换机的作用是什么?
-
接收publisher发送的消息
-
将消息按照规则路由到与之绑定的队列
-
不能缓存消息,路由失败,消息丢失
-
FanoutExchange的会将消息路由到每个绑定的队列
声明队列、交换机、绑定关系的Bean是什么?
-
Queue
-
FanoutExchange
-
Binding
发布订阅-Direct Exchangei(广播)
Direct Exchangei 会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式(routes)
-
每一个Queue都与Exchange设置一个BindingKey
-
发送者发送消息时,指定消息的RoutingKey
-
Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
-
案例 利用SpringAMQP演示DirectExchange的使用
实现思路如下:
1.利用@RabbitListener声明Exchange,Queue,RoutingKey
2.在consumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2
3.在publisher中编写测试方法,向jhj.direct发送消息
步骤1:在consumer服务声明Exchange,Queue,作为消费者
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "jhj.direct",type = ExchangeTypes.DIRECT),
key = {"red","blue"}
))
//value指定队列名称(没有则生成),exchange指定交换机名称,并与队列绑定,typ指定为direct模式(广播模式,默认就是direct模式),key指定队列只能接收与key值相关的消息
public void listenDirectQueue1(String msg){
System.err.println("欢迎紬紬到来(direct.queue1): " + msg + "耗时: " + LocalTime.now());
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "jhj.direct",type = ExchangeTypes.DIRECT),
key = {"red","yellow"}
))
public void listenDirectQueue2(String msg){
System.err.println("欢迎紬紬到来(direct.queue2): " + msg + "耗时: " + LocalTime.now());
}
步骤2:在publisher服务发送消息到DirectExchange
@Test
public void testSendDirectExchange(){
// 交换机名称
String exchangeName = "jhj.direct";
// 消息
String message = "hello,k-on blue!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName,"blue",message);
}
描述下Direct交换机与Fanout交换机的差异?
-
Fanout交换机将消息路由给每一个与之绑定的队列
-
Direct交换机根据RoutingKey判断路由给哪个队列
-
如果多个队列具有相同的RoutingKey,则与Fanout功能类似
基于@RabbitListener注解声明队列和交换机有哪些常见注解?
-
@Queue
-
@Exchange
发布订阅-Topoc Exchange(话题)
Topoc Exchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以 . 分割
Queue与Exchange指定BindingKey时可以使用通配符:
#:代指0个或多个单词
*:代指一个单词
案例 利用SpringAMQP演示TopicExchange的使用
实现思路如下:
1.利用@RabbitListener声明Exchange,Queue,RoutingKey
2.在consumer服务中,编写两个消费者方法,分别监听topic.queue1和topic.queue2
3.在publisher中编写测试方法,向jhj.topic发送消息
步骤1:在consumer服务声明Exchange,Queue
@Component
public class SpringRabbitListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"),
exchange = @Exchange(name = "jhj.topic",type = ExchangeTypes.TOPIC),
key = "china.#"
))
public void listenTopicQueue1(String msg){
System.err.println("欢迎紬紬到来(topic.queue1): " + msg + "耗时: " + LocalTime.now());
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue2"),
exchange = @Exchange(name = "jhj.topic",type = ExchangeTypes.TOPIC),
key = "#.news"
))
public void listenTopicQueue2(String msg){
System.err.println("欢迎紬紬到来(topic.queue2): " + msg + "耗时: " + LocalTime.now());
}
}
步骤2:在publisher服务发送消息到TopicExchange
@Test
public void testSendTopicExchange(){
// 交换机名称
String exchangeName = "jhj.topic";
// 消息
String message = "hello,k-on china.news!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName,"china.news",message);
}
描述下Direct交换机与Topic交换机的差异?
-
Topic交换机接收的消息RoutingKey必须是多个单词,以
**.**
分割 -
Topic交换机与队列绑定时的bindingKey可以指定通配符
-
#
:代表0个或多个词 -
*
:代表1个词