2.交换机与特殊队列
2.交换机
2.1.类型
1.FanoutExchange(扇形)
2.DirectExchange(直连)
3.TopicExchange(主题)
4.HeadersExchange(头部)
以下类型的交换机使用都会使用到这两个步骤
①选择依赖
②修改启动类
2.2.FanoutExchange
2.2.1.介绍
FanoutExchange: 扇形交换机
投递到所有绑定的队列,不需要路由键,不需要进行路由键的匹配,相当于广播、群发;
2.2.2.使用
①修改配置
②发送消息
③绑定交换机
2.3.DirectExchange
2.3.1.介绍
根据路由键精确匹配(一模一样)进行路由消息队列
error,info,warning称为路由key
2.3.2.使用
①修改配置类
②发送消息类
③绑定交换机
④访问测试
因为绑定的是info路由,queue.direct.a是error路由,所有没有信息,而queue.direct.b绑定了三个,所以什么路由key他都可以接收到
2.4.TopicExchange
2.4.1.介绍
只能匹配一个
通配符匹配,相当于模糊匹配;
:匹配多个单词,用来表示任意数量(零个或多个)单词
*:匹配一个单词(必须有一个,而且只有一个),用.隔开的为一个单词:
2.4.2.使用
①修改配置类
②绑定交换机
③发送消息
④访问测试
发送的路由key是lazy.orange.rabbit,所有队列a和b都可以接收到,但一个队列只能接收到一个信息,所以队列b也是1条信息
2.5.Headers Exchange (用的比较少)
2.5.1.介绍
这个是没有路由key,是由请求头进行绑定的
基于消息内容中的headers属性进行匹配
2.5.2.示例
绑定参考代码:
Map<String, Object> headerValues = new HashMap<>();
headerValues.put("type", "m");
headerValues.put("status", 1);
return BindingBuilder.*bind*(queueA).to(headersExchange).whereAll(headerValues).match();
发送参考代码
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("type", "m");
messageProperties.setHeader("status", 1);
Message message = new Message(msg.getBytes(), messageProperties);
// void convertAndSend(String exchange, String routingKey, Object message) throws AmqpException;
amqpTemplate.convertAndSend(RabbitConfig.*EXCHANGE*, null, message);
3.死信队列
3.1.介绍
一个用于存放无法被消费者处理的消息的特殊队列。当队列中的消息到达其过期时间或者出现错误时,RabbitMQ将尝试重新将该消息发送到队列中,但是如果重试次数超过阈值,则该消息将被移动到所配置的死信队列中,以便后续分析和处理。
队列的一些参数
DLX(Dead-Letter-Exchange):死信交换器,死信邮箱
3.2.队列过期
3.2.1.项目搭建
①选择依赖
②修改启动类
③修改配置
3.2.2.消费者类
3.2.3.绑定交换机
3.2.4.访问测试
刚打开时:正常队列是有一条信息的,20秒后,正常队列过期,变成为死信队列
3.3.消息过期
3.3.1.项目搭建
①选择依赖
②修改启动类
③修改配置
3.3.2.消费者类
3.3.3.绑定交换机
其他的和上面一样,只不过将这里的队列过期时间设置删除了
3.3.4.访问测试
刚打开时:正常队列是有两条信息的,20秒后,正常队列的消息过期,变成为死信队列
注意:当队列只有一条信息,并且已经过期了,当前队列也会变成死信队列
3.4.队列最大长度
3.4.1.项目搭建
①选择依赖
②修改启动类
③修改配置
3.4.2.消费者类
3.4.3.绑定交换机
其他的和上面一样,这里只是添加了队列的最大长度
3.44.4.访问测试
当正常队列超过五条信息时,其他的信息都会成为死信息
(这里有19个死信息不知道是哪里配置错了,发送了三次循坏)
是哪五个信息是正常信息
可以看到是先后面的五个信息是正常的信息
先进入的3个信息会成为死信息
3.5.自动确认接收消息
3.5.1.生产者搭建
其他的和之前一样
3.5.2.消费者搭建
没有绑定交换机这个,其他的和上面一样
3.5.3.访问测试
可以看到正常队列和死信队列都是没有信息的,因为一发送就已经被接收并且确认了
3.6.手动接收消息
3.6.1.手动确认接收消息
只需要修改接收消息端的部分代码即可
①修改代码
②访问测试
3.6.2.不重新投递
在接收消息这里添加一个错误
然后进行查看
3.6.3.重新投递
还是模拟刚才的错误,但是将是否重新投递修改为true
此时消息就会一直进行重新投递,不会进行确认也不会变成死信息
4.延迟队列
4.1.介绍
一种特殊的队列,它可以在到达指定的时间后将消息传递到消费者。这是通过为消息设置过期时间或 TTL(Time-to-Live)来实现的。
4.2.定时任务方式
每隔3秒扫描一次数据库,查询过期的订单然后进行处理
优点:简单,容易实现
缺点
- 存在延迟(延迟时间不准确),如果每隔1分钟扫一次,那么就有可能延迟1分钟;
- 性能较差,每次扫描数据库,如果订单量很大
4.3.被动取消
当用户查询订单的时候,判断订单是否超时,超时了就取消(交易关闭)
优点:对服务器而言,压力小
缺点
- 用户不查询订单,将永远处于待支付状态,会对数据统计等功能造成影响
- 用户打开订单页面,有可能比较慢,因为要处理大量订单,用户体验少稍差
4.4.JDK延迟队列
DelayedQueue
无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素
优点:实现简单,任务延迟低
缺点
- 服务重启、宕机,数据丢失
- 只适合单机版,不适合集群
- 订单量大,可能内存不足而发生异常(oom)
- OOM(Out Of Memory)指的是操作系统或应用程序因内存耗尽导致崩溃的情况。当一个进程运行时占用了太多的内存并且没有足够的空间对其进行处理,就会产生 OOM 错误。
- 在操作系统中,当可用内存不足时,系统会尝试通过交换(将内存中不活跃的页面移动到磁盘上)来腾出更多内存。但是,如果可用空间仍然不足或交换本身也出现问题,则可能会导致 OOM 错误。
4.5.使用消息中间件(RabbitMQ)
RabbitMQ本身不支持延迟队列,可以使用TTL结合DLX的方式来实现消息的延迟投递,即把DLX跟某个队列绑定,到了指定时间,消息过期后,就会从DLX路由到这个队列,消费者可以从这个队列取走消息。
这种方式有一个问题:如果先发送的消息延迟时间比后面的长,就会影响后面的延迟时间段的消息的消费
解决方法是:不同延迟时间的消息要发到不同的队列上,同一个队列的消息,它的延迟时间应该一样
不过写起来代码会有些繁琐,每一个不同的时间段都要创建一个不同的队列
4.6.延迟插件
rabbitmq-delayed-message-exchange
4.6.1.安装
在rabbitmq下的plugins目录安装插件
开启插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
查看插件
rabbitmq-plugins list
4.6.2.原理
消息发送后不会直接投递到队列,而是存储到 Mnesia(嵌入式数据库),检查 x-delay 时间(消息头部)
延迟插件在 RabbitMQ 3.5.7 及以上的版本才支持,依赖 Erlang/OPT 18.0 及以上运行环境
Mnesia 是一个小型数据库,不适合于大量延迟消息的实现,解决了消息过期时间不一致出现的问题。
4.6.3.使用
①选择依赖
②修改启动类
③修改配置类
④绑定交换机
⑤发送消息
⑥接收消息
⑦访问测试
又简单又方便
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构