【RabbitMQ高级特性】消息确认机制、持久化、发送方确认、TTL和死信队列

     🔥个人主页: 中草药

🔥专栏:【中间件】企业级中间件剖析


 一、消息确认机制

消费者确认机制确保消息被正确处理后才从队列中删除。如果消费者处理失败(如业务异常或宕机),Broker 会重新投递消息。

确认模式

1. 自动确认(autoAck=true

  • 原理
    消息一旦被消费者接收(无论是否处理成功),立即自动确认并从队列删除。

  • 风险
    若消费者在处理消息时崩溃或抛出异常,消息将永久丢失(因为已被确认删除)。

  • 适用场景
    非关键业务,允许消息偶尔丢失(如日志采集、监控数据)。

2. 手动确认(autoAck=false

  • 原理
    消费者在处理消息后,必须显式调用 basicAck() 确认消息;若处理失败,调用 basicNack() 或 basicReject() 拒绝消息。

  • 核心方法

    • basicAck(deliveryTag, multiple):确认单条或批量消息。

    • basicNack(deliveryTag, multiple, requeue):拒绝消息,可选择是否重新入队。

    • basicReject(deliveryTag, requeue):拒绝单条消息(不支持批量)。

  • 适用场景
    关键业务场景,要求消息必须可靠处理(如订单支付、库存扣减)。

手动确认(Spring Boot 示例)

spring:rabbitmq:listener:simple:acknowledge-mode: manual  # 开启手动确认

Spring-AMQP对消息确认机制提供了三种策略

public enum AcknowledgeMode {NONE,MANUAL,AUTO;private AcknowledgeMode() {}public boolean isTransactionAllowed() {return this == AUTO || this == MANUAL;}public boolean isAutoAck() {return this == NONE;}public boolean isManual() {return this == MANUAL;}
}

 AcknowledgeMode.NONE

        这种模式下,消息一旦投递 给消费者,不管消费者是否成功处理了消息,RabbitMQ 就会自动确认消息,从 RabbitMQ 队列中移除消息。如果消费者处理消息失败,消息可能会丢失。

AcknowledgeMode.AUTO (默认)

        这种模式下,消费者在消息处理成功时会自动确认消息,但如果处理过程中抛出了异常,则不会确认消息。

AcknowledgeMode.MANUAL

        手动确认模式下,消费者必须在成功处理消息后显式调用 basicAck 方法来确认消息。如果消息未被确认,RabbitMQ 会认为消息尚未被成功处理,并且会在消费者可用时重新投递该消息,这种模式提高了消息处理的可靠性,因为即使消费者处理消息后失败,消息也不会丢失,而是可以被重新处理。

测试Demo

producer

@RestController
@RequestMapping("/producer")
public class ProducerController {@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("/ack")public String ack(){rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack","ack mode test");return "ack mode test";}
}

listener

@Component
public class AckListener {@RabbitListener(queues = Constants.ACK_QUEUE)public void handleMessage(Message message, Channel channel) throws IOException {long deliveryTag = message.getMessageProperties().getDeliveryTag();try{System.out.printf("接收到消息: %s , deliveryTag = %d,\n",new String(message.getBody(),"UTF-8"),message.getMessageProperties().getDeliveryTag());System.out.println("业务逻辑处理1");int n=3/0;System.out.println("业务逻辑处理2");channel.basicAck(deliveryTag,false);}catch (Exception e) {channel.basicNack(deliveryTag,false,true);}}
}

二、持久化

RabbitMQ 的持久化是确保消息在服务器重启或异常崩溃后不丢失的核心机制。它通过将交换机、队列和消息内容写入磁盘来实现数据的持久保存。以下是持久化的详细说明及配置方法:


持久化的三个层级

交换机(Exchange)持久化

        作用:确保交换机元数据在服务器重启后仍存在。

        配置方式:声明交换机时设置 durable=true

        代码示例(Spring Boot)

@Bean
public Exchange orderExchange() {return ExchangeBuilder.directExchange("order.exchange").durable(true)  // 持久化交换机.build();
}

队列(Queue)持久化

        作用:确保队列元数据(如队列名称、绑定关系)和消息的存储位置在重启后保留。

        配置方式:声明队列时设置 durable=true

        代码示例

@Bean
public Queue orderQueue() {return QueueBuilder.durable("order.queue")  // 持久化队列.deadLetterExchange("dlx.exchange") // 可选:绑定死信队列.build();
}

消息(Message)持久化

        作用:将消息内容持久化到磁盘,防止服务器断电或重启导致消息丢失。

        配置方式:发送消息时设置 deliveryMode=2(持久化模式)。

        代码示例

rabbitTemplate.convertAndSend("order.exchange", "order.routingKey", message, msg -> {msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 消息持久化return msg;
});

消息是存储在队列之中的,所以消息持久化需要队列持久化+消息持久化 若仅仅是单独设置某一方的持久化,在MQ重启之后,消息仍然会发生丢失


三、发送方确认

        以上所述内容分别对应三个环节的RabbitMQ的可靠性保证,前文已经对发布确认模式有了一个详细的介绍,这里我们进行补充

【RabbitMQ】RabbitMQ的核心概念与七大工作模式_mq的几种模式-CSDN博客

针对确保消息可靠发送到服务器,

a. 通过事务机制实现
b. 通过发送方确认 (publisher confirm) 机制实现

        事务机制比较消耗性能,在实际工作中使用也不多,这里主要介绍 confirm 机制来实现发送方的确认。RabbitMQ为我们提供了两个方式来控制消息的可靠性传递

1、confirm确认模式

2、return退回模式

两种方法并不互斥,可以单独使用,也可以结合使用


Confirm确认模式

        Producer 在发送消息的时候,对发送端设置一个 ConfirmCallback 的监听,无论消息是否到达 Exchange,这个监听都会被执行,如果 Exchange 成功收到,ACK (Acknowledge character,确认字符) 为 true,如果没收到消息,ACK 就为 false.

配置

测试代码

RabbitMQTemplate

@Configuration
public class RabbitTemplateConfig {@Bean(name = "rabbitTemplate")public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {return new RabbitTemplate(connectionFactory);}@Bean(name = "confirmRabbitTemplate")public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory) {//设置回调方法RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {@Overridepublic void confirm(CorrelationData correlationData, boolean b, String s) {System.out.println("执行了confirm方法");if(b){System.out.printf("接收到消息,消息ID:%s \n",correlationData==null?null:correlationData.getId());}else {System.out.printf("未接收到消息的ID:%s \n, cause: %s",correlationData==null?null:correlationData.getId(),s);//相应的业务逻辑处理}}});return rabbitTemplate;}}
@RestController
@RequestMapping("/producer")
public class ProducerController {@Autowiredprivate RabbitTemplate rabbitTemplate;@Autowiredprivate RabbitTemplate confirmRabbitTemplate;@RequestMapping("/pers")public String pers(){Message message=new Message("Persistent".getBytes(),new MessageProperties());message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);rabbitTemplate.convertAndSend(Constants.PERS_EXCHANGE,"pers","pers test");return "pers mode test";}@RequestMapping("/confirm")public String confirm(){CorrelationData correlationData=new CorrelationData("1");// rabbitTemplate.convertAndSend(Constants.CONFIRM_EXCHANGE,"confirm","confirm test",correlationData);//错误演示 Constants.CONFIRM_EXCHANGE+"1" 交换机不存在confirmRabbitTemplate.convertAndSend(Constants.CONFIRM_EXCHANGE+"1","confirm","confirm test",correlationData);return "confirm test";}
}

注意,回调方法只能设置一次,因此要使用配置的方法,如果将回调方法写在controller的方法内部,会致使出现以下问题

1、这种方式设置的confirmcallback会影响所有使用RabbitTemplate的方法

2、重复调用接口时,会提示错误


return退回模式 

        消息到达 Exchange 之后,会根据路由规则匹配,把消息放入 Queue 中。Exchange 到 Queue 的过程,如果一条消息无法被任何队列消费 (即没有队列与消息的路由键匹配或队列不存在等),可以选择把消息退回给发送者。消息退回给发送者时,我们可以设置一个返回回调方法,对消息进行处理。

首先同样进行配置(如图前文confirm一致)

设置返回的逻辑

@Configuration
public class RabbitTemplateConfig {@Bean(name = "rabbitTemplate")public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {return new RabbitTemplate(connectionFactory);}@Bean(name = "confirmRabbitTemplate")public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory) {//设置回调方法RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {@Overridepublic void confirm(CorrelationData correlationData, boolean b, String s) {System.out.println("执行了confirm方法");if(b){System.out.printf("接收到消息,消息ID:%s \n",correlationData==null?null:correlationData.getId());}else {System.out.printf("未接收到消息的ID:%s \n, cause: %s",correlationData==null?null:correlationData.getId(),s);//相应的业务逻辑处理}}});//        rabbitTemplate.setReturnsCallback(returnedMessage -> {
//            System.out.println("消息退回");
//        });rabbitTemplate.setMandatory(true);//强制性rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {@Overridepublic void returnedMessage(ReturnedMessage returnedMessage) {System.out.println("消息退回"+returnedMessage);}});return rabbitTemplate;}
}

RabbitTemplate的setMandatory为true时,当有一条消息无法被任何队列消费,RabbitMQ应该会将消息返回该发送者,此时的ReturnCallBack方法就会被处罚

回调函数中有一个参数:ReturnedMessage


public class ReturnedMessage {//返回的消息对象,包含了消息体和消息属性private final Message message;//由Broker提供的回复码, 表⽰消息⽆法路由的原因. 通常是⼀个数字代码,每个数字代表不同
的含义. private final int replyCode;//⼀个⽂本字符串, 提供了⽆法路由消息的额外信息或错误描述.private final String replyText;//消息被发送到的交换机名称private final String exchange;//消息的路由键,即发送消息时指定的键private final String routingKey;
}

重试机制

        在消息传递过程中,可能会遇到各种问题,如网络故障,服务不可用,资源不足等,这些问题可能导致消息处理失败,为了解决这些问题,RabbitMQ 提供了重试机制,允许消息在处理失败后重新发送.但如果是程序代码逻辑引起的错误,那么多次重试也是没有用的,可以设置重试次数。

注意:此重试策略的配置只有在acknowledge-mode 为 auto 时,才会生效

设置Listener消费信息

@Component
public class RetryListener {@RabbitListener(queues = Constants.RETRY_QUEUE)public void handleMessage(Message message) {System.out.printf("["+Constants.RETRY_QUEUE+"]接收到消息: %s ,deliverTag: %s \n",new String(message.getBody()),message.getMessageProperties().getDeliveryTag());int num=3/0;System.out.println("业务处理完成");}//手动确认
//    @RabbitListener(queues = Constants.RETRY_QUEUE)
//    public void handleMessage(Message message,Channel channel) throws Exception {
//        System.out.printf("["+Constants.RETRY_QUEUE+"]接收到消息: %s ,deliverTag: %s \n",new String(message.getBody()),message.getMessageProperties().getDeliveryTag());
//        try{
//            int num=3/0;
//            System.out.println("业务处理完成");
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);//是否批量 false
//        }catch (Exception e){
//            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);//b-是否批量 b1-是否重新入队
//        }
//    }
}

 此时会按照我们重试策略的配置来进行重试

使用重试机制时需要注意:
1.自动确认模式下: 程序逻辑异常,多次重试还是失败,消息就会被自动确认,那么消息就丢失了
2.手动确认模式下: 程序逻辑异常,多次重试消息依然处理失败,无法被确认,就一直是unacked的状态,导致消息积压 


 如何保证RabbitMQ消息的可靠传输

消息可能丢失的场景以及解决方案

生产者将消息发送到 broker 失败
a. 可能原因:网络问题等
b. 解决办法:发送方确认 - confirm 确认模式

消息在交换机中无法路由到指定队列
a. 可能原因:代码或者配置层面错误,导致消息路由失败
b. 解决办法:发送方确认 - return 模式

消息队列自身数据丢失
a. 可能原因:消息到达 RabbitMQ 之后,RabbitMQ Server 宕机导致消息丢失.
b. 解决办法:持久性,开启 RabbitMQ 持久化,就是消息写入之后会持久化到磁盘,如果 RabbitMQ 挂了,恢复之后会自动读取之前存储的数据.(极端情况下,RabbitMQ 还未持久化就挂了,可能导致少量数据丢失,这个概率极低,也可以通过集群的方式提高可靠性)

消费者异常,导致消息丢失
a. 可能原因:消息到达消费者,还没来得及消费,消费者宕机。消费者逻辑有问题.
b. 解决办法:消息确认,RabbitMQ 提供了消费者应答机制来使 RabbitMQ 能够感知到消费者是否消费成功消息。默认情况下消费者应答机制是自动应答的,可以开启手动确认,当消费者确认消费成功后才会删除消息,从而避免消息丢失。除此之外,也可以配置重试机制 , 当消息消费异常时,通过消息重试确保消息的可靠性


四、TTL

         TTL(Time to live,过期时间),RabbitMQ可对消息和队列设置TTL,当消息打到大存活时间之后,还没有被消费就会被自动清除。

设置TTL的方法有很多种,这里不进行一一赘述

设置消息TTL

目前有两种方法可以设置消息的TTL.

        一是设置队列的TTL,队列中所有消息都有相同的过期时间,二是对消息本身进行单独设置,每条消息的TTL可以不同.如果两种方法一起使用,则消息的TTL以两者之间较小的那个数值为准.

@RestController
@RequestMapping("/producer")
public class ProducerController {@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("/ttl")public String ttl(){System.out.println("ttl ...");//        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
//            @Override
//            public Message postProcessMessage(Message message) throws AmqpException {
//                message.getMessageProperties().setExpiration("10000"); //单位毫秒
//                return message;
//            }
//        };//lambdarabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"ttl","ttl test",message -> {message.getMessageProperties().setExpiration("10000");  //单位毫秒return message;});return "ttl test";}}

即可通过队列观察到消息

 设置队列TTL

@Configuration
public class RabbitMQConfig {//TTL//未设置TTL的队列@Bean("ttlQueue")public Queue ttlQueue() {return QueueBuilder.durable(Constants.TTL_QUEUE).build();}//设置ttl的队列@Bean("ttlQueue2")public Queue ttlQueue2() {return QueueBuilder.durable(Constants.TTL_QUEUE2).ttl(20000).build(); //设置队列的ttl为20s}@Bean("ttlExchange")public DirectExchange ttlExchange() {return ExchangeBuilder.directExchange(Constants.TTL_EXCHANGE).build();}@Bean("ttlBinding")public Binding ttlBinding(@Qualifier("ttlQueue") Queue ttlQueue,@Qualifier("ttlExchange") Exchange ttlExchange) {return BindingBuilder.bind(ttlQueue).to(ttlExchange).with("ttl").noargs();}@Bean("ttlBinding2")public Binding ttlBinding2(@Qualifier("ttlQueue2") Queue ttlQueue,@Qualifier("ttlExchange") Exchange ttlExchange) {return BindingBuilder.bind(ttlQueue).to(ttlExchange).with("ttl").noargs();}
}

为什么这两种方法处理的方式不一样?
        因为设置队列过期时间,队列中已过期的消息肯定在队列头部,RabbitMO只要定期从队头开始扫描是否有过期的消息即可.
        而设置消息TTL的方式,每条消息的过期时间不同,如果要删除所有过期消息需要扫描整个队列,所以不如等到此消息即将被消费时再判定是否过期,如果过期再进行删除即可.


 五、死信队列


        死信(Dead Letter)是消息队列中的一种特殊消息,它指的是那些无法被正常消费或处理的消息,死信队列用于存储这些死信消息

消息变为死信一般有以下几种情况

消息过期:消息在队列中存活的时间超过了设定的TTL

消息被拒绝:消费者在处理消息时,可能因为消息内容错误,处理逻辑异常等原因拒绝处理该消息(Basic.Reject/Basic.Nack).如果拒绝时指定不重新入队(requeue=false),消息也会成为死信

队列达到最大长度:当队列达到最大长度,无法再容纳新的消息时,新来的消息会被处理为死信.

代码示例

@Configuration
public class DLConfig {//正常@Bean("normalQueue")public Queue normalQueue(){return QueueBuilder.durable(Constants.NORMAL_QUEUE).deadLetterExchange(Constants.DL_EXCHANGE).deadLetterRoutingKey("dlx").maxLength(10).ttl(10000).build();}@Bean("normalExchange")public DirectExchange normalExchange(){return ExchangeBuilder.directExchange(Constants.NORMAL_EXCHANGE).build();}@Bean("bindingNormal")public Binding bindingNormal(@Qualifier("normalQueue") Queue normalQueue,@Qualifier("normalExchange") DirectExchange normalExchange){return BindingBuilder.bind(normalQueue).to(normalExchange).with("normal");}//死信@Bean("dlQueue")public Queue dlQueue(){return QueueBuilder.durable(Constants.DL_QUEUE).build();}@Bean("dlExchange")public DirectExchange dlExchange(){return ExchangeBuilder.directExchange(Constants.DL_EXCHANGE).build();}@Bean("bindingDl")public Binding bindingDl(@Qualifier("dlQueue") Queue dlQueue,@Qualifier("dlExchange") DirectExchange dlExchange){return BindingBuilder.bind(dlQueue).to(dlExchange).with("dlx");}
}

 Listener

@Component
public class DLListener {@RabbitListener(queues = Constants.NORMAL_QUEUE)public void handleMessage(Message message, Channel channel) throws IOException {long deliveryTag = message.getMessageProperties().getDeliveryTag();try{System.out.printf("[normal.queue]接收到消息: %s , deliveryTag = %d,\n",new String(message.getBody(),"UTF-8"),message.getMessageProperties().getDeliveryTag());System.out.println("业务逻辑处理1");int n=3/0;System.out.println("业务逻辑处理2");channel.basicAck(deliveryTag,false);}catch (Exception e) {channel.basicNack(deliveryTag,false,false);}}@RabbitListener(queues = Constants.DL_QUEUE)public void dlHandleMessage(Message message, Channel channel) throws IOException {System.out.printf("[dl.queue]接收到消息: %s , deliveryTag = %d,\n",new String(message.getBody(),"UTF-8"),message.getMessageProperties().getDeliveryTag());}}

controller

@RestController
@RequestMapping("/producer")
public class ProducerController {@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("/dl")public String dl(){System.out.println("dl ...");//发送普通消息//rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","dl test");//测试队列长度for (int i = 0; i < 11; i++) {rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","dl test "+i);}return "dl test";}}

即可进行测试

死信队列的应用场景
        对于RabbitMQ来说,死信队列是一个非常有用的特性,它可以处理异常情况下,消息不能够被消费者正确消费而被置入死信队列中的情况,应用程序可以通过消费这个死信队列中的内容来分析当时所遇到的异常情况,进而可以改善和优化系统,
        比如: 用户支付订单之后,支付系统会给订单系统返回当前订单的支付状态为了保证支付信息不丢失,需要使用到死信队列机制,当消息消费异常时,将消息投入到死信队列中,由订单系统的其他消费者来监听这个队列,并对数据进行处理(比如发送工单等,进行人工确认).

场景的应用场景还有:
        消息重试:将死信消息重新发送到原队列或另一个队列进行重试处理

        消息丢弃:直接丢弃这些无法处理的消息,以避免它们占用系统资源

        日志收集:将死信消息作为日志收集起来,用于后续分析和问题定位 


抛弃时间的人,时间也抛弃他。——莎士比亚

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/39647.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

调用百度api实现语音识别(python)

该代码实现了一个企业级的语音识别解决方案,通过调用百度语音识别API,实现实时录音识别和对已有音频语音识别功能。 百度智能云:请自行访问百度智能云,开通免费的语音识别功能,获取API_KEY和SECRET_KEY。操作按照百度流程即可,可免费申请。 首先,配置下百度API和描述下错…

Python实现小红书app版爬虫

简介&#xff1a;由于数据需求的日益增大&#xff0c;小红书网页版已经不能满足我们日常工作的需求&#xff0c;为此&#xff0c;小编特地开发了小红书手机版算法&#xff0c;方便大家获取更多的数据&#xff0c;提升工作效率。 手机版接口主要包括&#xff1a;搜素&#xff0…

【AndroidRTC-11】如何理解webrtc的Source、TrackSink

Android-RTC系列软重启&#xff0c;改变以往细读源代码的方式 改为 带上实际问题分析代码。增加实用性&#xff0c;方便形成肌肉记忆。同时不分种类、不分难易程度&#xff0c;在线征集问题切入点。 问题1&#xff1a;如何理解VideoSource、VideoTrack&VideoSink三者的关系…

Windows安装Rust环境(详细教程)

一、 安装mingw64(C语言环境) Rust默认使用的C语言依赖Visual Studio&#xff0c;但该工具占用空间大安装也较为麻烦&#xff0c;可以选用轻便的mingw64包。 1.1 安装地址 (1) 下载地址1-GitHub&#xff1a;Releases niXman/mingw-builds-binaries GitHub (2) 下载地址2-W…

英伟达黄仁勋谈人工智能趋势,首提代理式AI,后续机器人将登场

近日&#xff0c;英伟达 GTC 2025 大会主题演讲中&#xff0c;英伟达 CEO 黄仁勋再次身穿皮衣登场。黄仁勋一上来就提到了 AI 发展的未来&#xff0c;现在我们处于生成式 AI&#xff08;Generative AI&#xff09;阶段&#xff0c;但根据黄仁勋的路线图&#xff0c;我们将迈向一…

LCR 187. 破冰游戏(python3解法)

难度&#xff1a;简单 社团共有 num 位成员参与破冰游戏&#xff0c;编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。社长抽取一个数字 target&#xff0c;从 0 号成员起开始计数&#xff0c;排在第 target 位的成员离开圆桌&#xff0c;且成员离开后从下一个成员开始计数…

水星(MERCURY)监控初始化的恢复和转码方法

水星(MERCURY)的安防监控恢复了很多&#xff0c;其嵌入式文件系统也一直迭代更新。做为数据恢复从业者每天处理最多的就是恢复数据&#xff0c;但是有的时候业务的需要我们不仅仅恢复出数据&#xff0c;还需要能够转码成通用的MP4类文件并要求画面和声音实现“同步”。 故障存…

基于SpringBoot的实现的客户关系管理系统(CRM)(源码+数据库)

464客户关系管理系统&#xff08;CRM&#xff09;&#xff0c;主要功能如下 【后台功能】 权限管理模块: 包括系统的登录与注册功能 用户管理模块: 基于RBAC的权限模型设计, 实现分配角色的功能功能 客户管理模块: 对客户信息进行新增 修改 删除 查看 联络信息管理模块: 对联络…

关于网络的一点知识(持续更新)

1、IP地址和子网掩码、端口号: IP地址是设备在网络上的地址,相当于一栋房子的门牌号。子网掩码相当于房子所在的街道。同一条街道的房子间是通过街道直通的,主人可以互相拜访。 举个例子,如下图所示。 说明:将两台设备的IP和子网掩码转化为二进制,然后将各自的IP地址和…

Python---数据分析(Pandas八:二维数组DataFrame数据操作一: 数据清洗,数据转换)

一、 数据清洗 1.1、 isnull() 用于检测 DataFrame 中的缺失值&#xff0c;它会返回一个相同形状的布尔型 DataFrame&#xff0c;其中每个元素表示原始 DataFrame 中相应位置的元素是否是缺失 值。 import pandas as pd import numpy as np# 创建一个包含缺失值的 DataFrame …

智能汽车图像及视频处理方案,支持视频星轨拍摄能力

美摄科技作为智能汽车图像及视频处理领域的先行者&#xff0c;正以革新性的技术引领着行业的未来发展。美摄科技智能汽车图像及视频处理方案&#xff0c;一个集高效性、智能化、画质增强于一体的创新解决方案&#xff0c;旨在重塑智能汽车图像画质的新标准&#xff0c;并支持前…

Flask接口开发--GET接口

安装Flask 1.安装命令&#xff1a; pip3 install Flask2.查看Flask版本&#xff1a; pip3 show flask如图我的Flask版本号是2.0.3 项目创建 1、在PyCharm中&#xff0c;我们点击左上方的 file&#xff0c;选择 New Project&#xff0c;创建一个Flask项目。&#xff08;Py…

应用权限组列表

文章目录 使用须知位置相机麦克风通讯录日历运动数据身体传感器图片和视频音乐和音频跨应用关联设备发现和连接剪切板文件夹文件(deprecated) 使用须知 在申请目标权限前&#xff0c;建议开发者先阅读应用权限管控概述-权限组和子权限&#xff0c;了解相关概念&#xff0c;再合…

Python为Word文档添加书签并打包成exe

背景简述 由于一些工作场景&#xff0c;需要从多个Word文档中找到出现的关键词&#xff0c;并阅读关键词的上下文内容。文件可能几十个&#xff0c;手动操作太要命了。所以python尝试处理。 目录 背景简述思路第一步、功能实现结果验证 第二步、打包成exe2-1、基础准备2-2、打…

【MYSQL】索引和事务

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 本期内容讲解 MySQL 中的索引和事务&#xff0c;在学习的过程中&#xff0c;我们需要经常问自己为什么 文章目录 1. 索…

2021年蓝桥杯第十二届CC++大学B组真题及代码

目录 1A&#xff1a;空间&#xff08;填空5分_单位转换&#xff09; 2B&#xff1a;卡片&#xff08;填空5分_模拟&#xff09; 3C&#xff1a;直线&#xff08;填空10分_数学排序&#xff09; 4D&#xff1a;货物摆放&#xff08;填空10分_质因数&#xff09; 5E&#xf…

PicGo安装与配置-Gitee图床

1、 前言 平时使用Typora写文章,上传文章到第三方平台上去都要把图片一个一个上传上去,于是我就百度了有没有什么方法可以省略这一步骤,我发现Typora可以用PicGo+Gitee图床方式,这个挺容易的,我把安装的过程在此记录下来。 PicGo是一个用于快速上传图片并获取图片 URL 链…

html css js网页制作成品——HTML+CSS+js迪奥口红网站网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…

重学Java基础篇—如何优雅的删除HashMap元素

在Java中优雅地删除HashMap元素需要注意遍历时的安全性和代码的简洁性。 以下是几种推荐的方法&#xff1a; 1. 使用迭代器遍历并删除&#xff08;传统方式&#xff09; 在遍历时通过迭代器的remove() 方法删除元素&#xff0c;避免ConcurrentModificationException异常。 H…

26考研——图_图的遍历(6)

408答疑 文章目录 三、图的遍历图的遍历概述图的遍历算法的重要性图的遍历与树的遍历的区别图的遍历过程中的注意事项避免重复访问遍历算法的分类遍历结果的不唯一性 广度优先搜索广度优先搜索&#xff08;BFS&#xff09;概述BFS 的特点广度优先遍历的过程示例图遍历过程 BFS …