当前位置: 首页 > 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;简…...

【图片转换PDF】多个文件夹里图片逐个批量转换成多个pdf软件,子文件夹单独合并转换,子文件夹单独批量转换,基于Py的解决方案

建筑设计公司在项目执行过程中&#xff0c;会产生大量的设计图纸、效果图、实景照片等图片资料。这些资料按照项目名称、阶段、专业等维度存放在多个文件夹和子文件夹中。 操作需求&#xff1a;为了方便内部管理和向客户交付完整的设计方案&#xff0c;公司需要将每个项目文件…...

前端学习之Flex布局

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Flex布局示例</title><style>.conta…...

游戏引擎学习第97天

回顾昨天并计划今天 在这期节目中&#xff0c;主要讲解了光照的概念&#xff0c;并进一步讨论了法线贴图光照的实现。节目的内容大致分为几个部分&#xff1a; 光照的基础概述&#xff1a;讨论了光的工作原理以及如何在编程图形时需要考虑光照问题。尽管这些概念并没有深入到…...

Mysql中存储引擎各种介绍以及应用场景、优缺点

概述 MySQL 提供了多种存储引擎&#xff0c;每种引擎有不同的特点和适用场景。以下是几种常见的 MySQL 存储引擎的详细介绍&#xff0c;包括它们的底层工作原理、优缺点&#xff0c;以及为什么 MySQL 默认选择某种引擎。 1. InnoDB 底层工作原理&#xff1a; 事务支持&#…...

PHP 运算符

PHP 运算符 概述 PHP 是一种广泛使用的开源服务器端脚本语言,它具有丰富的运算符集,这些运算符是编写 PHP 程序的基础。运算符用于执行各种数学、逻辑和比较操作。本篇文章将详细介绍 PHP 中常用的运算符,包括算术运算符、比较运算符、逻辑运算符、赋值运算符等。 算术运…...

Vue全流程--Vue3.0与Vue2.0响应式原理对比

Vue2中数据的响应式 需要使用Vue.set这么一个api&#xff0c;修改数据 需要使用Vue.delete这么一个api&#xff0c;删除数据 数据代理这个当面的理解可以看看我前面文章Vue全流程--数据代理的理解以及在Vue中的应用-CSDN博客 Vue3中数据的响应式 Vue3使用proxy这个api实现…...

C语言学习笔记:子函数的调用实现各个位的累加和

在C语言程序学习之初&#xff0c;我们都会学习如何打印 hello world&#xff0c;在学习时我们知道了int main&#xff08;&#xff09;是主函数&#xff0c;程序从main函数开始执行&#xff0c;这是流程控制的一部分内容。在主函数中我们想要实现一些功能&#xff0c;比如求各个…...

【大模型】本地部署DeepSeek-R1:8b大模型及搭建Open-WebUI交互页面

本地部署DeepSeek-R1:8b大模型 一、摘要及版本选择说明1.1 摘要1.2 版本选择 二、下载并安装Ollama三、运行DeepSeek-R1:8b大模型四、安装Open WebUI增强交互体验五、关闭Ollama开机自动启动六、DeepSeek大模型启停步骤 一、摘要及版本选择说明 1.1 摘要 作为一名对 AI 和生成…...

Python 调用 Stabilityai API在本地生成图像

Python 调用 Stabilityai API在本地生成图像 摘要功能 代码结构关键技术代码下载立即体验 摘要 本程序利用硅基流动目前的免费 stabilityai/stable-diffusion-2-1 模型API&#xff0c;生成图像并下载到本地&#xff0c;用户可以通过输入描述性提示词来获取相应的图像。使用Pyt…...

Python3中异常处理:try-finally语句的示例

一. 简介 前面一篇文章简单学习了 try-finally语句结构&#xff0c;执行过程、以及使用场景。文章如下&#xff1a; Python3中异常处理&#xff1a;try-finally语句-CSDN博客 本文写一些简单的示例来继续学习 try-finally语句的使用。 二. Python3中异常处理&#xff1a;try…...