RabbitMQ发布确认和消息回退(6)

news/2024/5/20 2:15:08

概念

发布确认原理

生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。

confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息,生产者应用程序同样可以在回调方法中处理该 nack 消息。

两种模式

1.简单确认模式(Simple Publisher Confirm)

在简单确认模式下,每次生产者发送一条消息到 RabbitMQ,都会立即等待 RabbitMQ 返回一个确认消息。如果消息成功发送到 RabbitMQ 服务器上的交换机,并且至少一个队列接收到了消息,RabbitMQ 就会返回一个确认消息给生产者。否则,如果消息发送失败,则会返回一个 Nack 消息。在这种模式下,生产者可以针对每一条消息都进行确认处理,确保消息是否被正确地发送到了 RabbitMQ。

3.批量确认模式(Publisher Confirm with Batch)

在批量确认模式下,生产者可以将一批消息发送到 RabbitMQ,然后等待一段时间后再收到确认消息。这样可以降低每条消息发送的确认成本,并提高性能。批量确认模式通过指定一个大小来定义批量确认的数量,当达到指定的数量后,RabbitMQ 会一次性发送确认消息给生产者。这种模式适用于需要发送大量消息的场景,可以减少确认消息的数量,提高消息发送的效率。

这两种发布确认模式在实现上有一些不同,可以根据实际的业务需求和性能要求来选择合适的模式。简单确认模式更适用于需要对每一条消息进行实时确认的场景,而批量确认模式适用于需要发送大量消息并且希望降低确认消息成本的场景。

开启方法

-- 确认模式配置

spring.rabbitmq.publisher-confirm-type: correlated

  • NONE

禁用发布确认模式,是默认值。

  • CORRELATED

发布消息成功到交换器后会触发回调方法。

  • SIMPLE

测试有两种效果,其一效果和 CORRELATED 值一样会触发回调方法,其二在发布消息成功后使用 rabbitTemplate 调用 waitForConfirms 或 waitForConfirmsOrDie 方法等待 broker 节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是 waitForConfirmsOrDie 方法如果返回 false 则会关闭 channel,则接下来无法发送消息到 broker。(相当于单一发布)

spring:# 配置RabbitMQrabbitmq:host: 192.168.0.70port: 5674username: guestpassword: guest# 虚拟主机virtual-host: my_vhost# 开启确认模式publisher-confirm-type: correlated

 测试

为了方便测试,此机制单独编写类去测试,使用的模式为路由模式(其他模式也可以)

1.创建回调函数

package com.model.callback;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/22 17:26* @Description: TODO*/
@Component
@Slf4j
public class MyConfirmCallBack implements RabbitTemplate.ConfirmCallback {/*** 被调用的回调方法* @param correlationData 相关配置信息* @param ack 交换机是否成功收到消息 可以根据 ack 做相关的业务逻辑处理* @param cause 失败原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {System.out.println("消息推送回调:配置信息="+correlationData+";结果="+ack+"失败原因="+cause);if(ack){//消息推送成功System.out.println("消息推送成功");}else{System.out.println("消息推送失败:" + cause);}}
}

回调函数会在消息推送到队列后调用

 2.配置回调函数

将上述回调函数设置到mq的配置中,这里再RabbitmqConfig文件中配置

package com.model.config;import com.model.callback.MyConfirmCallBack;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/18 17:28* @Description: TODO*/
@Configuration
public class RabbitmqConfig {@Value("${rabbitmq.work.queue}")private String workQueue;@Resourceprivate MyConfirmCallBack myConfirmCallBack;/*** 工作模式的队列* @return 队列*/@Bean(name = "workQueue")public Queue getWorkQueue(){return QueueBuilder.durable(workQueue).build();}@Beanpublic RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate();rabbitTemplate.setConnectionFactory(connectionFactory);// 设置开启 Mandatory 强制执行调用回调函数rabbitTemplate.setMandatory(true);//设置回调rabbitTemplate.setConfirmCallback(myConfirmCallBack);//设置回退回调return rabbitTemplate;}}

 3.创建交换机和队列

这里创建ConfirmConfig配置文件与其他队列进行区分

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: Haiven* @Time: 2024/4/22 17:22* @Description: TODO*/
@Configuration
public class ConfirmConfig {/*** 测试发布确认的交换机* @return exchange*/@Bean(name = "confirmExchange")public Exchange getConfirmExchange(){return ExchangeBuilder.directExchange("exchange_confirm").build();}/*** 队列* @return queue*/@Bean(name = "confirmQueue")public Queue getConfirmQueue(){return QueueBuilder.durable("queue_confirm").build();}/*** 绑定队列* @return binding*/@Beanpublic Binding getConfirmBinding(){return BindingBuilder.bind(getConfirmQueue()).to(getConfirmExchange())//路由键 队列1接收debug级别的消息.with("confirm").noargs();}}

 4.创建消费者

package com.model.listener;import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/22 17:31* @Description: TODO*/
@Component
public class ConfirmConsumer {@RabbitListener(queues = {"queue_confirm"})public void routingConfirm(String msg){System.out.println("消费者 -confirm- 接收消息:" + msg);}
}

 5.发送并接收消息

package com.model.controller;import com.code.domain.Response;
import com.model.service.RabbitService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 9:46* @Description: TODO*/
@RestController
@RequestMapping("/producer")
public class ProducerController {@Resourceprivate RabbitService rabbitService;@GetMapping("/simple")public Response<Void> simple(String msg){boolean res = rabbitService.simple(msg);return res ? Response.success() : Response.fail();}@GetMapping("/work")public Response<Void> work(String msg){boolean res = rabbitService.work(msg);return res ? Response.success() : Response.fail();}@GetMapping("/sub")public Response<Void> sub(String msg){boolean res = rabbitService.sub(msg);return res ? Response.success() : Response.fail();}@GetMapping("/routing")public Response<Void> routing(String msg, String type){boolean res = rabbitService.routing(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/topic")public Response<Void> topic(String msg, String type){boolean res = rabbitService.topic(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/confirm")public Response<Void> confirm(String msg, String type){boolean res = rabbitService.confirm(msg, type);return res ? Response.success() : Response.fail();}
}
package com.model.service.impl;import com.model.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 10:51* @Description: TODO*/
@Service
@Slf4j
public class RabbitServiceImpl implements RabbitService {@Resourceprivate RabbitTemplate rabbitTemplate;@Value("${rabbitmq.simple.queue}")private String simpleQueue;@Value("${rabbitmq.work.queue}")private String workQueue;@Overridepublic boolean simple(String msg) {try {rabbitTemplate.convertAndSend(simpleQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean work(String msg) {try {rabbitTemplate.convertAndSend(workQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean sub(String msg) {try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_sub","", msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean routing(String msg, String type) {System.out.println("理由模式发送消息:msg="+msg+",type="+type+"");try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_routing",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean topic(String msg, String type) {System.out.println("主题模式发送消息:msg="+msg+",type="+type+"");try {//主题模式会根据 type的通配符进行分发rabbitTemplate.convertAndSend("exchange_topic",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean confirm(String msg, String type) {System.out.println("发布确认模式发送消息:msg="+msg+",type="+type+"");try {rabbitTemplate.convertAndSend("exchange_confirm",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}
}

 发送消息

 接收消息

 发送成功后回调函数会执行,即使消费者没有消费该消息,回调函数仍然会执行

 消息回退

当第二条消息推送后消费者是没有消费消息的,虽然推送成功,但是却被丢弃了,而此时生产者需要知道此消息是否被消费成功,所有就使用到了消息回退机制

spring.rabbitmq.publisher-returns=true

1.设置回调函数

MyReturnCallBack

package com.model.callback;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/23 10:16* @Description: TODO*/
@Component
@Slf4j
public class MyReturnCallBack implements RabbitTemplate.ReturnCallback {/**** @param msg 消息对象* @param errCode 错误码* @param errMsg 错误信息* @param exchange 交换机* @param rout 路由键*/@Overridepublic void returnedMessage(Message msg, int errCode, String errMsg, String exchange, String rout) {log.debug("消息对象={},错误码={},错误消息={},交换机={},路由键={}", msg, errCode, errMsg, exchange, rout);}
}

 此函数会在消息被丢弃或者消费失败后回调

2.设置到配置中

在RabbitmqConfig配置文件设置

package com.model.config;import com.model.callback.MyConfirmCallBack;
import com.model.callback.MyReturnCallBack;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/18 17:28* @Description: TODO*/
@Configuration
public class RabbitmqConfig {@Value("${rabbitmq.work.queue}")private String workQueue;@Resourceprivate MyConfirmCallBack myConfirmCallBack;@Resourceprivate MyReturnCallBack myReturnCallBack;/*** 工作模式的队列* @return 队列*/@Bean(name = "workQueue")public Queue getWorkQueue(){return QueueBuilder.durable(workQueue).build();}@Beanpublic RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate();rabbitTemplate.setConnectionFactory(connectionFactory);// 设置开启 Mandatory 强制执行调用回调函数rabbitTemplate.setMandatory(true);//设置发布确认rabbitTemplate.setConfirmCallback(myConfirmCallBack);//设置消息回退rabbitTemplate.setReturnCallback(myReturnCallBack);//设置回退回调return rabbitTemplate;}}

先注入,在设置到rabbitTemplate对象中,这样发送消息时就可以回调

3.发送并接收消息

此处使用confirm交换机测试

发送一条没有路由的消息

 

 此时消息推送成功,但是没有被消费者消费,而是被丢弃,所有消息回退的回调函数执行:

消息对象=(

        Body:'消息',

        MessageProperties : [

                headers={},

                contentType=text/plain,

                contentEncoding=UTF-8,                                           

                contentLength=0,

                receivedDeliveryMode=PERSISTENT,

                priority=0, deliveryTag=0])

错误码=312

错误消息=NO_ROUTE

交换机=exchange_confirm

路由键=unknown

 发送一条路由的消息

 消息推送到交换机成功,并被成功消费,回退消息的回调函数未执行

备份交换机

有了发布确认和回退消息,我们获得了对无法投递消息的感知能力,在生产者的消息无法被投递时发现并处理。但有时候,我们并不知道该如何处理这些无法路由的消息,最多打个日志,然后触发报警,再来手动处理。而通过日志来处理这些无法路由的消息是很不优雅的做法,特别是当生产者所在的服务有多台机器的时候,手动复制日志会更加麻烦而且容易出错。而且设置参数会增加生产者的复杂性,需要添加处理这些被退回的消息的逻辑。如果既不想丢失消息,又不想增加生产者的复杂性,就可以使用备份交换机

 备份交换机可以理解为 RabbitMQ 中交换机的“备份”,当我们为某一个交换机声明一个对应的备份交换机时,就是为它创建一个 备份,当交换机接收到一条不可路由消息时,将会把这条消息转发到备份交换机中,由备份交换机来进行转发和处理,通常备份交换机的类型为 Fanout ,这样就能把所有消息都投递到与其绑定的队列中,然后我们在备份交换机下绑定一个队列,这样所有那些原交换机无法被路由的消息,就会都进 入这个队列了。当然,我们还可以建立一个报警队列,用独立的消费者来进行监测和报警。

1.创建备份交换机

为了方便区分,这里新建一个配置类ConfirmBackupConfig,用于创建虚拟机和队列,备份交换机的类型一定要为fanout

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;/*** @Author: Haiven* @Time: 2024/4/23 15:07* @Description: TODO*/
@Configuration
public class ConfirmBackupConfig {/*** exchange_confirm 交换机的备份交换机* @return exchange*/@Bean(name = "confirmBackupExchange")public Exchange getConfirmBackupExchange(){return ExchangeBuilder.fanoutExchange("exchange_confirm_backup").build();}@Bean("confirmBackupQueue")public Queue getConfirmBackupQueue(){return QueueBuilder.durable("queue_confirm_backup").build();}@Bean("confirmBackupBinding")public Binding getConfirmBackupBinding(){return BindingBuilder.bind(getConfirmBackupQueue()).to(getConfirmBackupExchange()).with("").noargs();}
}

 2.绑定备份交换机

这里我们用上面的消息回退测试用的交换机进行测试,就不额外再创建了,在ConfirmConfig配置文件中直接绑定:

.withArgument("alternate-exchange", "exchange_confirm_backup")

在创建交换机的时候直接指定 alternate-exchange,exchange_confirm_backup为备份交换的名称

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;/*** @Author: Haiven* @Time: 2024/4/22 17:22* @Description: TODO*/
@Configuration
public class ConfirmConfig {/*** 测试发布确认的交换机* @return exchange*/@Bean(name = "confirmExchange")public Exchange getConfirmExchange(){return ExchangeBuilder.directExchange("exchange_confirm").withArgument("alternate-exchange", "exchange_confirm_backup").durable(true).build();}/*** 队列* @return queue*/@Bean(name = "confirmQueue")public Queue getConfirmQueue(){return QueueBuilder.durable("queue_confirm").build();}/*** 绑定队列* @return binding*/@Beanpublic Binding getConfirmBinding(){return BindingBuilder.bind(getConfirmQueue()).to(getConfirmExchange())//路由键 队列1接收debug级别的消息.with("confirm").noargs();}}

 由于之前创建exchange_confirm交换机的时候没有指定备份交换机,所以这里要先将该交换机删掉,然后重新创建,备份交换机一定要在创建的时候指定

 进入控制台删除,不想删可创建新的交换机用于测试

 3.备份交换机消费者

直接在ConfirmConsumer配置文件中声明confirmBackupConsumer

package com.model.listener;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/22 17:31* @Description: TODO*/
@Component
@Slf4j
public class ConfirmConsumer {@RabbitListener(queues = {"queue_confirm"})public void confirmConsumer(String msg){System.out.println("消费者 -confirm- 接收消息:" + msg);}@RabbitListener(queues = {"queue_confirm_backup"})public void confirmBackupConsumer(String msg){log.debug("消费者 -- 备份队列 -- 接收消息:" + msg);}
}

4.发送消息测试

发送一条没有路由的消息

1.可以看到发布确认的消息回调执行,说明已经推送到交换机

2.但是消息回退的回调没有执行,但该消息没有被confirm的消费者消费

3.该消息被备份交换机的消费者消费

有了备份交换机,消息如果消费失败,消息不会回退,而会被备份队列的消费者接收


http://www.mrgr.cn/p/23567480

相关文章

第一个大型汽车ITU-T车载语音通话质量实验室投入使用

中国汽车行业蓬勃发展&#xff0c;尤其是新能源汽车风起云涌&#xff0c;无论是国内还是海外需求旺盛的趋势下&#xff0c;除乘用车等紧凑型车外&#xff0c;中型汽车如MPV、小巴、小型物流车&#xff0c;大型汽车如重卡、泥头车等亦加入了手机互联、智驾的科技行列&#xff0c…

LT9611UXC双端口 MIPI DSI/CSI 转 HDMI2.0,带音频

1. 说明 LT9611UXC 是一款高性能 MIPI DSI/CSI 至 HDMI2.0 转换器。MIPI DSI/CSI 输入具有可配置的单端口或双端口&#xff0c;具有 1 个高速时钟通道和 1~4 个高速数据通道&#xff0c;工作速率最高为 2Gbps/通道&#xff0c;可支持高达 16Gbps 的总带宽。 LT9611UXC 支持突发…

四:物联网ARM开发

一&#xff1a;ARM体系结构概述 1&#xff1a;控制外设led灯还有一些按键这些就要用到gpio&#xff0c;采集传感器的数据需要adc进行转化数据格式&#xff0c;特殊的外设和传感器是通过特殊的协议接口去进行连接的比如一些轴传感器和主控器的连接是通过spi&#xff0c;IIC 控制…

深入解析YOLOv2

深入解析YOLOv2 引言 目标检测是计算机视觉中的一个核心问题&#xff0c;它旨在识别图像中所有感兴趣的目标&#xff0c;并给出它们的类别和位置。近年来&#xff0c;随着深度学习技术的发展&#xff0c;目标检测领域取得了巨大的进步。YOLO&#xff08;You Only Look Once&a…

maven-idea新建和导入项目

全局配置 新建项目 需要新建的文件夹 src/testsrc/test/javasrc/main/java 注&#xff1a;1、新建Java-class&#xff0c;输入.com.hello.hellomaven 2、快捷键psvm显示 public static void main(String[] args) {.... } package com.hello;public class hellomaven {publ…

Pytorch 的实际应用 学习笔记

一. 模型的下载 weights为false时则为没有提前经过训练的模型&#xff0c;为true时则经过了提前训练 vgg16_false torchvision.models.vgg16(weightsFalse) vgg16_true torchvision.models.vgg16(weightsTrue) 打印 二. 模型的修改 &#xff08;1&#xff09;添加操作 …

【机器学习】集成学习:强化机器学习模型与创新能的利器

集成学习&#xff1a;强化机器学习模型预测性能的利器 一、集成学习的核心思想二、常用集成学习方法Bagging方法Boosting方法Stacking方法 三、集成学习代表模型与实现四、总结与展望 在大数据时代的浪潮下&#xff0c;机器学习模型的应用越来越广泛&#xff0c;而集成学习作为…

AJAX——黑马头条-数据管理平台项目

1.项目介绍 功能&#xff1a; 登录和权限判断查看文章内容列表&#xff08;筛选&#xff0c;分页&#xff09;编辑文章&#xff08;数据回显&#xff09;删除文章发布文章&#xff08;图片上传&#xff0c;富文本编辑器&#xff09; 2.项目准备 技术&#xff1a; 基于Bootst…

读天才与算法:人脑与AI的数学思维笔记11_算法如何思考

读天才与算法:人脑与AI的数学思维笔记11_算法如何思考1. 创造力 1.1. 创建一种算法,其首要任务是放弃已知的所有艺术风格,然后判断由算法自己所产生的艺术品是否具有与所有艺术风格都截然不同的特性,即真正独树一帜的艺术风格 1.2. 抗性模型同样适用于人类创造力代码的引导…

考研数学|张宇《1000题》正常用多久刷完?

考研数学1000题的刷题时间因人而异&#xff0c;主要取决于以下几个因素。 首先是个人基础&#xff0c;如果你的数学基础较好&#xff0c;对考研数学的知识点已经比较熟悉&#xff0c;刷题速度可能会更快。 其次是每天投入时间&#xff1a;你每天能够投入多少时间来刷题也会影…

Hadoop伪分布式平台搭建

搭建Hadoop伪分布式环境是在单台机器上模拟完整的Hadoop分布式系统&#xff0c;使得所有的Hadoop守护进程&#xff08;如NameNode、DataNode、ResourceManager、NodeManager等&#xff09;都在同一台机器上运行。这样可以在一台机器上体验Hadoop的分布式特性&#xff0c;适合学…

python使用opencv对图像的基本操作(2)

13.对多个像素点进行操作&#xff0c;使用数组切片方式访问 img[i,:] img[j,:] #将第j行的数值赋值给第i行 img[-2,:]或img[-2] #倒数第二行 img[:,-1] #最后一列 img[50:100,50:100] #50-100行&#xff0c;50-100列&#xff08;不包括第100行和第100列&#xff09; img[:100…

防盗链在nginx中如何配置,简单演示403forbidden的效果

一、使用场景&#xff1a; 资源被其他网站无端盗用 服务器压力无端增加 二、实现方法 1.valid_referers指令可以检测被访问资源从哪个地址来 2.通过referer头字段判断 3.若为空&#xff0c;报403错误 nginx的准备工作&#xff1a; 可以看 虚拟机中使用LNMP模拟跨域并结合…

北京车展“第一枪”:长安汽车发布全球首款量产可变新汽车

4月25日&#xff0c;万众瞩目的2024北京国际汽车展览会在中国国际展览中心如期而至。作为中国乃至全球汽车行业的盛宴&#xff0c;本次车展也吸引了无数业内人士的高度关注。 此次北京车展以“新时代 新汽车”为主题&#xff0c;汇聚了1500余家主流车企及零部件制造商&#xff…

数据结构-二叉树-堆(二)

一、建堆的时间复杂度问题 1、除了向上调整建堆&#xff0c;我们还可以向下调整建堆。不能在根上直接开始向下调整。这里的条件就是左右子树必须都是大堆或者小堆。我们可以倒着往前走&#xff0c;可以从最后一个叶子开始调整。但是从叶子开始调整没有意义。所以我们可以从倒数…

Java基础之JVM基础调优与常见问题

常见命令 以下命令的介绍&#xff0c;全部在jdk8环境下运行的&#xff1b; jps ☆☆☆☆☆ 查看当前运行的进程号&#xff1b; jmap ☆☆☆ jmap命令可以查看jvm的内存信息&#xff0c;class对应的实例个数以及占用的内存大小 jmap -histo 查看当前java进程 [rdVM-8-12-c…

微信小程序关于主包大小不能超过1.5MB的问题

常规的解决办法有以下几种 1、把资源文件改成远程服务器的&#xff0c;比如png这些 2、进入如图的分析页面&#xff0c;能明确知道你哪个插件包太大&#xff0c;我这里之前echart的包就1mb&#xff0c;现在给他缩减到了500kb的样子 3、解决vant等npm包太大的问题&#xff0c…

Linux——NFS网络文件系统

在生产环境中共享宿主目录可以用于集中管理账户 一、存储设备 DAS 是直连存储相当于移动硬盘 NAS 是网络文件系统&#xff0c;挂载后可以直接访问 SAN 存储区域网络 IPSAN 网线连接 共享的是设备&#xff0c;需要挂载后分区使用 FCSAN 光纤连接 二、服务的管理 1、安…

【C++杂货铺】多态

目录 &#x1f308;前言&#x1f308; &#x1f4c1;多态的概念 &#x1f4c1; 多态的定义及实现 &#x1f4c2; 多态的构成条件 &#x1f4c2; 虚函数 &#x1f4c2; 虚函数重写 &#x1f4c2; C11 override 和 final &#x1f4c2; 重载&#xff0c;覆盖&#xff08;重写…

《HCIP-openEuler实验指导手册》1.6 Apache静态资源配置

知识点 常用用途&#xff1a; 软件仓库镜像及提供下载服务&#xff1a; 配置步骤 删除网站主目录中的文件&#xff08;本实验机目录为/home/source ip为192.168.12.137 端口为81&#xff09; cd /home/source rm -rf *在主目录中新建6个文件夹如下图 mkdir test{1..6}新建…