rabbitmq相关
工作队列模型
workQueue,多个消费者绑定到一个队列,当队列堆积消息时,可使用work模型。
多个消费者绑定一个队列,同一条消息只会被一个消费者处理
通过设置prefetch来控制消费者预取的消息数量
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息
发布订阅模型
角色类型如下:
publisher,生产者
exchange,交换机,负责转发消息,不负责存储消息的能力
交换机类型如下:
Fanout,广播,将消息交给所有绑定交换机的队列
Direct,定向,把消息交给符合制定routing key的队列
Topic,通配符,把消息交给符合routing pattern的队列
consumer,消费者
queue,消息队列
在广播模式下,消息发送流程是这样的: - 1) 可以有多个队列 - 2) 每个队列都要绑定到Exchange(交换机) - 3) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定 - 4) 交换机把消息发送给绑定过的所有队列 - 5) 订阅队列的消费者都能拿到消息
生产者代码
public static final String EXCHANGE_NAME = "fanout_exchange"; public static final String QUEUE_NAME = "fanout_queue1"; @Bean("fanout_exchange")//交换机 public Exchange exchange(){ return ExchangeBuilder.fanoutExchange(EXCHANGE_NAME).durable(true).build(); } @Bean("fanout_queue1")//消息队列 public Queue queue(){ return QueueBuilder.durable(QUEUE_NAME).build(); } @Bean//绑定交换机和消息队列1 public Binding binding(@Qualifier("fanout_exchange")Exchange exchange,@Qualifier("fanout_queue1")Queue queue){ return BindingBuilder.bind(queue).to(exchange).with("").noargs(); } @Bean("fanout_queue2")//消息队列2 public Queue queue2(){ return QueueBuilder.durable("fanout_queue2").build(); } @Bean//绑定交换机和消息队列2 public Binding binding2(@Qualifier("fanout_exchange")Exchange exchange,@Qualifier("fanout_queue2")Queue queue){ return BindingBuilder.bind(queue).to(exchange).with("").noargs(); }
发送消息
@Test void fanout_exchangeTest() { rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_NAME,"","fanout,这是在广播..."); }
消费者
@RabbitListener(queues = "fanout_queue1") public void listenFanoutQueue1(Message message) { System.out.println("消费者1收到:"+new String(message.getBody())); } @RabbitListener(queues = "fanout_queue2") public void listenFanoutQueue2(Message message) { System.out.println("消费者2收到:"+new String(message.getBody())); }
------------------------------------------------------------------------------------------------------------------------------------ --------------
在Direct模型下: - 队列与交换机的绑定,不能是任意绑定了,而是要指定一个`RoutingKey`(路由key) - 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 `RoutingKey`。 - Exchange不再把消息交给每一个绑定的队列,而是根据消息的`Routing Key`进行判断,只有队列的`Routingkey`与消息的 `Routing key`完全一致,才会接收到消息
基于@Bean的方式声明队列和交换机比较麻烦,Spring还提供了基于注解方式来声明。
在consumer的SpringRabbitListener中添加两个消费者,同时基于注解来声明队列和交换机:
@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct_queue1"), exchange = @Exchange(name = "direct_exchange", type = ExchangeTypes.DIRECT), key = {"red", "blue" })) public void listenDirectQueue1(String msg) { System.out.println("消费者1收到direct_queue1的消息" + msg); } @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct_queue2"), exchange = @Exchange(name = "direct_exchange", type = ExchangeTypes.DIRECT), key = {"red", "yellow" })) public void listenDirectQueue2(String msg) { System.out.println("消费者2收到direct_queue2的消息" + msg); }
生产者发送消息
@Test void direct_exchangeTest() { String exchangeName="direct_exchange"; String message="由于排放核污水,日本出现哥斯拉"; // rabbitTemplate.convertAndSend(exchangeName,"red",message); rabbitTemplate.convertAndSend(exchangeName,"blue",message); }
------------------------------------------------------------------------------------------------------------------------------------ --------------
`Topic`类型的`Exchange`与`Direct`相比,都是可以根据`RoutingKey`把消息路由到不同的队列。只不过`Topic`类型`Exchange`可以让队列在绑定`Routing key` 的时候使用通配符!
`Routingkey` 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: `item.insert`
通配符规则:
`#`:匹配一个或多个词
`*`:匹配不多不少恰好1个词
举例:
`item.#`:能够匹配`item.spu.insert` 或者 `item.spu`
`item.*`:只能匹配`item.spu`
添加两个消费者,同时声明队列和交换机
@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "topic.queue1"), exchange = @Exchange(name = "topic.exchange",type = ExchangeTypes.TOPIC),key = "china.#")) public void listenTopicQueue1(String msg){ System.out.println("消费者1收到topic_queue1的消息" + msg); } @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "topic.queue2"), exchange = @Exchange(name = "topic.exchange",type = ExchangeTypes.TOPIC),key = "#.news")) public void listenTopicQueue2(String msg){ System.out.println("消费者2收到topic_queue2的消息" + msg); }
生产者
@Test void topic_exchangeTest() { String exchangeName="topic.exchange"; String message="喜报!xxx中了1000万彩票"; rabbitTemplate.convertAndSend(exchangeName,"china.news",message); }
消息转换器
Spring会把你发送的消息序列化为字节发送给MQ,接收消息的时候,还会把字节反序列化为Java对象。
默认情况下Spring采用的序列化方式是JDK序列化。
.配置JSON转换器
JDK序列化方式并不合适。我们希望消息体的体积更小、可读性更高,因此可以使用JSON方式来做序列化和反序列化。
在publisher和consumer两个服务中都引入依赖:
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.9.10</version> </dependency>
配置消息转换器。
在启动类中添加一个Bean即可:
@Bean public MessageConverter jsonMessageConverter(){ return new Jackson2JsonMessageConverter(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!