当前位置: 首页 > news >正文

[RabbitMQ] 保证消息可靠性的三大机制------消息确认,持久化,发送方确认

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482
🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1. 消息确认
    • 1.1 消息确认机制
    • 1.2 手动确认方法
    • 1.3 代码示例
      • 1.3.1 NONE
      • 1.3.2 AUTO
      • 1.3.3 MANUAL
  • 2. 持久性
    • 2.1 交换机持久化
    • 2.2 队列持久化
    • 2.3 消息持久化
  • 3. 发送方确认
    • 3.1 confirm确认模式
    • 3.2 return回退模式
  • 4. 常见面试题

1. 消息确认

1.1 消息确认机制

生产者发送消息之后,到达消费端之后,可能会有以下情况:一种是消息处理成功,一种是消息处理异常.
在这里插入图片描述
如果消息处理异常的情况下,这条消息就会被删除,此时就会造成消息的丢失.此时我们就需要保证消息可靠地到达消费者,RabbitMQ为此提供了消息确认的机制.
消费者在订阅队列的时候,可以之地宁autoAck参数,根据这个参数,消息确认的机制可以分为一下的两种:

  • 自动确认,autoAck参数为true.RabbitMQ的队列会把发送出去的消息自动置为确认,然后自动从内存或者硬盘中删除,而不管消费者是否真正地消费到了这些消息.
  • 手动确认,autoAck参数为false.RabbitMQ的队列会等待消费者显示调用Basic.Ack命令,回复确认信号之后才从内存或者硬盘中进行删除.
    对于手动确认的队列中的消息而言,队列中的消息分为两部分:
    ⼀是等待投递给消费者的消息.
    二是已经投递给消费者,但是还没有收到消费者确认信号的消息.
    如果RabbitMQ一直没有收到消费者的确认消息信号,则RabbitMQ会安排这个消息重新进入队列,等待投递个下一个消费者,当然下一个消费者还是有可能是原来的消费者.
    在这里插入图片描述

1.2 手动确认方法

消费者在收到消息之后,可以选择确认,也可以选择直接拒绝或者跳过,RabbitMQ也提供了不同的确认应答的方式.一共有以下三种:

  1. 肯定确认: Channel.basicAck(long deliveryTag, boolean multiple)
    这种方法表示的是,消费者已经成功接收到消息,可以将其丢弃了.参数分别表示的是:
    • deliveryTag: 消息的id,是消息的唯一标识.
    • multiple: 是否批量确认,值为true则会一次性ack所有小于或等于指定deliveryTag的消息.值为false,则只确认当前指定deliveryTag的消息.这种方式可以在一定程度上减少网络的开销.
      在这里插入图片描述
  2. 否定确认: Channel.basicReject(long deliveryTag, boolean requeue)
    消费者拒绝接收这个消息.参数说明:
    • requeue: 表示在拒绝这条消息之后,这条消息会不会被放回到队列中.
  3. 否定确认: Channel.basicNack(long deliveryTag, boolean multiple, boolean requeue)
    次方法可以让消费者批量拒绝消息,参数说明:
    • multiple: 设置为true表示拒绝deliveryTag编号之前的所有未被消费消息.其他的参数和上面的类似.

1.3 代码示例

下面我们通过SpringBoot来演示消息确认的机制.
Spring-AMQP对消息确认的机制提供了三种策略.

public enum AcknowledgeMode {NONE,MANUAL,AUTO;
}
  1. NONE: 在这种模式之下,不管消费者是否成功处理了消息,RabbitMQ会自动确认消息,从RabbitMQ的队列中自动移除消息,如果消息处理失败,消息会发生丢失.
  2. AUTO: 如果不对这个配置项进行配置的话,这个就是默认的确认机制,在这种模式之下,消费者会自动确认消息,但是如果在消费者处理消息的过程中抛出了异常,该消息不会被确认.
  3. MANUAL: 手动确认,消费者必须在成功处理之后显示调用basicAck方法来确认消息,如果消息未被确认
    ,RabbitMQ会认为消息没有被处理成功,并且会在消费者可用的时候重新投递该消息.如果消息被拒绝,可以按照自己的需求来处理消息是否重新进入队列中.

1.3.1 NONE

  • 配置确认机制
spring:application:name: rabbitmq-springrabbitmq:host: 39.105.137.64port: 5672username: jiangruijiapassword: ******virtual-host: /listener:simple:acknowledge-mode: none
  • 发送消息
    首先配置交换机和队列
public static final String ACK_QUEUE = "ack_queue";
public static final String ACK_EXCHANGE = "ack_exchange";
@Bean
public DirectExchange ackExchange(){return ExchangeBuilder.directExchange(Constant.ACK_EXCHANGE).durable(true).build();
}
@Bean
public Queue ackQueue(){return QueueBuilder.durable(Constant.ACK_QUEUE).build();
}
@Bean
public Binding ackBinding(@Qualifier("ackExchange") DirectExchange exchange,@Qualifier("ackQueue") Queue queue){return BindingBuilder.bind(queue).to(exchange).with("ack");
}

通过接口发送消息:

@RequestMapping("/ack")
public String ack(){rabbitTemplate.convertAndSend(Constant.ACK_EXCHANGE,"ack","Ack消息");return "发送成功";
}
  • 使用Postman发送消息
    我们先把消费者注释掉,不要运行消费者.
    在发送消息之后,我们看到队列中的消息有一条未消费的消息.
    在这里插入图片描述
    消费者消费者之后:
    在这里插入图片描述
    在这里插入图片描述
    我们看到控制台中接收到了消息,处理异常,但是队列中的消息已经被标记为了确认.

1.3.2 AUTO

  1. 配置消息确认机制
spring:application:name: rabbitmq-springrabbitmq:host: 182.92.204.253port: 5672username: jiangruijiapassword: *****virtual-host: /listener:simple:acknowledge-mode: auto
  1. 运行程序
    我们发现消息在不停地重发,并且消息的tag值在不停地增加.在这里插入图片描述
    在这里插入图片描述
    在管理界面上,我们发现未确认的消息一直为1,这是由于在消费者处理信息的时候出现了异常,这时候RabbitMQ会不断地对消息进行重发,消息不会被确认.导致了消息一直都是unacked状态.

1.3.3 MANUAL

  1. 修改确认机制
    listener:simple:acknowledge-mode: manual
  1. 修改消费者逻辑为手动确认
    首先我们正常处理消息,先不要抛出异常
@Component
public class AckListener {@RabbitListener(queues = Constant.ACK_QUEUE)public void listener(Message message, Channel channel) throws IOException{System.out.printf("接收到消息:%s,消息tag:%d\n",new String(message.getBody(),"UTF-8"),message.getMessageProperties().getDeliveryTag());System.out.println("处理消息");
//            int i = 3/0;//消息处理异常channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);}
}

调用接口发送消息:
在这里插入图片描述
在这里插入图片描述

我们看到运行结果是正常的.队列中的消息成功被消费.
接下来我们把异常放开:

@Component
public class AckListener {@RabbitListener(queues = Constant.ACK_QUEUE)public void listener(Message message, Channel channel) throws IOException{System.out.printf("接收到消息:%s,消息tag:%d\n",new String(message.getBody(),"UTF-8"),message.getMessageProperties().getDeliveryTag());System.out.println("处理消息");int i = 3/0;//消息处理异常channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);}
}

在这里插入图片描述
在这里插入图片描述
我们发现队列中的消息发生了积压,消息直接被退回的队列,控制台抛出了异常,消息没有被处理成功.
之后我们可以把代码改为出现异常的时候拒绝接收,其中basicRejectrequeue属性配置为true,让消息拒绝之后重新入队.

@Component
public class AckListener {@RabbitListener(queues = Constant.ACK_QUEUE)public void listener(Message message, Channel channel) throws IOException{System.out.printf("接收到消息:%s,消息tag:%d\n",new String(message.getBody(),"UTF-8"),message.getMessageProperties().getDeliveryTag());try {System.out.println("处理消息");Thread.sleep(1000);int i = 3/0;//消息处理异常channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);}catch (Exception e){channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);}}
}

运行结果: 消费异常会被捕捉,拒绝接收回到队列之后,会不停进行重试.
在这里插入图片描述
在这里插入图片描述

这里与直接发生异常回到队列的结果不一样,发生异常回到队列不会反复重新处理,如果捕捉到异常之后拒绝接收,后续还是会不停地重新处理,与atuo的结果差不多.

2. 持久性

前面的消息确认机制是保证了消息从队列中到达消费者的过程中不发生丢失.但是如果RabbitMQ由于某种异常情况以外退出或者崩溃,交换机,队列或者消息可能会发生丢失.
RabbitMQ中的持久化分为三个部分: 交换机持久化,队列持久化,消息持久化.

2.1 交换机持久化

我们之前在进行RabbitMQ配置的时候,就曾经反复使用交换机的持久化.他是在声明交换机的时候,通过durable方法的参数设置为true来实现的.
相当于将交换机的属性在服务器内部保存,当MQ的服务器发生意外或关闭之后,重启 RabbitMQ 时不需要重新去建立交换机,交换机会自动建立,相当于一直存在.

public DirectExchange directExchange(){return ExchangeBuilder.directExchange(Constant.DIRECT_EXCHANGE).durable(true).build();
}

2.2 队列持久化

和交换机持久化一样,我们在之前也一直使用queue的持久化.持久化是通过在声明队列的时候,使用durable方法把其中的属性设置为队列名来实现的.

public Queue directQueue1(){return QueueBuilder.durable(Constant.DIRECT_QUEUE1).build();
}

当我们在查看队列持久化的源码的时候,我们发现队列的durable属性默认为true.

public static QueueBuilder durable(String name) {return (new QueueBuilder(name)).setDurable();
}
private QueueBuilder setDurable() {this.durable = true;return this;
}

我们也可以使用nonDurable方法来创建非持久化的队列.

public Queue directQueue1(){return QueueBuilder.nonDurable(Constant.DIRECT_QUEUE1).build();
}

2.3 消息持久化

消息实现持久化,需要把消息的投递模式设置为2(也就是MessageProperties中的deliveryMode设置为MessageDeliveryMode.PERSISTENT),MessageDeliveryMode为一个枚举类.

public enum MessageDeliveryMode {NON_PERSISTENT,//⾮持久化 PERSISTENT;//持久化 

只有我们同时设置了队列和消息的持久化,才可以保证RabbitMQ服务在重启之后,消息依然是存在的.如果只是设置了队列的持久化或者是消息的持久化,重启之后消息依然会丢失.
接下来我们让生产者来发送一条持久化的消息:
交换机和队列的配置还是我们之前的配置

@RequestMapping("/ack")
public String ack(){String s = "hello ack";Message message = new Message(s.getBytes(StandardCharsets.UTF_8),new MessageProperties());message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);rabbitTemplate.convertAndSend(Constant.ACK_EXCHANGE,"ack",message);return "发送成功";
}

在默认的条件下,消息是持久化,除非队列被声明为非持久化或者是在发送消息的时候消息被标记为持久化.

如果所有的消息都被标记为了持久化,会严重影响RabbitMQ的性能.这是因为写硬盘会拖慢速度.对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量.在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做⼀个权衡.

如果把交换机,队列,消息都设置为持久化,就可以保证百分之百数据不丢失了吗?答案是不是的.

  1. 如果消费者在消费队列消息的时候,设置的是自动确认,那么当消费者还没有来得及处理消息,消费者就宕机了,就会造成消息丢失,这种情况就需要我们把消息的确认机制设置为手动确认即可.
  2. 如果消息在RabbitMQ中进行持久化的时候,在存硬盘的过程中发生了宕机,消息还没有来得及落盘,那么这些消息也会消失.
    这种情况如何解决呢?第一种方法就是使用仲裁队列,我们后面说明.第二种方法就是发送端引入发送方确认的机制来保证消息的可靠性.下面我们就来详细介绍发送方确认机制.

3. 发送方确认

在使用RabbitMQ的时候,可以通过持久化来解决RabbitMQ宕机而导致的消息丢失的问题,如果有一种情况,消息在到达服务器之前就已经发生了丢失,这时候消息根本没有到达RabbitMQ,持久化也解决不了这个问题.
RabbitMQ为我们提供了两种方案,一种是事务机制,一种是发送方确认机制.
由于事务机制比较消耗性能,一般情况下,我们不会使用事务.我们主要介绍发送方确认的机制.
RabbitMQ为我们提供了两个方式来控制消息可靠性的投递:

  1. confirm确认模式
  2. return退回模式

3.1 confirm确认模式

  1. 配置RabbitMQ
rabbitmq:host: 182.92.204.253port: 5672username: jiangruijiapassword: ******virtual-host: /listener:simple:acknowledge-mode: manualpublisher-confirm-type: correlated #消息发送确认
  1. 设置确认回调逻辑并发送消息
    无论消息发送是成功还是失败,都会调用ConfirmCallback中的confirm方法.如果消息成功发送到Broker,则ack为true.如果发送失败,ack为false,并且cause提供失败的原因.
    首先配置带有发送方确认机制的RabbitTemplate,使用setConfirmCallback来设置发送方确认,其中使用匿名内部类的方式重写confirm方法,在其中编写确认成功和确认失败的逻辑.
@Bean
public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory){RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {//设置消息队列的确认机制@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {if (ack){//如果确认成功,ack为trueSystem.out.println("发送方确认成功:"+correlationData.getId());//correlationData中包含消息的id}else {System.out.println("发送方确认失败:"+correlationData.getId()+",原因:"+cause);}}});return rabbitTemplate;
}

confirm中的三个参数的意思分别是:
- correlationData:消息发送时候的一些附加信息,其中包含一个id属性,通常用于在确认回调中识别特定的消息.
- ack: 交换机是否收到消息发送方的信息,收到为true,未收到为false.
- cause: 当消息确认失败的时候,这个字符串会提供失败的原因.

注意: 在我们在SpringIoC容器中自定义一个RabbitTemplate的Bean对象的时候,在我们对RabbitTemplate对象进行DI注入的时候,由于Spring的Bean的优先级管理机制,Spring不再会调用RabbitMQ原生的Bean对象,而是调用我们自定义的Bean对象.
配置交换机和队列

@Bean
public DirectExchange confirmExchange(){return ExchangeBuilder.directExchange(Constant.CONFIRM_EXCHANGE).durable(true).build();
}
@Bean
public Queue confirmQueue(){return QueueBuilder.durable(Constant.CONFIRM_QUEUE).build();
}
@Bean
public Binding confirmBinding(@Qualifier("confirmExchange") DirectExchange exchange,@Qualifier("confirmQueue") Queue queue){return BindingBuilder.bind(queue).to(exchange).with("confirm");
}

之后在生产者使用@Resource注解注入我们写好的具有发送者确认机制的RabbitTemplate.之后进行消息发送.

@Resource
private RabbitTemplate confirmRabbitTemplate;
@RequestMapping("/confirm")
public String confirm(){CorrelationData correlationData = new CorrelationData("1");//指定消息id,用于生产者进行消息确认confirmRabbitTemplate.convertAndSend(Constant.CONFIRM_EXCHANGE,"confirm","confirm消息",correlationData);return "发送成功";
}

在发送消息的时候.我们需要指定消息的correlationData中的id,这个id可以让生产者进行对应消息的确认.
我们在之前的文章中也提到过发布确认模式,其中包含了一个确认监听器,叫做ConfirmListener接口,其中提供了handleAckhandleNack,一个用于处理消息确认成功时候的业务逻辑,一个是消息否定确认时候的业务逻辑.和ConfirmCallback.confirm方法中的ack参数类似.
3. 测试
我们使用Postman对接口进行调用,观察控制台.
在这里插入图片描述
我们看到发送方成功确认了消息的接收,说明了消息已经成功到达了交换机.
如果我们把交换机的名称改掉.

@RequestMapping("/confirm")
public String confirm(){CorrelationData correlationData = new CorrelationData("1");//指定消息id,用于生产者进行消息确认confirmRabbitTemplate.convertAndSend("Constant.CONFIRM_EXCHANGE","confirm","confirm消息",correlationData);return "发送成功";
}

在这里插入图片描述
我们看到发送方消息确认失败了,说明消息没有正确地到达交换机.

3.2 return回退模式

消息到达exchange之后,会根据路由规则进行匹配,把消息放入Queue中.Exchange到Queue的过程,如果一条消息服务被任何队列消费,可以选择把消息发回给生产者,我们可以设置一个一个返回回调方法,对消息进行处理.

  1. 配置RabbitMQ
    和confirm模式相同
  2. 设置返回回调逻辑并发送消息
    配置具有return退回模式的RabbitTemplate.
@Bean
public RabbitTemplate returnRabbitTemplate(ConnectionFactory connectionFactory){RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setMandatory(true);rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {@Overridepublic void returnedMessage(ReturnedMessage returned) {System.out.println("消息被退回:"+returned);}});return rabbitTemplate;
}

这里在使用RabbitTemplate中的setMandatory方法的时候,如果设置为true,这个属性就是告诉Broker,如果一条消息没有被任何一条队列消费,那么就触发ReturnsCallback.
其中ReturnedMessage中有以下属性:

public class ReturnedMessage {private final Message message;private final int replyCode;private final String replyText;private final String exchange;private final String routingKey;

message表示返回消息的对象,包含了消息体和消息的属性.
replyCode表示Broker提供的回复码,表示消息无法路由的原因,有点类似与错误码.
replyText表示无法路由消息的额外信息和错误描述.
exchange,routingKey分别表示交换机的名称和路由键.
发送消息

@Resource
private RabbitTemplate returnRabbitTemplate;
@RequestMapping("/return")
public String confirmReturn(){CorrelationData correlationData = new CorrelationData("2");returnRabbitTemplate.convertAndSend(Constant.RETURN_EXCHANGE,"return","return消息",correlationData);return "发送成功";
}
  1. 测试
    使用Postman调用接口,观察控制台日志.
    当发送成功的时候,我们看到控制台上什么都没有打印,而且我们查看管理界面,发现队列中已经有消息准备被消费了.
    在这里插入图片描述
    如果发送的路由关键字配置错误.
@RequestMapping("/return")
public String confirmReturn(){CorrelationData correlationData = new CorrelationData("2");returnRabbitTemplate.convertAndSend(Constant.RETURN_EXCHANGE,"return1","return消息",correlationData);return "发送成功";
}

我们再次进行测试,发现消息被回退:
在这里插入图片描述

4. 常见面试题

这一个板块,会涉及到一个非常常见的面试题.就是如何保证RabbitMQ消息的可靠性.
在这里插入图片描述
我们可以根据消息可能丢失的场景来解决:

  1. 消息从生产者到交换机期间发生丢失
    • 出现原因: 网络问题等
    • 解决办法: confirm确认模式
  2. 消息从交换机无法路由到指定队列
    • 出现原因: 交换机与队列配置错误
    • 解决办法: return确认模式
  3. 消息队列自身数据发生丢失
    • 出现原因: RabbitMQ服务器宕机
    • 解决办法: 消息,队列,交换机持久化
  4. 消费者异常,导致消息丢失
    • 出现原因: 消费者宕机,消费者业务逻辑异常
    • 解决办法: 消息确认.
      对于解决办法,我们需要根据上述的讲解进行具体描述

相关文章:

[RabbitMQ] 保证消息可靠性的三大机制------消息确认,持久化,发送方确认

🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...

aws服务--机密数据存储AWS Secrets Manager(1)介绍和使用

一、介绍 1、简介 AWS Secrets Manager 是一个完全托管的服务,用于保护应用程序、服务和 IT 资源中的机密信息。它支持安全地存储、管理和访问应用程序所需的机密数据,比如数据库凭证、API 密钥、访问密钥等。通过 Secrets Manager,你可以轻松管理、轮换和访问这些机密信息…...

Java设计模式笔记(一)

Java设计模式笔记(一) (23种设计模式由于篇幅较大分为两篇展示) 一、设计模式介绍 1、设计模式的目的 让程序具有更好的: 代码重用性可读性可扩展性可靠性高内聚,低耦合 2、设计模式的七大原则 单一职…...

Unity3d C# 实现一个基于UGUI的自适应尺寸图片查看器(含源码)

前言 Unity3d实现的数字沙盘系统中,总有一些图片或者图片列表需要点击后弹窗显示大图,这个弹窗在不同尺寸分辨率的图片查看处理起来比较麻烦,所以,需要图片能够根据容器的大小自适应地进行缩放,兼容不太尺寸下的横竖图…...

【es6进阶】vue3中的数据劫持的最新实现方案的proxy的详解

vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的,在大版本v3中彻底重写了这部分,使用了proxy这个数据代理的方式,来修复了v2中对数组和对象的劫持的遗留问题。 proxy是什么 Proxy 用于修改某些操作的默认行为&#xff0…...

w~视觉~3D~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/12538137 #SIF3D 通过两种创新的注意力机制——三元意图感知注意力(TIA)和场景语义一致性感知注意力(SCA)——来识别场景中的显著点云,并辅助运动轨迹和姿态的预测…...

IT服务团队建设与管理

在 IT 服务团队中,需要明确各种角色。例如系统管理员负责服务器和网络设备的维护与管理;软件工程师专注于软件的开发、测试和维护;运维工程师则保障系统的稳定运行,包括监控、故障排除等。通过清晰地定义每个角色的职责&#xff0…...

一文学习开源框架OkHttp

OkHttp 是一个开源项目。它由 Square 开发并维护,是一个现代化、功能强大的网络请求库,主要用于与 RESTful API 交互或执行网络通信操作。它是 Android 和 Java 开发中非常流行的 HTTP 客户端,具有高效、可靠、可扩展的特点。 核心特点 高效…...

自研芯片逾十年,亚马逊云科技Graviton系列芯片全面成熟

在云厂商自研芯片的浪潮中,亚马逊云科技无疑是最早践行这一趋势的先驱。自其迈出自研芯片的第一步起,便如同一颗石子投入平静的湖面,激起了层层涟漪,引领着云服务和云上算力向着更高性能、更低成本的方向演进。 早在2012年&#x…...

Stable Diffusion 3 部署笔记

SD3下载地址:https://huggingface.co/stabilityai/stable-diffusion-3-medium/tree/main https://huggingface.co/spaces/stabilityai/stable-diffusion-3-medium comfyui 教程: 深度测评:SD3模型表现如何?实用教程助你玩转Stabl…...

微信小程序WXSS全局样式与局部样式的使用教程

微信小程序WXSS全局样式与局部样式的使用教程 引言 在微信小程序的开发中,样式的设计与实现是提升用户体验的关键部分。WXSS(WeiXin Style Sheets)作为微信小程序的样式表语言,不仅支持丰富的样式功能,还能通过全局样式与局部样式的灵活运用,帮助开发者构建美观且易于维…...

Docker 部署 MongoDB

🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🍃 vue-uniapp-template 🌺 仓库主页: GitCode💫 Gitee &#x1f…...

Unity图形学之法线贴图原理

1.正常贴图:RGBA 4通道 每个通道取值范围 0-255 贴图里面取值是 0-1 2.法线贴图:法线怎么存入正常贴图的过程 每个通道里面存储的是一个向量(x,y,z,w) 通常我们会对应xyzw为rgba 存储值的范围也是0-1向量的取值范围是 -1到1法线怎么存入正常贴图的过程&…...

爬虫开发(5)如何写一个CSDN热门榜爬虫小程序

笔者 綦枫Maple 的其他作品,欢迎点击查阅哦~: 📚Jmeter性能测试大全:Jmeter性能测试大全系列教程!持续更新中! 📚UI自动化测试系列: SeleniumJava自动化测试系列教程❤ &#x1f4da…...

JVM系列之OOM观测准备

OOM, 全称 “Out Of Memory”,即内存用完的意思。JVM 因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时(可分配内存大于需要分配的内存), 就会抛出 java.lang.OutOfMemoryError。在实际的生产应用中,一旦…...

Qt | 开发技能提升档次哈

点击上方"蓝字"关注我们 01、Creator常用快捷键 >>> F1 查看帮助 F2 跳转到函数定义 Shift+F2 声明和定义之间切换 F3 查找下一个 F4 头文件和源文件之间切换 Ctrl+1 欢迎模式 Ctrl+2 编辑模…...

D79【 python 接口自动化学习】- python基础之HTTP

day79 requests模块发送请求 学习日期:20241125 学习目标:http定义及实战 -- requests模块进行get请求带参数&requests模块进行post请求 学习笔记: requests模块进行get请求 import requestsparams{"shouji":"130999…...

C++【日志模块中的writer类】前文中 循环队列用法

用到前文中的循环队列模板 /* ** File name: LogWriter.h ** Author: ** Date: 2024-11-4 ** Brief: 日志写入类 ** Note: 日志写入类,负责将日志写入文件和连接客户端。 ** Copyright (C) 1392019713qq.com All rights reserve…...

Linux:文件管理(一)——文件描述符fd

目录 一、文件基础认识 二、C语言操作文件的接口 1.> 和 >> 2.理解“当前路径” 三、相关系统调用 1.open 2.文件描述符 3.一切皆文件 4.再次理解重定向 一、文件基础认识 文件 内容 属性。换句话说,如果在电脑上新建了一个空白文档&#xff0…...

【C++初阶】第3课—类和对象(类的默认成员函数)

文章目录 1. 类的默认成员函数2. 构造函数3. 拷贝构造函数3.1 传值传参3.2 传值返回3.3 深拷贝和浅拷贝3.4 总结 4. 析构函数5. 赋值运算符重载5.1 运算符重载5.2 赋值运算符重载5.3 日期类的实现 6. 取地址运算符重载6.1 const 成员函数6.2 取地址运算符重载 1. 类的默认成员函…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

<6>-MySQL表的增删查改

目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表&#xf…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

2025季度云服务器排行榜

在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...