【Rabbitmq篇】高级特性----TTL,死信队列,延迟队列
目录
一.TTL
1.设置消息的TTL
2.设置队列的TTL
3.俩者区别
二.死信队列
定义:
消息成为死信的原因:
1.消息被拒绝(basic.reject 或 basic.nack)
2.消息过期(TTL)
3.队列达到最大长度
编辑
死信队列的应用场景
三.延迟队列
定义:
应用场景
实现一:TTL+死信队列
实现二:延迟队列插件
编辑 俩种实现对比:
一.TTL
TTL(TimetoLive,过期时间),即过期时间.RabbitMQ可以对消息和队列设置TTL.
它代表消息的存活时间。当一条消息被发送到 RabbitMQ 队列后,TTL 可以限制消息在队列中能够存活的最长时间。一旦消息在队列中的存活时间超过了 TTL 设定的值,消息就会被自动删除。
咱们在网上购物,经常会遇到一个场景,当下单超过24小时还未付款,订单会被自动取消
申请退款之后,超过7天未被处理,则自动退款这就是设置了TTL
目前有俩种方式可以设置消息的TTL
一是设置队列的TTL,队列中所有消息都有相同的过期时间.
二是对消息本身进行单独设置,每条消息的TTL可以不同.如果两种方法一起使用,则消息的TTL以两者之间较小的那个数值为准.
先看针对每条消息设置TTL
1.设置消息的TTL
1)配置交换机&队列
//TTLpublic static final String TTL_QUEUE = "ttl.queue";public static final String TTL_EXCHANGE = "ttl.exchange";
//TTL@Bean("ttlQueue")public Queue ttlQueue() {return QueueBuilder.durable(Constants.TTL_QUEUE).build();}@Bean("ttlExchange")public DirectExchange ttlExchange() {return ExchangeBuilder.directExchange(Constants.TTL_EXCHANGE).build();}@Bean("ttlBinding")public Binding ttlBinding(@Qualifier("ttlQueue") Queue queue,@Qualifier("ttlExchange") DirectExchange directExchange) {return BindingBuilder.bind(queue).to(directExchange).with("ttl");}
2)发送消息
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setExpiration("10000");//单位为毫秒,设置10秒后过期return message;}};
MessagePostProcessor中 重写postProcessMessage 方法可以设置它的过期时间
这里使用了lambda表达式
@RequestMapping("/ttl")public String ttl() {System.out.println("ttl!!!");rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"ttl","ttl test...", message -> {message.getMessageProperties().setExpiration("10000");//单位为毫秒,设置10秒后过期return message;});return "消息发送成功";}
3)测试
十秒钟过后结果:
自动消失了这条消息
如果不设置TTL,则表示此消息不会过期;如果将TTL设置为0,则表示除非此时可以直接将消息投递到消费者,否则该消息会被立即丢弃.
2.设置队列的TTL
设置队列TTL的方法是在创建队列时,加⼊ x-message-ttl 参数实现的,单位是毫秒
1)配置队列和绑定关系
public static final String TTL_QUEUE2= "ttl.queue2";
@Bean("ttlQueue2")public Queue ttlQueue2() {return QueueBuilder.durable(Constants.TTL_QUEUE2).ttl(20000).build();}@Bean("ttlBinding2")public Binding ttlBinding2(@Qualifier("ttlQueue2") Queue queue,@Qualifier("ttlExchange") DirectExchange directExchange) {return BindingBuilder.bind(queue).to(directExchange).with("ttl");}
2)发送消息
@RequestMapping("/ttl2")public String ttl2() {System.out.println("ttl2!!!");rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"ttl","ttl2 test 20s...");return "消息发送成功";}
3)测试
20s后结果:
因为绑定的交换机是同一个,并且routingKey也是同一个,所有会向Q1和Q2同时发消息
我们发送的普通消息 并没有设置带有TTL的消息
所有Q1并不会消失 而Q2设置了队列的TTL,所以会消失。
4)测试2
如果发送消息的TTL(10s过期)给设置了TTL(20s过期)的队列,会发生什么结果?
结论:10s后俩个队列里面的消息全部消失,所有可得知取 它最短的时间过期
3.俩者区别
设置队列TTL属性的方法,一旦消息过期,就会从队列中删除
设置消息TTL的方法,即使消息过期,也不会马上从队列中删除,而是在即将投递到消费者之前进行判定的.
为什么这两种方法处理的方式不⼀样?
因为设置队列过期时间,队列中已过期的消息肯定在队列头部,RabbitMQ只要定期从队头开始扫描是否有过期的消息即可.
而设置消息TTL的方式每条消息的过期时间不同,如果要删除所有过期消息需要扫描整个队列,所以不如等到此消息即将被消费时再判定是否过期,如果过期再进行删除即可.
测试!!!
@RequestMapping("/ttl")public String ttl() {System.out.println("ttl!!!");rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"ttl","ttl test... 3os", message -> {message.getMessageProperties().setExpiration("30000");//单位为毫秒,设置30秒后过期return message;});rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"ttl","ttl test... 10s", message -> {message.getMessageProperties().setExpiration("10000");//单位为毫秒,设置10秒后过期return message;});return "消息发送成功";}
先发一个设置30s过期的信息,再发一条设置10过期的信息 看看结果如何?
結果:
20s后首先是Q2(设置了20s的TTL队列)的队列全部消失
而Q1设置了10s的消息没有消失,而是等到30s过,再一起消失了
二.死信队列
定义:
当消息在一个普通队列中变成 “死信”(无法被正常消费的消息)时,这些消息会被重新路由到死信队列中。
有死信(DL),自然就有死信队列.当消息在⼀个队列中变成死信之后,它能被重新被发送到另⼀个交换器中,这个交换器就是DLX( Dead Letter Exchange ),绑定DLX的队列,就称为死信队列(DeadLetterQueue,简称DLQ).
声明队列和交换机与绑定关系
//正常队列public static final String NORMAL_QUEUE= "normal.queue";public static final String NORMAL_EXCHANGE = "normal.exchange";//死信队列public static final String DL_QUEUE= "dl.queue";public static final String DL_EXCHANGE = "dl.exchange";
package com.bite.extensions.config;import com.bite.extensions.constant.Constants;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DLConfig {@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constants.NORMAL_QUEUE).deadLetterExchange(Constants.DL_EXCHANGE).deadLetterRoutingKey("dlx").build();}@Bean("normalExchange")public DirectExchange normalExchange() {return ExchangeBuilder.directExchange(Constants.NORMAL_EXCHANGE).durable(true).build();}@Bean("normalBinding")public Binding normalBinding(@Qualifier("normalQueue") Queue queue,@Qualifier("normalExchange") DirectExchange directExchange) {return BindingBuilder.bind(queue).to(directExchange).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).durable(true).build();}@Bean("dlBinding")public Binding dlBinding(@Qualifier("dlQueue") Queue queue,@Qualifier("dlExchange") DirectExchange directExchange) {return BindingBuilder.bind(queue).to(directExchange).with("dlx");}
}
消息成为死信的原因:
1.消息被拒绝(basic.reject 或 basic.nack)
消息被拒绝(basic.reject 或 basic.nack):消费者在接收到消息后可以明确地拒绝该消息,并且可以选择是否将消息重新放回队列。如果消费者拒绝消息且不重新放回队列,同时该队列配置了死信交换机(Dead - Letter - Exchange,DLX),那么消息就会被发送到死信队列。
消费者:
package com.bite.extensions.listener;import com.bite.extensions.constant.Constants;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;@Component
public class DLListener {@RabbitListener(queues = Constants.NORMAL_QUEUE)public void handleMessage(Message message, Channel channel) throws Exception {//消费者逻辑long deliverTag = message.getMessageProperties().getDeliveryTag();try {System.out.printf("[normal.queue]接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);//业务逻辑处理System.out.println("业务逻辑处理!");int num = 3/0;System.out.println("业务逻辑完成!");//肯定确认channel.basicAck(deliverTag,false);} catch (Exception e) {//否定确认channel.basicNack(deliverTag,false,false);//requeue为false,则变成死信队列}}@RabbitListener(queues = Constants.DL_QUEUE)public void dlxHandleMessage(Message message, Channel channel) throws Exception {System.out.printf("[dl.queue]接收到信息: %s, deliveryTag: %d\n", new String(message.getBody(), "UTF-8"), message.getMessageProperties().getDeliveryTag(););}
}
测试:
2.消息过期(TTL)
消息过期(TTL):如果消息在队列中的存活时间(通过设置 TTL)超过了限定时间,消息会变成死信。前提是队列配置了死信交换机,过期消息会被发送到死信队列。
设置10s过期队列:
@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constants.NORMAL_QUEUE).deadLetterExchange(Constants.DL_EXCHANGE).deadLetterRoutingKey("dlx").ttl(10*1000).build();}
生产者:
@RequestMapping("/dl")public String dl() {System.out.println("dl test !!!");rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","dl test 10s...");return "消息发送成功";}
测试:
10s后:
3.队列达到最大长度
队列达到最大长度:当队列设置了最大长度限制,并且消息数量达到这个限制时,新进入队列的消息会导致最早的消息被挤出队列。如果被挤出的消息对应的队列配置了死信交换机,这些消息会成为死信并被发送到死信队列。
设置队列最大容量为10:
@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constants.NORMAL_QUEUE).deadLetterExchange(Constants.DL_EXCHANGE).deadLetterRoutingKey("dlx").maxLength(10l).build();}
生产者:
@RequestMapping("/dl")public String dl() {System.out.println("dl test !!!");for (int i = 0; i < 15; i++) {rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","dl test ..."+i);}return "消息发送成功";}
测试:
死信队列的应用场景
- 消息重试与错误处理
- 场景描述:
- 在分布式系统中,消息的处理可能会因为各种原因(如网络波动、依赖服务故障、业务逻辑异常等)而失败。当消费者无法正确处理消息时,可以将消息拒绝,使其进入死信队列。
- 具体示例:
- 假设一个电商系统中,订单处理服务从消息队列中接收订单消息进行处理。如果在处理订单时,由于库存服务不可用而导致无法完成库存扣减操作,订单处理服务可以拒绝该订单消息。这条消息就会进入死信队列,然后在死信队列的消费者中,可以实现重试机制。例如,每隔一段时间(如 5 分钟)尝试重新处理这些死信消息,直到库存服务恢复或者达到最大重试次数。
- 消息过期后的补偿处理
- 场景描述:
- 对于一些有时间限制的消息,当消息过期后(可能是由于业务时效性或者设置了 TTL),将其发送到死信队列进行特殊的补偿处理。
- 具体示例:
- 在一个限时促销活动系统中,促销活动消息被发送到消息队列。这些消息设置了 TTL,代表活动的有效期。当消息过期后,表示活动已经结束。这些过期消息进入死信队列,在死信队列的消费者中,可以对过期的促销活动进行数据清理,如删除相关的临时缓存数据、更新数据库中的活动状态为 “已结束” 等操作。
- 流量削峰与缓冲
- 场景描述:
- 当消息生产者的生产速度远大于消费者的消费速度时,普通队列可能会因为消息堆积而出现问题。通过设置队列长度限制,让超过限制的消息成为死信进入死信队列,可以起到缓冲和流量削峰的作用。
- 具体示例:
- 在一个热门电商平台的促销活动期间,订单消息大量涌入消息队列。为了避免普通队列因为消息过多而崩溃,可以设置普通队列的最大长度。当订单消息数量超过这个长度时,新的消息成为死信进入死信队列。在死信队列中,可以根据业务情况,例如在流量低谷时期,再将这些死信消息重新放回普通队列进行处理,或者进行一些其他的优化策略,如合并订单消息等。.....
三.延迟队列
定义:
延迟队列是一种特殊的队列,其中的消息会在经过一段延迟时间后才会被消费者消费。
即消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费
应用场景
延迟队列的使⽤场景有很多,比如:
1. 智能家居:用户希望通过手机远程遥控家⾥的智能设备在指定的时间进行工作.这时候就可以将用户指令发送到延迟队列,当指令设定的时间到了再将指令推送到智能设备.
2. 日常管理:预定会议后,需要在会议开始前十五分钟提醒参会人参加会议
3. 用户注册成功后,7天后发送短信,提高用户活跃度等
4. ......
实现一:TTL+死信队列
队列:
@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constants.NORMAL_QUEUE).deadLetterExchange(Constants.DL_EXCHANGE).deadLetterRoutingKey("dlx").ttl(10*1000).maxLength(10l).build();}
生产者:
@RequestMapping("/dl")public String dl() {System.out.println("dl test !!!");rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","dl test ...");System.out.printf("%tc 消息发送成功\n",new Date());return "消息发送成功";}
消费者:
@RabbitListener(queues = Constants.DL_QUEUE)public void dlxHandleMessage(Message message, Channel channel) throws Exception {System.out.printf("[dl.queue] %tc 接收到信息: %s, deliveryTag: %d\n",new Date(), new String(message.getBody(), "UTF-8"), message.getMessageProperties().getDeliveryTag());}
测试1:
当前设置队列的TTL不会发生问题,可设置消息的TTL将会存在问题
测试2:设置消息的TTL
@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constants.NORMAL_QUEUE).deadLetterExchange(Constants.DL_EXCHANGE).deadLetterRoutingKey("dlx")//.ttl(10*1000).maxLength(10l).build();}
生产者:
@RequestMapping("/delay")public String delay() {System.out.println("delay!!!");rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","delay test... 30s", message -> {message.getMessageProperties().setExpiration("30000");//单位为毫秒,设置30秒后过期return message;});rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE,"normal","delay test... 10s", message -> {message.getMessageProperties().setExpiration("10000");//单位为毫秒,设置10秒后过期return message;});System.out.printf("%tc 消息发送成功\n",new Date());return "消息发送成功";}
结果:
因为是先发30s 再发10s的消息时
结果俩条信息都过了30s才接收到,并不符合我们的预期
反正先发时间少的消息 再发长的才行,
不过这种模式终究还是存在缺陷
所以在考虑使用TTL+死信队列实现延迟任务队列的时候,需要确认业务上每个任务的延迟时间是一致的,如果遇到不同的任务类型需要不同的延迟的话,需要为每⼀种不同延迟时间的消息建立单独的消息队列.
实现二:延迟队列插件
下载插件
插件地址 Releases · rabbitmq/rabbitmq-delayed-message-exchange
选择合适的版本自行安装
再linux中找到 /usr/lib/rabbitmq/plugins目录 安装在此
/usr/lib/rabbitmq/plugins 是⼀个附加目录,RabbitMQ包本身不会在此安装任何内容,如果
没有这个路径,可以自己进行创建
我这边是没有的 所有我得创建一个plugins目录
再将下载好的文件拖到linux中
#启动插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
#重启服务 service rabbitmq-server restart
查看结果即可
出现这种情况即可安装成功 一定要安装对应版本 小编刚刚就安装错了,捣鼓了很久
声明交换机,队列,绑定关系
//延迟队列public static final String DELAY_QUEUE= "delay.queue";public static final String DELAY_EXCHANGE = "delay.exchange";
package com.bite.extensions.config;import com.bite.extensions.constant.Constants;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DelayConfig {@Bean("delayQueue")public Queue delayQueue() {return QueueBuilder.durable(Constants.DELAY_QUEUE).build();}@Bean("delayExchange")public DirectExchange delayExchange() {return ExchangeBuilder.directExchange(Constants.DELAY_EXCHANGE).delayed().build();}@Bean("delayBinding")public Binding delayBinding(@Qualifier("delayQueue") Queue queue, @Qualifier("delayExchange") DirectExchange directExchange) {return BindingBuilder.bind(queue).to(directExchange).with("delay");}
}
生产者:
@RequestMapping("/delay2")public String delay2() {System.out.println("delay!!!");rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay test... 30s", message -> {message.getMessageProperties().setDelayLong(30000l);//单位为毫秒,设置30秒后过期return message;});rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay test... 10s", message -> {message.getMessageProperties().setDelayLong(10000l);//单位为毫秒,设置10秒后过期return message;});System.out.printf("%tc 消息发送成功\n",new Date());return "消息发送成功";}
消费者:
package com.bite.extensions.listener;import com.bite.extensions.constant.Constants;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class DelayListener {@RabbitListener(queues = Constants.DELAY_QUEUE)public void dlxHandleMessage(Message message, Channel channel) throws Exception {System.out.printf("[delay.queue] %tc 接收到信息: %s, deliveryTag: %d\n",new Date(), new String(message.getBody(), "UTF-8"), message.getMessageProperties().getDeliveryTag());}
}
测试:
10s后:
20s后:
俩种实现对比:
二者对比:
1. 基于死信实现的延迟队列
a. 优点:1)灵活不需要额外的插件支持
b. 缺点:1)存在消息顺序问题2)需要额外的逻辑来处理死信队列的消息,增加了系统的复杂性
2. 基于插件实现的延迟队列
a. 优点:1)通过插件可以直接创建延迟队列,简化延迟消息的实现.2)避免了DLX的时序问题
b. 缺点:1)需要依赖特定的插件,有运维工作2)只适用特定版本
结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!
相关文章:
【Rabbitmq篇】高级特性----TTL,死信队列,延迟队列
目录 一.TTL 1.设置消息的TTL 2.设置队列的TTL 3.俩者区别 二.死信队列 定义: 消息成为死信的原因: 1.消息被拒绝(basic.reject 或 basic.nack) 2.消息过期(TTL) 3.队列达到最大长度 编辑 …...
【Linux】gcc/g++使用
编译 我们知道,gcc只能编译C,g既能编译C,也能编译C。 由于两者的选项是相同的,这里我们使用gcc来说明。 这就是一个我们在linux中gcc编译一段代码后会自动生成一个a.out为名的可执行文件,然后我们./a.out,…...
IDEA2023 SpringBoot整合MyBatis(三)
一、数据库表 CREATE TABLE students (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100) NOT NULL,age INT,gender ENUM(Male, Female, Other),email VARCHAR(100) UNIQUE,phone_number VARCHAR(20),address VARCHAR(255),date_of_birth DATE,enrollment_date DATE,cours…...
Java网络编程 - cookiesession
cookie 之前学习了 Okhttp3 库可以调用API、抓取网页、下载文件。但是这些操作都是不要求登录的,如果 API、网页、文件等内容要求登录才能访问,就需要学习新的 cookie 相关的知识。 下面以豆瓣为例,使用 Java 程序读取“我的豆瓣”页面内容…...
100.【C语言】数据结构之二叉树的堆实现(顺序结构) 1
目录 1.顺序结构 2.示意图 编辑 从物理结构还原为逻辑结构的方法 3.父子节点编号的规律 4.顺序存储的前提条件 5.堆的简介 堆的定义 堆的两个重要性质 小根堆和大根堆 6.堆的插入 7.堆的实现及操作堆的函数 堆的结构体定义 堆初始化函数HeapInit 堆插入元素函…...
《Python基础》之循环结构
目录 简介 一、for循环 1、基本语法与作用 2、使用 range() 函数配合 for 循环 3、嵌套的for循环 二、while循环 1、基本语法与作用 2、while 循环嵌套 (1)、while循环与while循环嵌套 (2)、while循环与for循环嵌套 简介 …...
使用JDBC操作数据库
文章目录 使用JDBC操作数据库1. JDBC访问数据库步骤2. Statement与PreparedStatement区别3. JDBC的内容4. JDBC封装4.1 为什么进行JDBC封装4.2 实现JDBC封装4.3 什么是DAO4.4 配置数据库访问参数4.5 配置数据库连接池使用之JNDI的方式 5. 单例模式5.1 懒汉模式5.2 饿汉模式 使用…...
轻松解析 PDF 文档:深入了解 Python 的 pdfplumber 库
轻松解析 PDF 文档:深入了解 Python 的 pdfplumber 库 PDF 是一种常见的文件格式,广泛用于报告、文档、表单等领域。然而,如何高效解析 PDF 内容(尤其是文本和表格),一直是开发者面临的挑战。pdfplumber 是…...
实验五 时域采样与频域采样
时域采样理论的验证 【实例3-1】近似绘制x (n) R4n 在(0,2 π \pi π ) 上的幅频响应曲线( F T [ x ( n ) ] FT[x(n)] FT[x(n)] )。 x [1, 1, 1, 1]; N 64; xk fft(x, N); figure; subplot(2, 1, 1); stem(0:3, x, .); subplot(2, 1, 2); k 0:N-1; plot(2*k/N, abs(x…...
爬虫cookie反爬------加速乐(jsl)
加速乐 反爬虫技术:加速乐采用了包括OB混淆、动态加密算法和多层Cookie获取等高级反爬虫技术,确保整体校验的严密性。关键校验字段位于Cookie中的 __jsl_clearance_s,其验证过程通常涉及三次关键的请求,有效抵御恶意爬虫的侵扰。…...
设计模式——解释器模式
定义: 解释器模式是一种行为设计模式,它给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在这种模式中,通常会将一个复杂的表达式(如数学表达…...
sorted()函数
sorted(iterable, keyNone, reverseFalse)iterable: 需要排序的可迭代对象(如列表、元组、字符串等)。 key: 一个函数,用于从每个元素中提取排序的依据。如果未指定,默认直接比较元素本身。 reverse: 一个布尔值,Tru…...
动静态分析
静态分析 获取哈希值: 查壳: 导出函数: 获取资源信息: 通过发现dos头和pe头,来确定它是个可执行程序。 动态分析...
2024年信号处理与神经网络应用国际学术会议(SPNNA 2024)
重要信息 会议时间:2024年12月13-15日 会议地点:中国武汉 会议官网:www.spnna.org 会议简介 2024年信号处理与神经网络应用国际学术会议(SPNNA 2024)将于2024年12月13日至15日在中国武汉召开。本次会议旨在为全球研…...
winfrom快速自适应
在软件界面设计中,我们通常需要添加各种布局器和规则来实现界面布局,但对于不太熟练的工程师来说,这可能存在一定难度。这里要分享一种自适应布局的方法,它可以根据界面比例自动缩放内容控件,在较短时间内完成软件布局…...
VMware16安装macOS12【详细教程】
因为在应用上线IOS应用商店时,需要用到mac系统进行,于是就在VMware16pro虚拟机进行安装macOS12系统,安装的过程做了一个记录,希望对你有所帮助! 前言 首先需要下载好下面工具: VMware workstation pro 16…...
【设计模式】【创建型模式(Creational Patterns)】之单例模式
单例模式是一种常用的创建型设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。 单例模式的原理 单例模式的核心在于控制类的实例化过程,通常通过以下方式实现: 私有化构造函数,防止外部直接实例化。…...
【1.2 Getting Started--->Installation Guide】
NVIDIA TensorRT DOCS 此 NVIDIA TensorRT 10.6.0 安装指南提供安装要求、TensorRT 包中包含的内容列表以及安装 TensorRT 的分步说明。 安装指南 摘要: 本 NVIDIA TensorRT 10.3.0 安装指南提供了安装要求、TensorRT 软件包中包含的内容列表以及安装 TensorRT 的…...
Vue 中 data 属性为函数的深度剖析:原理、区别与实践
在 Vue.js 中,data 属性通常是一个 函数 而不是一个对象,这背后有一系列设计上的原因和原理,尤其是与 Vue 的组件系统、实例化机制、以及响应式数据的管理有关。下面我将详细解答这个问题,并结合实际项目示例和代码分析,进行全面讲解。 1. Vue 中 data 为什么是一个函数而…...
【漏洞复现】H3C 用户自助服务平台 dynamiccontent.properties.xhtml 远程命令执行
免责声明: 本文旨在提供有关特定漏洞的信息,以帮助用户了解潜在风险。发布此信息旨在促进网络安全意识和技术进步,并非出于恶意。读者应理解,利用本文提到的漏洞或进行相关测试可能违反法律或服务协议。未经授权访问系统、网络或应用程序可能导致法律责任或严重后果…...
【技术支持】vscode不使用插件,两种方式重命名html标签对
1. 使用 VS Code 内置功能 VS Code 内置支持 HTML/XML 标签对的重命名功能。步骤如下: 将光标放置在标签名上(如 <div> 或</div>)。按下快捷键 F2(重命名符号)。输入新的标签名,按 Enter&…...
【Seed-Labs 2.0】The Kaminsky Attack Lab
说在前面 本实验属为Seed-Labs 的DNS LAB 中的第二个实验,是第一个实验的延伸,从攻击者和受害者同一个LAN中变成不在同一个LAN中,该系列一共有五个实验: Local DNS Attack LabThe Kaminsky Attack LabDNS Rebinding Attack LabDNS Infrastr…...
node.js中使用express.static()托管静态资源
express.static()定义 express.static(root, [options])是一个中间件函数,负责为Express应用提供静态资源服务。它允许你指定一个或多个目录作为静态资源的根目录,当客户端请求这些资源时,Express会查找并返回对应的文件。 安装express npm i…...
SQL MAX() 函数深入解析
SQL MAX() 函数深入解析 概述 SQL(Structured Query Language)是一种广泛使用的数据库查询语言,它允许用户从数据库中检索、更新和管理数据。在SQL中,MAX() 函数是一个常用的聚合函数,用于从数据集中找出某一列的最大…...
WPF——自定义ToolTip
问题 前一天制作的图标按钮,在测试的过程中发现一个问题:为图标按钮添加的提示如下图所示,它的显示效果非常差,甚至不能看清文本内容,并且其字体与颜色也不是愚所希望的。 产生原因 此是由于tooltip有一个默认的样式…...
linux基本命令(1)
1. 文件和目录操作 ls — 列出目录内容 ls # 显示当前目录的文件和目录 ls -l # 显示详细的文件信息(权限、大小、修改时间等) ls -a # 显示所有文件(包括隐藏文件) ls -lh # 显示详细信息并以易读的方式显示文件大小 cd — 改…...
从0-1逐步搭建一个前端脚手架工具并发布到npm
前言 本文介绍的案例已同步到github,github地址。 vue-cli 和 create-react-app 等 cli 脚手架工具用于快速搭建应用,无需手动配置复杂的构建环境。本文介绍如何使用 rollup 搭建一个脚手架工具。 脚手架工具的工作流程简言为:提供远端仓库…...
开发者视角下的鸿蒙
鸿蒙操作系统(HarmonyOS)是华为公司自主研发的一款面向未来、面向全场景的分布式操作系统。它旨在为用户提供一个无缝的智能生活体验,支持多种终端设备,如智能手机、平板电脑、智能穿戴设备、智能家居等。鸿蒙操作系统的出现&…...
docker基础命令
目录 1、docker拉取镜像 2、查看镜像 3、运行镜像 4、查看容器 5、停止、启动、容器和删除容器 6、进入容器 7、删除镜像 8、保存镜像 9、加载镜像 10、镜像标签 11、制作镜像 12、镜像上传 1、docker拉取镜像 docker pull 用户名/镜像名:tag不加tag(版本号) 即…...
订单日记为“惠采科技”提供全方位的进销存管理支持
感谢温州惠采科技有限责任公司选择使用订单日记! 温州惠采科技有限责任公司,成立于2024年,位于浙江省温州市,是一家以从事销售电气辅材为主的企业。 在业务不断壮大的过程中,想使用一种既能提升运营效率又能节省成本…...
C++共享智能指针
C中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄漏。解决这个问题最有效的方式是使用智能指针。 智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动…...
数学建模学习(138):基于 Python 的 AdaBoost 分类模型
1. AdaBoost算法简介 AdaBoost(Adaptive Boosting)是一种经典的集成学习算法,由Yoav Freund和Robert Schapire提出。它通过迭代训练一系列的弱分类器,并将这些弱分类器组合成一个强分类器。算法的核心思想是:对于被错误分类的样本,在下一轮训练中增加其权重;对于正确分类…...
sqlite-vec一个SQLite3高效向量搜索扩展--JDBC环境使用
最近要用SQLite3,之前放出来了SQLiteUtile工具,方便操作。今天发现AIGC方面,RAG知识库需要使用向量数据库,来存储知识信息。一般呢都是用mysql,但无奈的是mysql就是不让用。突然又发现SQLite3有向量库扩展组件…...
Spark SQL操作
Spark SQL操作 文章目录 Spark SQL操作一、DataFrame的创建与保存1.前提操作2.数据准备3.创建4.保存DataFrame 二、DataFrame的操作1.printSchema2.show3.select4.filter5.groupBy(filed)6.sort(field) 三、临时表操作1.创建临时表2.通过临时表及SQL语句进行查询 四、从RDD转换…...
【大模型】LLaMA: Open and Efficient Foundation Language Models
链接:https://arxiv.org/pdf/2302.13971 论文:LLaMA: Open and Efficient Foundation Language Models Introduction 规模和效果 7B to 65B,LLaMA-13B 超过 GPT-3 (175B)Motivation 如何最好地缩放特定训练计算预算的数据集和模型大小&…...
聚焦AI存储,联想凌拓全力奔赴
【全球存储观察 | 科技热点关注】 每一个时代,都有每一个时代的骄傲。 在信息化时代,NAS文件存储肩负着非结构化数据管理与存储的重任,NetApp以其创新实力,赢得了全球存储市场的极高声誉。 在数智化时代,…...
ansible常用模块
一.ansible常用模块 ansible [主机or组列表] -m 模块 -a "参数"1.shell:类似于在终端上直接输入命令,支持bash特性2.command(默认模块):使用的变量需要事先定义好,不支持bash特性,如管道、重定向3.script: 执行脚本,支持python,shell脚本4.file:用于在被控…...
window11编译pycdc.exe
一、代码库和参考链接 在对python打包的exe文件进行反编译时,会使用到uncompyle6工具,但是这个工具只支持python3.8及以下,针对更高的版本的python则不能反编译。 关于反编译参考几个文章: Python3.9及以上Pyinstaller 反编译教…...
C语言——break、continue、goto
目录 一、break 二、continue 1、在while循环中 2、在for循环中 三、go to 一、break 作用是终止循环,在循环内遇到break直接就跳出循环。 注: 一个break语句只能跳出一层循环。 代码演示: #include<stdio.h>void test01() {for (…...
实战OpenCV之人脸识别
基础入门 随着计算机视觉技术和深度学习的发展,人脸识别已经成为一项广泛应用的技术,涵盖了从安全监控、身份验证、智能家居到大型公共安全项目等多个领域。 人脸识别技术通常包括以下几个主要步骤。 图像采集:通过摄像头或其他图像采集设备,捕获包含人脸的图像或视频帧。 …...
记录第一次安装laravel项目
window系统 Laravel中文文档:https://laravel-docs.catchadmin.com/docs/11/getting-started/installation 1.使用composer安装全局laravel composer global require laravel/installer2.安装完成后在命令行输入laravel,如果报错:laravel不是…...
AWTK-WEB 快速入门(1) - C 语言应用程序
先安装 AWTK Designer 用 AWTK Designer 新建一个应用程序 2.1. 新建应用程序 这里假设应用程序的名称为 AwtkApplicationC,后面会用到,如果使用其它名称,后面要做相应修改。 在窗口上放置一个按钮将按钮的名称改为 “close”将按钮的文本改…...
《操作系统 - 清华大学》4 -5:非连续内存分配:页表一反向页表
文章目录 1. 大地址空间的问题2. 页寄存器( Page Registers )方案3. 基于关联内存(associative memory )的反向页表(inverted page table)4. 基于哈希(hashed)查找的反向页表5. 小结 1. 大地址空间的问题 …...
数据可视化复习1-Matplotlib简介属性和创建子图
1.Matplotlib简介 Matplotlib是一个Python的2D绘图库,它可以在各种平台上以各种硬拷贝格式和交互环境生成具有出版品质的图形。通过Matplotlib,开发者可以仅需要几行代码,便可以生成绘图、直方图、功率谱、条形图、错误图、散点图等。 以下…...
98. 验证二叉搜索树【 力扣(LeetCode) 】
文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 98. 验证二叉搜索树 一、题目描述 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下: 节点的左子树只包含 小于 当…...
github中banch和tag的应用
GitHub 中的 Branch 和 Tag 之间的关系 在 GitHub 和 Git 中,**Branch(分支)**和**Tag(标签)**都是用来管理和标记代码的概念,但它们在版本控制中扮演不同的角色和有不同的用途。 --- 名词解释 1. 分支…...
鸿蒙HarmonyOS开发:一次开发,多端部署(工程级)三层工程架构
文章目录 一、工程创建1、先创建出最基本的项目工程。2、新建common、features、 products 目录 二、工程结构三、依赖关系1、oh-package.json52、配置ohpm包依赖 四、引用ohpm包中的代码1、定义共享资源2、在common模块index文件中导出3、在phone模块oh-package.json5文件中引…...
无插件H5播放器EasyPlayer.js视频流媒体播放器如何开启electron硬解码Hevc(H265)
在数字化时代,流媒体播放器技术正经历着前所未有的变革。随着人工智能、大数据、云计算等技术的融合,流媒体播放器的核心技术不断演进,为用户提供了更加丰富和个性化的观看体验。 EasyPlayer.js H5播放器,是一款能够同时支持HTTP、…...
关于vue生命周期理解示例代码
在业务运作时,特定的逻辑代码,需要在特定的阶段去执行,所以需要理解Vue的生命周期,以及各个周期内的方法,才能明确业务代码的编写 概述:Vue生命周期,指一个vue实例从创建到销毁的过程。 分为四…...
【MySQL数据库】C#实现MySQL数据库最简单的查询和执行函数
文章目录 前言一、查询方法二、执行方法 前言 C#和MySQL数据库是常见的数据交互,标准的查询和执行方法如下,做个记录。 一、查询方法 private static int QueryTable(string tableName, DateTime today, string stepName){int result 0; // 返回数据…...