[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也提供了不同的确认应答的方式.一共有以下三种:
- 肯定确认:
Channel.basicAck(long deliveryTag, boolean multiple)
这种方法表示的是,消费者已经成功接收到消息,可以将其丢弃了.参数分别表示的是:- deliveryTag: 消息的id,是消息的唯一标识.
- multiple: 是否批量确认,值为true则会一次性ack所有小于或等于指定deliveryTag的消息.值为false,则只确认当前指定deliveryTag的消息.这种方式可以在一定程度上减少网络的开销.

- 否定确认:
Channel.basicReject(long deliveryTag, boolean requeue)
消费者拒绝接收这个消息.参数说明:- requeue: 表示在拒绝这条消息之后,这条消息会不会被放回到队列中.
- 否定确认:
Channel.basicNack(long deliveryTag, boolean multiple, boolean requeue)
次方法可以让消费者批量拒绝消息,参数说明:- multiple: 设置为true表示拒绝deliveryTag编号之前的所有未被消费消息.其他的参数和上面的类似.
1.3 代码示例
下面我们通过SpringBoot来演示消息确认的机制.
Spring-AMQP对消息确认的机制提供了三种策略.
public enum AcknowledgeMode {NONE,MANUAL,AUTO;
}
- NONE: 在这种模式之下,不管消费者是否成功处理了消息,RabbitMQ会自动确认消息,从RabbitMQ的队列中自动移除消息,如果消息处理失败,消息会发生丢失.
- AUTO: 如果不对这个配置项进行配置的话,这个就是默认的确认机制,在这种模式之下,消费者会自动确认消息,但是如果在消费者处理消息的过程中抛出了异常,该消息不会被确认.
- 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
- 配置消息确认机制
spring:application:name: rabbitmq-springrabbitmq:host: 182.92.204.253port: 5672username: jiangruijiapassword: *****virtual-host: /listener:simple:acknowledge-mode: auto
- 运行程序
我们发现消息在不停地重发,并且消息的tag值在不停地增加.

在管理界面上,我们发现未确认的消息一直为1,这是由于在消费者处理信息的时候出现了异常,这时候RabbitMQ会不断地对消息进行重发,消息不会被确认.导致了消息一直都是unacked状态.
1.3.3 MANUAL
- 修改确认机制
listener:simple:acknowledge-mode: manual
- 修改消费者逻辑为手动确认
首先我们正常处理消息,先不要抛出异常
@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);}
}


我们发现队列中的消息发生了积压,消息直接被退回的队列,控制台抛出了异常,消息没有被处理成功.
之后我们可以把代码改为出现异常的时候拒绝接收,其中basicReject的requeue属性配置为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的性能.这是因为写硬盘会拖慢速度.对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量.在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做⼀个权衡.
如果把交换机,队列,消息都设置为持久化,就可以保证百分之百数据不丢失了吗?答案是不是的.
- 如果消费者在消费队列消息的时候,设置的是自动确认,那么当消费者还没有来得及处理消息,消费者就宕机了,就会造成消息丢失,这种情况就需要我们把消息的确认机制设置为手动确认即可.
- 如果消息在RabbitMQ中进行持久化的时候,在存硬盘的过程中发生了宕机,消息还没有来得及落盘,那么这些消息也会消失.
这种情况如何解决呢?第一种方法就是使用仲裁队列,我们后面说明.第二种方法就是发送端引入发送方确认的机制来保证消息的可靠性.下面我们就来详细介绍发送方确认机制.
3. 发送方确认
在使用RabbitMQ的时候,可以通过持久化来解决RabbitMQ宕机而导致的消息丢失的问题,如果有一种情况,消息在到达服务器之前就已经发生了丢失,这时候消息根本没有到达RabbitMQ,持久化也解决不了这个问题.
RabbitMQ为我们提供了两种方案,一种是事务机制,一种是发送方确认机制.
由于事务机制比较消耗性能,一般情况下,我们不会使用事务.我们主要介绍发送方确认的机制.
RabbitMQ为我们提供了两个方式来控制消息可靠性的投递:
- confirm确认模式
- return退回模式
3.1 confirm确认模式
- 配置RabbitMQ
rabbitmq:host: 182.92.204.253port: 5672username: jiangruijiapassword: ******virtual-host: /listener:simple:acknowledge-mode: manualpublisher-confirm-type: correlated #消息发送确认
- 设置确认回调逻辑并发送消息
无论消息发送是成功还是失败,都会调用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接口,其中提供了handleAck和handleNack,一个用于处理消息确认成功时候的业务逻辑,一个是消息否定确认时候的业务逻辑.和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的过程,如果一条消息服务被任何队列消费,可以选择把消息发回给生产者,我们可以设置一个一个返回回调方法,对消息进行处理.
- 配置RabbitMQ
和confirm模式相同 - 设置返回回调逻辑并发送消息
配置具有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 "发送成功";
}
- 测试
使用Postman调用接口,观察控制台日志.
当发送成功的时候,我们看到控制台上什么都没有打印,而且我们查看管理界面,发现队列中已经有消息准备被消费了.

如果发送的路由关键字配置错误.
@RequestMapping("/return")
public String confirmReturn(){CorrelationData correlationData = new CorrelationData("2");returnRabbitTemplate.convertAndSend(Constant.RETURN_EXCHANGE,"return1","return消息",correlationData);return "发送成功";
}
我们再次进行测试,发现消息被回退:

4. 常见面试题
这一个板块,会涉及到一个非常常见的面试题.就是如何保证RabbitMQ消息的可靠性.

我们可以根据消息可能丢失的场景来解决:
- 消息从生产者到交换机期间发生丢失
- 出现原因: 网络问题等
- 解决办法: confirm确认模式
- 消息从交换机无法路由到指定队列
- 出现原因: 交换机与队列配置错误
- 解决办法: return确认模式
- 消息队列自身数据发生丢失
- 出现原因: RabbitMQ服务器宕机
- 解决办法: 消息,队列,交换机持久化
- 消费者异常,导致消息丢失
- 出现原因: 消费者宕机,消费者业务逻辑异常
- 解决办法: 消息确认.
对于解决办法,我们需要根据上述的讲解进行具体描述
相关文章:
[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 用于修改某些操作的默认行为࿰…...
w~视觉~3D~合集3
我自己的原文哦~ https://blog.51cto.com/whaosoft/12538137 #SIF3D 通过两种创新的注意力机制——三元意图感知注意力(TIA)和场景语义一致性感知注意力(SCA)——来识别场景中的显著点云,并辅助运动轨迹和姿态的预测…...
IT服务团队建设与管理
在 IT 服务团队中,需要明确各种角色。例如系统管理员负责服务器和网络设备的维护与管理;软件工程师专注于软件的开发、测试和维护;运维工程师则保障系统的稳定运行,包括监控、故障排除等。通过清晰地定义每个角色的职责࿰…...
一文学习开源框架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 …...
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自动化测试系列教程❤ 📚…...
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.再次理解重定向 一、文件基础认识 文件 内容 属性。换句话说,如果在电脑上新建了一个空白文档࿰…...
【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. 类的默认成员函…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
Mysql故障排插与环境优化
前置知识点 最上层是一些客户端和连接服务,包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念,为通过安全认证接入的客户端提供线程。同样在该层上可…...
react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架
1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...
当下AI智能硬件方案浅谈
背景: 现在大模型出来以后,打破了常规的机械式的对话,人机对话变得更聪明一点。 对话用到的技术主要是实时音视频,简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术,开发自己的大模型。商用方案多见为字节、百…...
【字节拥抱开源】字节团队开源视频模型 ContentV: 有限算力下的视频生成模型高效训练
本项目提出了ContentV框架,通过三项关键创新高效加速基于DiT的视频生成模型训练: 极简架构设计,最大化复用预训练图像生成模型进行视频合成系统化的多阶段训练策略,利用流匹配技术提升效率经济高效的人类反馈强化学习框架&#x…...
在MobaXterm 打开图形工具firefox
目录 1.安装 X 服务器软件 2.服务器端配置 3.客户端配置 4.安装并打开 Firefox 1.安装 X 服务器软件 Centos系统 # CentOS/RHEL 7 及之前(YUM) sudo yum install xorg-x11-server-Xorg xorg-x11-xinit xorg-x11-utils mesa-libEGL mesa-libGL mesa-…...
Linux——TCP和UDP
一、TCP协议 1.特点 TCP提供的是面向连接、可靠的、字节流服务。 2.编程流程 (1)服务器端的编程流程 ①socket() 方法创建套接字 ②bind()方法指定套接字使用的IP地址和端口。 ③listen()方法用来创建监听队列。 ④accept()方法处理客户端的连接…...

