【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)配置交换机&队列

    //TTL
    public 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() {
            @Override
            public 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 "消息发送成功";
    }

测试:

 

死信队列的应用场景

  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 消息发送成功\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)只适用特定版本


结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942672.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Solon v3.0.5 发布!(Spring 可以退休了吗?)

Solon 框架&#xff01; 新一代&#xff0c;面向全场景的 Java 应用开发框架。从零开始构建&#xff08;非 java-ee 架构&#xff09;&#xff0c;有灵活的接口规范与开放生态。 追求&#xff1a; 更快、更小、更简单提倡&#xff1a; 克制、高效、开放、生态 有什么特点&am…

【PCIe 总线及设备入门学习专栏 1.1 -- PCIe 基础知识 lane和link介绍】

文章目录 OverivewLane 和 LinkRC 和 RPPCIe controllerPCIE ControllerPHY模块 Inbound 和 OutboundPCIe transaction modelPIODMAP2P Overivew PCIe&#xff0c;即PCI-Express总线&#xff08;Peripheral Component Interconnect Express&#xff09;&#xff0c;是一种高速…

安卓 SystemServer 启动流程

目录 引言 Android系统服务启动顺序 zygote fork SystemServer 进程 SystemServer启动流程 1、SystemServer.main() 2、SystemServer.run() 3、初始化系统上下文 4、创建系统服务管理 5、启动系统各种服务 总结 引言 开机启动时 PowerManagerService 调用 AudioSer…

117.【C语言】数据结构之排序(选择排序)

目录 1.知识回顾 2.分析 设想的思路 代码 执行结果 ​编辑 错误排查和修复 详细分析出错点 执行结果 3.正确的思路 4.其他问题 1.知识回顾 参见42.5【C语言】选择排序代码 点我跳转 2.分析 知识回顾里所提到的文章的选择排序一次循环只比一个数字,和本文接下来要…

嵌入式驱动开发详解21(网络驱动开发)

文章目录 前言以太网框架ENET 接口简介MAC接口MII \ RMII 接口MDIO 接口RJ45 接口 PHY芯片以太网驱动驱动挂载wifi模块挂载后续 前言 linux驱动主要是字符设备驱动、块设备驱动还有网络设备驱动、字符设备驱动在本专栏前面已经详细将解了&#xff0c;网络设备驱动本文会做简要…

代码随想录Day37 动态规划:完全背包理论基础,518.零钱兑换II,本周小结动态规划,377. 组合总和 Ⅳ,70. 爬楼梯(进阶版)。

1.完全背包理论基础 思路 完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 完…

软件测试之全链路压测详解

随着业务的快速发展我们日常遇到的系统性能压力问题也逐渐出现&#xff0c;甚至在部分场合会遇到一些突发的营销活动&#xff0c;会导致系统性能突然暴涨&#xff0c;可能导致我们系统的瘫痪。最近几年随着电商的各种促销活动&#xff0c;有一个词也渐渐进入我们眼帘&#xff0…

用于汽车碰撞仿真的 Ansys LS-DYNA

使用 Ansys LS-DYNA 进行汽车碰撞仿真汽车碰撞仿真 简介 汽车碰撞仿真是汽车设计和安全工程的一个关键方面。这些仿真使工程师能够预测车辆在碰撞过程中的行为&#xff0c;从而有助于改进安全功能、增强车辆结构并符合监管标准。Ansys LS-DYNA 是一款广泛用于此类仿真的强大工具…

ES已死,文本检索永生

长期以来&#xff0c;混合查询&#xff08;Hybrid Search&#xff09;一直是提升 RAG&#xff08;Retrieval-Augmented Generation&#xff09;搜索质量的重要手段。尽管基于密集向量&#xff08;Dense Embedding&#xff09;的搜索技术随着模型规模和预训练数据集的不断扩展&a…

43. Three.js案例-绘制100个立方体

43. Three.js案例-绘制100个立方体 实现效果 知识点 WebGLRenderer&#xff08;WebGL渲染器&#xff09; WebGLRenderer是Three.js中最常用的渲染器之一&#xff0c;用于将3D场景渲染到网页上。 构造器 WebGLRenderer(parameters : Object) 参数类型描述parametersObject…

YOLO原理讲解

一、YOLO的输入参数介绍 打标签后会生成一系列参数&#xff0c;包含&#xff1a; 置信度、预测框的位置&#xff08;中心点的位置、高度宽度&#xff09;、类别&#xff08;标签1、标签2、标签3......&#xff09; 二、处理图像和标签 首先YOLO会把图像均分为19*19个格子 &a…

9. zynq应用开发--makefile编译

3. 使用SDK工具 如果只做 Linux 应用开发&#xff0c;只需要一个 sdk.sh 文件即可&#xff0c;可以脱离 Petalinux 和 Vitis&#xff0c;也可以编译其三方的应用&#xff0c;可以说一劳永逸。 配置根文件系统 petalinux-config -c rootfs 编译SDK petalinux-build --sdk Linu…

“鞋履创新工坊”:运动鞋店的新产品设计与管理

3.1 系统可行性分析 开发一款程序软件不仅需要时间&#xff0c;也需要人力&#xff0c;物力资源。而进行可行性分析这个环节就是解决用户这方面的疑问&#xff0c;看看程序在当前的条件下是否可以进行开发。 3.1.1 技术可行性分析 此程序选用的开发语言是Java&#xff0c;这种编…

重温设计模式--6、享元模式

文章目录 享元模式&#xff08;Flyweight Pattern&#xff09;概述享元模式的结构C 代码示例1应用场景C示例代码2 享元模式&#xff08;Flyweight Pattern&#xff09;概述 定义&#xff1a; 运用共享技术有效地支持大量细粒度的对象。 享元模式是一种结构型设计模式&#xff0…

*(int**)是什么意思

有这样一段连续的内存&#xff0c;int*arr(int*)malloc(20); malloc 开辟了 20 个字节大小的空间&#xff0c;arr 指向这段空间的开头 我们要实现像链表一样的功能&#xff0c;有什么方法呢&#xff1f;(关于为什么要在一段连续的空间上实现像链表一样的功能&#xff0c;这只是…

STM32 SPI读取SD卡

七个响应类型&#xff1a; R1 Response (Normal Response): R1响应是最基本的响应&#xff0c;包含一个字节的状态位&#xff0c;用于指示命令是否成功执行。常用。最高位为0。最低位为1表示是空闲状态。其他位是各种错误提示。 R1b Response (Normal with Busy): 类似于R1&a…

[手机Linux] 七,NextCloud优化设置

安装完成后在个人设置里发现很多警告&#xff0c;一一消除。 只能一条一条解决了。 关于您的设置有一些错误。 1&#xff0c;PHP 内存限制低于建议值 512 MB。 设置php配置文件&#xff1a; /usr/local/php/etc/php.ini 把里面的&#xff1a; memory_limit 128M 根据你自…

使用Excel制作通达信自定义“序列数据“

序列数据的视频教程演示 Excel制作通达信自定义序列数据 1.序列数据的制作方法&#xff1a;删掉没有用的数据&#xff08;行与列&#xff09;和股代码格式处理&#xff0c;是和外部数据的制作方法是相同&#xff0c;自己上面看历史博文。只需要判断一下&#xff0c;股代码跟随的…

逆向工程在医疗器械中的应用

关于逆向工程&#xff1a; 逆向设计跟正向设计流程不同&#xff0c;它是对己有产品原型进行分析、改进和再创造的过程。通过先进的数字测量手段反向获取产品的外形数据&#xff0c;然后利用各种造型软件由点云数据重构出该产品的CAD模型。逆向工程的辅助设计建构可以缩短产品的…

Web安全攻防入门教程——hvv行动详解

Web安全攻防入门教程 Web安全攻防是指在Web应用程序的开发、部署和运行过程中&#xff0c;保护Web应用免受攻击和恶意行为的技术与策略。这个领域不仅涉及防御措施的实现&#xff0c;还包括通过渗透测试、漏洞挖掘和模拟攻击来识别潜在的安全问题。 本教程将带你入门Web安全攻防…