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

【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
",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
", 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 "消息发送成功";}

测试:

死信队列的应用场景

  1. 消息重试与错误处理
    • 场景描述
      • 在分布式系统中,消息的处理可能会因为各种原因(如网络波动、依赖服务故障、业务逻辑异常等)而失败。当消费者无法正确处理消息时,可以将消息拒绝,使其进入死信队列。
    • 具体示例
      • 假设一个电商系统中,订单处理服务从消息队列中接收订单消息进行处理。如果在处理订单时,由于库存服务不可用而导致无法完成库存扣减操作,订单处理服务可以拒绝该订单消息。这条消息就会进入死信队列,然后在死信队列的消费者中,可以实现重试机制。例如,每隔一段时间(如 5 分钟)尝试重新处理这些死信消息,直到库存服务恢复或者达到最大重试次数。
  2. 消息过期后的补偿处理
    • 场景描述
      • 对于一些有时间限制的消息,当消息过期后(可能是由于业务时效性或者设置了 TTL),将其发送到死信队列进行特殊的补偿处理。
    • 具体示例
      • 在一个限时促销活动系统中,促销活动消息被发送到消息队列。这些消息设置了 TTL,代表活动的有效期。当消息过期后,表示活动已经结束。这些过期消息进入死信队列,在死信队列的消费者中,可以对过期的促销活动进行数据清理,如删除相关的临时缓存数据、更新数据库中的活动状态为 “已结束” 等操作。
  3. 流量削峰与缓冲
    • 场景描述
      • 当消息生产者的生产速度远大于消费者的消费速度时,普通队列可能会因为消息堆积而出现问题。通过设置队列长度限制,让超过限制的消息成为死信进入死信队列,可以起到缓冲和流量削峰的作用。
    • 具体示例
      • 在一个热门电商平台的促销活动期间,订单消息大量涌入消息队列。为了避免普通队列因为消息过多而崩溃,可以设置普通队列的最大长度。当订单消息数量超过这个长度时,新的消息成为死信进入死信队列。在死信队列中,可以根据业务情况,例如在流量低谷时期,再将这些死信消息重新放回普通队列进行处理,或者进行一些其他的优化策略,如合并订单消息等。…

三.延迟队列

定义:

延迟队列是一种特殊的队列,其中的消息会在经过一段延迟时间后才会被消费者消费。

即消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费

应用场景

延迟队列的使场景有很多,比如:
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 消息发送成功
",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
",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 消息发送成功
",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 消息发送成功
",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
",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.俩者区别? 二.死信队列 定义&#xff1a; 消息成为死信的原因&#xff1a; 1.消息被拒绝&#xff08;basic.reject 或 basic.nack&#xff09; 2.消息过期&#xff08;TTL&#xff09; 3.队列达到最大长度? …...

机器学习赋能的智能光子学器件系统研究与应用

机器学习赋能的智能光子学器件系统研究与应用 时间&#xff1a; 2025年03月29日-03月30日 2025年04月05日-04月06日 机器学习赋能的光子学器件与系统&#xff1a;从创新设计到前沿应用 课程针对光子学方面的从业科研人员及开发者&#xff0c;希望了解和实践在集成光学/空间…...

尚硅谷课程【笔记】——大数据之Linux【三】

课程视频链接&#xff1a;尚硅谷大数据Linux课程 七、定时任务调度 任务调度&#xff1a;指系统在某个时间执行的特定的命令或程序。 1&#xff09;系统工作&#xff1a;有些重要的工作必须周而复始地执行。 2&#xff09;个别用户工作&#xff1a;用户可能希望在某些特定的时…...

Visual Studio踩过的坑

统计Unity项目代码行数 编辑-查找和替换-在文件中查找 查找内容输入 b*[^:b#/].*$ 勾选“使用正则表达式” 文件类型留空 也有网友做了指定&#xff0c;供参考 !*\bin\*;!*\obj\*;!*\.*\*!*.meta;!*.prefab;!*.unity 打开Unity的项目 注意&#xff1a;只是看&#xff0…...

教程 | MySQL 基本指令指南(附MySQL软件包)

此前已经发布了安装教程安装教程&#xff0c;现在让我们来学习一下MySQL的基本指令。 一、数据库连接与退出 连接本地数据库 mysql -uroot -p # 输入后回车&#xff0c;按提示输入密码&#xff08;密码输入不可见&#xff09;若需隐藏密码显示&#xff0c;可使用&#xff1…...

企业数据集成案例:吉客云销售渠道到MySQL

测试-查询销售渠道信息-dange&#xff1a;吉客云数据集成到MySQL的技术案例分享 在企业的数据管理过程中&#xff0c;如何高效、可靠地实现不同系统之间的数据对接是一个关键问题。本次我们将分享一个具体的技术案例——通过轻易云数据集成平台&#xff0c;将吉客云中的销售渠…...

网络编程 day3

思维导图 以select函数模型为例 思维导图2 对应 epoll模型 应使用的函数 题目 使用epoll函数实现 两个客户端 通过服务器 实现聊天 思路 在原先代码基础上 实现 服务器 发向 客户端 使用客户端在服务器上的 套接字描述符 实现 客户端 接收 服务器…...

Excel 融合 deepseek

效果展示 代码实现 Function QhBaiDuYunAIReq(question, _Optional Authorization "Bearer ", _Optional Qhurl "https://qianfan.baidubce.com/v2/chat/completions")Dim XMLHTTP As ObjectDim url As Stringurl Qhurl 这里替换为你实际的URLDim postD…...

【论文笔记】Are Self-Attentions Effective for Time Series Forecasting? (NeurIPS 2024)

官方代码https://github.com/dongbeank/CATS Abstract 时间序列预测在多领域极为关键&#xff0c;Transformer 虽推进了该领域发展&#xff0c;但有效性尚存争议&#xff0c;有研究表明简单线性模型有时表现更优。本文聚焦于自注意力机制在时间序列预测中的作用&#xff0c;提…...

游戏手柄Type-c方案,支持一边充电一边传输数据

乐得瑞推出LDR6023SS&#xff0c;专门针对USB-C接口手机手柄方案&#xff0c;支持手机快充&#xff0c;支持任天堂游戏机&#xff0c;PS4等设备~同时支持手机充电跟数据传输 1、概述 LDR6023SS SSOP16 是乐得瑞科技针对 USB Type-C 标准中的 Bridge 设备而开发的双 USB-C DRP …...

2. 4 模块化JDK:JDK模块结构与核心模块

第3章&#xff1a;模块化JDK&#xff1a;JDK模块结构与核心模块 JDK 9 将自身拆分为一系列模块&#xff0c;彻底告别传统的“单一JAR&#xff08;如 rt.jar&#xff09;”模式。本章深入解析 JDK 的模块化架构、核心模块功能及开发者如何高效利用这些模块。 3.1 JDK 模块化设计…...

每日一题——缺失的第一个正整数

缺失的第一个正整数 题目描述进阶&#xff1a;数据范围&#xff1a; 示例示例 1示例 2示例 3 题解思路代码实现代码解释复杂度分析总结 题目描述 给定一个无重复元素的整数数组 nums&#xff0c;请你找出其中没有出现的最小的正整数。 进阶&#xff1a; 时间复杂度&#xff…...

CEF132 编译指南 MacOS 篇 - 基础开发工具安装实战 (二)

1. 引言 在 macOS 平台上编译 CEF132 之前&#xff0c;首要任务是搭建一个完善的开发环境。与 Windows 和 Linux 环境不同&#xff0c;macOS 的开发环境主要以 Xcode 为核心。本篇将作为 CEF132 编译指南系列的第二篇&#xff0c;详细指导读者如何在 macOS 系统上安装和配置 X…...

vi 是 Unix 和 Linux 系统中常用的文本编辑器

vi是 Unix 和 Linux 系统中常用的文本编辑器&#xff0c;它有几种不同的模式&#xff0c;其中最常用的是命令模式和插入模式。光标控制主要在命令模式下进行&#xff0c;以下是一些常用的vi命令来控制光标位置&#xff1a; • h,j,k,l&#xff1a;分别用于将光标向左、向下、向…...

SwanLab x verl:可视化LLM强化学习后训练教程

文章目录 介绍Verl和SwanLab1. 环境安装2. 使用方法3. 查看训练日志 介绍Verl和SwanLab verl 是一个灵活、高效且可用于生产环境的强化学习&#xff08;RL&#xff09;训练框架&#xff0c;专为大型语言模型&#xff08;LLMs&#xff09;的后训练设计。它由字节跳动火山引擎团…...

职场到校园,初心未改:我的2024年

Hi&#xff0c;大家好&#xff0c;我是几何心凉。 其实早就想写一份复盘文章&#xff0c;正好借助2024年度博客之星的评选机会&#xff0c;来写下这篇总结。回望过去&#xff0c;感慨颇多。自从加入CSDN平台以来&#xff0c;已经见证了许多博主的来去匆匆&#xff0c;各类创作…...

C++基础知识学习记录—引用

1、引用的概念 概念&#xff1a;引用相当于给变量取个别名 对引用进行操作与直接操作变量相同&#xff0c;注意引用类型与变量类型一致 #include<iostream> using namespace std; int main(){int a10;int& cite_a a;//操作引用cite_a 与操作变量a完全一样cout &l…...

AWS Savings Plans 监控与分析工具使用指南

一、背景介绍 1.1 什么是 Savings Plans? AWS Savings Plans 是一种灵活的定价模式,通过承诺持续使用一定金额的 AWS 服务来获得折扣价格。它可以帮助用户降低 AWS 使用成本,适用于 EC2、Fargate 和 Lambda 等服务。 1.2 为什么需要监控? 优化成本支出跟踪使用情况评估投…...

【AI学习】关于 DeepSeek-R1的几个流程图

遇见关于DeepSeek-R1的几个流程图&#xff0c;清晰易懂形象直观&#xff0c;记录于此。 流程图一 来自文章《Understanding Reasoning LLMs》&#xff0c; 文章链接&#xff1a;https://magazine.sebastianraschka.com/p/understanding-reasoning-llms?continueFlagaf07b1a0…...

C++ ——从C到C++

1、C的学习方法 &#xff08;1&#xff09;C知识点概念内容比较多&#xff0c;需要反复复习 &#xff08;2&#xff09;偏理论&#xff0c;有的内容不理解&#xff0c;可以先背下来&#xff0c;后续可能会理解更深 &#xff08;3&#xff09;学好编程要多练习&#xff0c;简…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...