中间件之RabbitMQ进阶篇

RabbitMQ-过期时间TTL

概述

TTL 表示消息的过期时间,当这条消息小于过期时间,那么消费者可以正常获取,否则一旦超过这个过期时间消息将会被删除

TTL设置对象

目前可针对消息和队列设置TTL,通过队列属性设置后,队列所有的消息都将拥有相同的过期时间,也可以对每条消息单独设置不同的过期时间(若两者同时设置TTL,那么以其中TTL较小者为准),若消息队列中消息一旦超过TTL,则一般会将其投递到死信队列;而针对单独一条消息设置的TTL则没有此功能,将会被删除

TTL代码实现示例

TTL-队列

        TTL队列配置类(设置了队列过期时间为5s)

中间件之RabbitMQ进阶篇_第1张图片 TTL配置类-direct-routing

           TTL队列-producer服务

中间件之RabbitMQ进阶篇_第2张图片 TTL-队列生产者业务

TTL-Message       

         TTL-Message配置类

中间件之RabbitMQ进阶篇_第3张图片 TTL-Message配置类-direct-routing

         TTL-Message-producer服务(设置了消息过期时间为5s)

中间件之RabbitMQ进阶篇_第4张图片 TTL-Message生产者业务

RabbitMQ-死信队列

概述

DLX-Dead Letter Exchange,死信交换机也称为死信邮箱,当消息在一个队列中变成死信之后,它会被重新投递到一个交换机DLX中,通过绑定这个DLX的队列就称之为死信队列,导致死信的原因包括消息被拒绝,消息过期,队列达到最大长度。

死信队列应用代码实现示例

消息过期+死信队列(也称为延迟队列)                

        TTL-死信队列配置类 

中间件之RabbitMQ进阶篇_第5张图片 dead-direct-routing

        TTL-队列配置类(关联死信队列配置类,这样过期的消息将被投递到死信队列)

中间件之RabbitMQ进阶篇_第6张图片 TTL配置类-direct-routing

提示:队列的args在调用put增加或者减少参数(属性)时,不会同步更新已有的broker,所以我们在编写配置类相关参数之前一定要想好再写入,否则参数变更会加大维护成本 

        TTL队列-producer服务

中间件之RabbitMQ进阶篇_第7张图片 TTL-队列生产者业务

         测试结果如下

中间件之RabbitMQ进阶篇_第8张图片

队列达到最大长度+死信队列  

        当TTL-队列配置类的队列增加如下参数

        那么若发送到消息队列中消息个数为11,则多余的6个消息将转移到死信队列

RabbitMQ-内存磁盘

概述

        当内存使用超过配置的阈值或者磁盘剩余空间小于配置的磁盘空间阈值时,RabbitMQ会阻塞客户端连接,并停止接收客户端发来的消息 ,所以对于消息持久化,我们必须保证它们的空间大小时刻处于一种满足消息队列持久化的状态

        Rabbit内存和硬盘实时状态如下

关注RabbitMQ网页端Memory和Disk space状态

内存换页 

        在broker节点内存阻塞生产者之前,它会尝试将队列中消息换页到磁盘以释放内存空间,持久化以及非持久化消息都会写入磁盘,由于持久化消息本身在磁盘中存在一个副本,所以在转移过程中优先清除掉内存中持久化的消息

        提示:默认情况下,内存到达内存阈值的50%就会换页处理

设置空间大小

           a.通过命令设置内存/磁盘

设置内存 设置磁盘

           b.通过配置文件设置内存/磁盘

中间件之RabbitMQ进阶篇_第9张图片 设置内存

设置磁盘

        提示:

        1. 一般内存设置服务器内存的0.5左右,比如服务器内存6G,那么它的绝对值就是3G

        2.磁盘默认剩余空间大小为50m时进行预警,此时停止内存消息换页到磁盘

RabbitMQ-分布式事务

概述

分布式事务指的就是不同节点上的服务(即跨JVM)之间在进行通信的过程中保证各个节点的操作要么全部成功,要么全部失败,即保证事务的ACID,实现最终数据一致性

典型的分布式事务解决方案

  1. 两阶段提交(2PC):引入协调者协调参与者行为,根据协调者返回结果决定是否执行事务。存在的问题:同步阻塞,数据不一致,没有完善的容错机制
  2. TCC补偿事务:针对每个操作Try->Confirm->Cancel,都注册了与其对应的确认和补偿(撤销)操作。存在问题:需要开发者针对操作写很多补偿代码
  3. 本地消息表:将分布式事务中一方完成操作写入业务数据表的同时也写到本地消息表,然后通过Kafka或者Rabbit传递消息,通过异步确保数据一致性。存在问题:消息表耦合在业务中
  4. MQ事务消息:通用性强,扩展性高。存在问题:仅适合异步场景,消息会延迟处理

RabbitMQ-分布式事务

可靠生产

中间件之RabbitMQ进阶篇_第10张图片 分布式事务场景

中间件之RabbitMQ进阶篇_第11张图片 分布式事务实现流程-可靠生产 中间件之RabbitMQ进阶篇_第12张图片 可靠生产

        提示:由于消息中间件因网络抖动或者其他原因导致异常,可能无法接收到消息,所以在本地创建一个消息冗余DB,采取冗余机制来保证消息正确的投递到了消息队列中,保证可靠生产(其中一般会结合定时器重发消息队列未接收的消息,若达到重发次数还有问题,可能是消息存在问题,后续可能需要人工排查)

关键代码实现

中间件之RabbitMQ进阶篇_第13张图片 同时存储到业务数据表和消息冗余表

在yml中需要开启发布确认

中间件之RabbitMQ进阶篇_第14张图片 消息推送到消息队列的确认机制

可靠消费

中间件之RabbitMQ进阶篇_第15张图片 可靠消费流程图

        关键代码

中间件之RabbitMQ进阶篇_第16张图片 为业务绑定deadQueue

中间件之RabbitMQ进阶篇_第17张图片 监听业务队列获取消息

中间件之RabbitMQ进阶篇_第18张图片 出现异常则将消息转移到deadQueue

消费过程说明:消费者监听消息队列,获取消息后进行业务处理,当业务处理发生异常后,不进行重发机制直接将异常消息转移到死信队列,释放我们消息队列空间

当然,我们也可以对死信队列进行监听消费,根据公司业务进行相应的处理

中间件之RabbitMQ进阶篇_第19张图片 监听死信队列

Rabbit学习过程中补充的知识点

  1. 在声明队列的durable参数为ture表示持久化,将消息存储到本地硬盘且队列不会消失,为false时则表示非持久化,也会存储数据到硬盘,但当rabbit服务重启后队列会删除导致数据丢失。
  2. RabbitMQ为什么基于channel通道而非connection连接呢?连接底层是基于TCP/IP,会涉及到三次握手四次挥手,造成服务器很大性能开销,而一个连接可以创建多个通道,多个通道共用一个连接,这样用来传递接收更多消息,性能比较高。
  3. MQ中可能存在没有交换机的队列吗?不可能,消息是基于交换机传递的,虽然没有显示指定交换机但一定存在一个默认的交换机。
  4. 为什么使用微服务/Redis/MQ?  在刚来公司之前,公司对于xxx项目采用的是单体架构,采取的同步方式调用各个服务,随着公司业务变得复杂,目前的技术架构不能完全满足需求,所以公司项目负责人采取了分布式进行重构,将系统进行了拆分,针对各个服务之间需要协同运行,所以采用了MQ,后面综合各个MQ的优劣势采用了RabbitMQ,在使用过程中,感触最深的就是它的异步,处理数据更加高效,使得网站性能成倍的提升。
  5. 消费者在消费消息过程中出现异常,RabbitMQ会出现什么问题?如何解决?可能会出现死循环,因为它有一个重试机制,会不断地对服务器进行重试,可能导致服务崩溃。解决方案:a.设置重试次数 + 死信队列 b.try/catcah + 手动ack c.try/catcha +手动ack + 死信队列 +人工干预
  6. 消息队列应该创建在消费者端还是生产者端?按理应该在消费端,因为消费消息位于消费端,但存在生产者在生产消息过程中丢失消息;在生产端的话可以保证消息不会丢失;两个都创建的话,谁先启动谁先创建,后面启动会覆盖前面队列

项目实战

公司平台利用RabbitMQ中间件发送网页版的邮件核心流程:应用(比如:邮件功能)生产消息 -> mqs-broker服务(路由模式:RoutingKey(xxx.email)、Exchange等)存放消息 -> mqs-consumer服务获取消息 –> 邮件服务进行发送邮件(消费消息);

具体流程如下: 首先涉及发送邮件的应用作为消息(邮件内容等)生产者通过messageServiceFeign调用mqs-broker服务,在mqs-broker服务中验证消息并最终将消息发送到队列中; mqs-consumer服务通过RabbitListener监听队列并从其中获取消息,并通过 SendEmailServiceFeign调用邮件服务(邮件服务中采用webservice技术完成邮件发送),发送成功后相当于消费成功

你可能感兴趣的:(#,RabbitMQ,rabbitmq,中间件,java)