RabbitMQ的理解与使用,以及消息丢失,重复,积压的demo处理

一,关于Mq的概念与使用场景

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件

  1. 生产者 产生数据发送消息的程序是生产者,如下图的 下单购买商品就是生产者

  1. 交换机 交换机是 RabbitMQ 非常重要的一个部件,一方面它接收来自生产者的消息,另一方面它将消息推送到队列中。交换机必须确切知道如何处理它接收到的消息,是将这些消息推送到特定队列还是推送到多个队列,亦或者是把消息丢弃,这个得有交换机类型决定

  1. 队列 队列是 RabbitMQ 内部使用的一种数据结构,本质是大的消息缓冲区,生产者将消息发送到队列中,消费者就可以从这里面拿然后去消费

  1. 消费者 顾名思义就是消费消息的一个方法。如下图。同一个方法既可以是生产者也可以是消费者

使用场景,优点1.异步处理数据和解耦,可以增加容错性和减少耗时(简单理解一下)

2.流量削峰,保证服务器正常运行,不要出现服务器瘫痪和宕机

缺点:

系统的可用性降低
系统引入的外部依赖越多,系统越容易挂掉,本来只是A系统调用BCD三个系统接口就好,ABCD四个系统不报错整个系统会正常运行。引入了MQ之后,虽然ABCD系统没出错,但MQ挂了以后,整个系统也会崩溃。

系统的复杂性提高
引入了MQ之后,需要考虑的问题也变得多了,如何保证消息没有重复消费?如何保证消息不丢失?怎么保证消息传递的顺序?

一致性问题
A系统发送完消息直接返回成功,但是BCD系统之中若有系统写库失败,则会产生数据不一致的问题。

二。关于MQ的核心部分(不同的业务选择不同的模式使用,主要是根据公司业务来决定)

  • Hello Wold 简单模式

  • Work queues工作 队列模式

  • Publish/Subscribe发布订阅模式

  • Routing 路由模式

  • Topics 主题模式

  • Publisher Confirms 发布确认模式

三,开始MQ的demo项目,首先创建两个项目或者两个模块都可以,我这里是创建的两个项目springboot+mysql+MQ,项目A表示生产者,项目B表示消费者。(网上关于安装MQ多的很,自己去安装)

1.Hello Wold 简单模式例子

在pom.xml中配置jar包

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>
    </dependencies>

在propertest配置数据库的连接,以及MQ的连接配置(数据库的连接后面有用)

新建一个类,需要配置队列的name,我这里是创建的一个order的队列名字,你们可以自己命名

开始给队列发送消息,我这里是给order这个队列名字发送

到这里之后,就可以在测试内中进行发送消息,只要没报错,就说明没有问题了,我们可以在MQ中查看消息

查看order队列的消息是否存在

我们开始对B项目开始处理(接收者),先引入pom.xml

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>
    </dependencies>

配置properties,注意端口号冲突

编写接收类

开始测试接收,刚刚我们发送者发送了一条消息,启动成功可以直接获取

MQ的消息也被接收了(消费)

2.多对多的Work queues工作 队列模式的案例

我们直接复制粘贴一个接收者(每次写完记得重启一下)

然后我们在A项目开始生产信息

开始测试,看是否接收到了10条消息

3.topic exchange(主题模式)

在企业开发中用的比较多,因为这个模式是最灵活的一种方式,可以根据routing_key自由的绑定不同的队列

开始创建主题模式的配置类

开始消息发送TopicService

测试

topic模式可以匹配通配符。*代表匹配一个元素,#代表匹配一个或多个元素。

然后我们就可以去队列中查看到order队列跟order_key队列都有一条消息未被消费

启动两个消费者,他们都能收到消息

4..Fanout Exchange(订阅模式)给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息

配置队列绑定在交换机中

创建三个消费者 @RabbitListener(queues = "A"),@RabbitListener(queues = "B"),@RabbitListener(queues = "C")

队列的name

开始产生消息(生产者)

测试,成功消费

四.关于MQ的消息丢失,重复,积压的解决方案

1)、消息的丢失

场景1.消息发送出去,由于网络问题没有抵达服务器

解决方案1. 做好容错方法(try-catch),发送消息可能会网络失败,失败后要有重试机制,可记录到数据库,采用定期扫描重发的方式

2.做好日志记录,每个消息状态是否都被服务器收到都应该记录

3. 做好定期重发,如果消息没有发送成功,定期去数据库扫描未成功的消息进行重发

场景2.消息抵达Broker,Broker要将消息写入磁盘(持久化)才算成功。此时Broker尚未持久化完成,宕机。

解决方案 publisher也必须加入确认回调机制,确认成功的消息,修改数据库消息状态。

场景3.自动ACK的状态下。消费者收到消息,但没来得及消息然后宕机

解决方案 一定开启手动ACK,消费成功才移除,失败或者没来得及处理就noAck并重新入队

2)、消息的重复

场景1.消息消费成功,事务已经提交,ack时,机器宕机。导致没有ack成功,Broker的消息重新由unack变为ready,并发送给其他消费者

场景2.消息消费失败,由于重试机制,自动又将消息发送出去(这种是允许的)

场景3.成功消费,ack时宕机,消息由unack变为ready,Broker又重新发送

解决方案1消费者的业务消费接口应该设计为幂等性的。比如扣库存有工作单的状态标志

解决方案2. 使用防重表(redis/mysql),发送消息每一个都有业务的唯一标识,处理过就不用处理

解决方案3.rabbitMQ的每一个消息都有redelivered字段,可以获取是否是被重新投递过来的,而不是第一次投递过来的

3)、消息的积压

场景1.消费者宕机积压

场景2.消费者消费能力不足积压

场景3.发送者发送流量太大

解决方案1.上线更多的消费者,进行正常消费

解决方案2.上线专门的队列消费服务,将消息先批量取出来,记录数据库,离线慢慢处理

五.关于解决MQ的消息丢失,重复,积压的简单demo

MQ消息丢失场景1.消息发送出去,由于网络问题没有抵达服务器的处理方式

场景2.消息抵达Broker,Broker要将消息写入磁盘(持久化)才算成功。此时Broker尚未持久化完成,宕机。设置rabbit的回调机制,但性能下降了一部分

@Configuration
public class MqConfig {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Bean
    public MessageConverter integrationEventMessageConverter() {
        //使用json序列化机制,进行消息转换
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制rabbitTemplate
     * 1.服务器收到消息就回调
     *      1.spring.rabbitmq.publisher-confirms=true
     *      2.设置确定回调ConfirmCallback
     * 2.消息正确抵达队列进行回调
     *      1.spring.rabbitmq.publisher-return=true
     *        spring.rabbitmq.temlate.mandatory=true
     *      2.设置确认回调ReturnCallback
     */
    @PostConstruct  //rabbitTemplate的回调对象
    public void initRabbitTemplate(){
        //设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 只要消息抵达Broker就ack=true
             * @param correlationData 消息的唯一id
             * @param ack  消息是否成功收到
             * @param s  失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String s) {
                //服务器收到了
            }
        });

        //设置消息抵达队列的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *只要消息没有投递给指定的队列,就触发这个失败回调
             * @param message 投递失败的消息详情
             * @param i 回复的状态码
             * @param s 回复的文本内容
             * @param exchange 这个消息是给那个交换机
             * @param key 这个消息是用那个路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String exchange, String key) {
            //服务器收到了,但这里报错误了,就要修改数据库当前这条消息的错误状态->错误。
            }
        });
    }
}

场景3.自动ACK的状态下。消费者收到消息,但没来得及消息然后宕机的处理方式,手动ack

MQ消息重复消费场景1.消息消费成功,事务已经提交,该手动ack时,机器宕机。导致没有ack成功,Broker的消息重新由unack变为ready,并发送给其他消费者

MQ消息重复消费场景2.消息消费失败,由于重试机制,自动又将消息发送出去(这种是允许的)

MQ消息重复消费场景3.成功消费,ack时宕机,消息由unack变为ready,Broker又重新发送,其实跟场景1差不多。

MQ消息积压的场景1.消费者宕机积压

MQ消息积压的场景2.消费者消费能力不足积压。

上面这两种就是多加消费者的机器,进行正常消费

MQ消息积压的场景3.发送者发送流量太大

把消息从mq中存放到数据库中的一张表中,然后通过定时任务慢慢的处理完就行了,这样就不影响mq的正常使用

您好!对于 RabbitMQ 消息丢失消息积压消息重复的问题,一般可以从以下几个方面进行排查和解决: 1. 消息丢失: - 确保生产者成功发送消息RabbitMQ,可以通过确认机制(publisher confirms)来确保消息的可靠性发送。 - 确保消费者正确地从 RabbitMQ 接收消息并进行处理,可以使用消费者确认机制(consumer acknowledgements)来确保消息的可靠消费。 2. 消息积压: - 检查消费者的处理速度是否跟得上消息的生产速度,如果消费者处理速度较慢,会导致消息积压。可以增加消费者数量或优化消费者的处理逻辑来提高消费速度。 - 调整 RabbitMQ 的队列参数,包括队列的最大长度、最大优先级等,以适应不同的场景需求。 3. 消息重复: - 在生产者端,可以使用唯一标识符来标记每条消息,避免重复发送相同的消息。 - 在消费者端,可以使用幂等性操作来处理消息,即使消息重复消费也不会产生错误结果。例如,使用数据库的唯一键或者记录操作日志来避免重复处理。 此外,还可以考虑以下几点来提高 RabbitMQ 的可靠性和性能: - 使用持久化消息,确保消息RabbitMQ 服务器重启后不会丢失。 - 配置合适的消息 TTL(Time-To-Live)时间,防止长时间积压过期消息。 - 使用合适的集群和镜像队列设置,提高 RabbitMQ 的可用性和容错性。 希望以上信息能对您有所帮助!如有更多问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值