(五)RabbitMQ-进阶 死信队列、延迟队列、防丢失机制

Lison <dreamlison@163.com>, v1.0.0, 2023.06.23

RabbitMQ-进阶 死信队列、延迟队列、防丢失机制

文章目录

  • RabbitMQ-进阶 死信队列、延迟队列、防丢失机制
    • 死信队列
    • 延迟队列
      • 延迟队列介绍
      • **延迟队列_死信队列_的实现**
      • 延迟队列_插件实现
        • 下载插件
        • RabbitMQ 配置类
        • RabbitMQ 生产者
        • RabbitMQ 消费者
        • 测试
    • RabbitMQ防止消息丢失
      • 消息丢失场景
      • 生产者发送消息没有发送到rabbit交换机
      • 交换机没有发送到队列
      • 交换机、队列、消息没有设置持久化
      • 消费者接收到消息没有执行业务逻辑,导致消息丢失

死信队列

概念

在MQ中,当消息成为死信(Dead message)后,消息中间件可以 将其从当前队列发送到另一个队列中,这个队列就是死信队列。而 在RabbitMQ中,由于有交换机的概念,实际是将死信发送给了死 信交换机(Dead Letter Exchange,简称DLX)。死信交换机和死信队列和普通的没有区别。

在这里插入图片描述

消息成为死信的情况

  1. 队列消息长度到达限制
  2. 消费者拒签消息,并且不把消息重新放入原队列
  3. 消息到达存活时间未被消费

代码实现

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig2 {
    private final String DEAD_EXCHANGE = "dead_exchange";
    private final String DEAD_QUEUE = "dead_queue";
    private final String NORMAL_EXCHANGE = "normal_exchange";
    private final String NORMAL_QUEUE = "normal_queue";
    // 死信交换机
    @Bean(DEAD_EXCHANGE)
    public Exchange deadExchange(){
        return ExchangeBuilder
                .topicExchange(DEAD_EXCHANGE)
                .durable(true)
                .build();
    }
    // 死信队列
    @Bean(DEAD_QUEUE)
    public Queue deadQueue(){
        return QueueBuilder
                .durable(DEAD_QUEUE)
                .build();
    }
    // 死信交换机绑定死信队列
    @Bean
    public Binding bindDeadQueue(@Qualifier(DEAD_EXCHANGE) Exchange exchange, @Qualifier(DEAD_QUEUE)Queue queue){
        return BindingBuilder
                .bind(queue)
                .to(exchange)
                .with("dead_routing")
                .noargs();
    }
    // 普通交换机
    @Bean(NORMAL_EXCHANGE)
    public Exchange normalExchange(){
        return ExchangeBuilder
                .topicExchange(NORMAL_EXCHANGE)
                .durable(true)
                .build();
    }
    // 普通队列
    @Bean(NORMAL_QUEUE)
    public Queue normalQueue(){
        return QueueBuilder
                .durable(NORMAL_QUEUE)
                .deadLetterExchange(DEAD_EXCHANGE) // 绑定死信交换机
                .deadLetterRoutingKey("dead_routing") // 死信队列路由关键字
                .ttl(10000) // 消息存活10s
                .maxLength(10) // 队列最大长度为10
                .build();
    }
    // 普通交换机绑定普通队列
    @Bean
    public Binding bindNormalQueue(@Qualifier(NORMAL_EXCHANGE) Exchange exchange,@Qualifier(NORMAL_QUEUE)Queue queue){
        return BindingBuilder
                .bind(queue)
                .to(exchange)
                .with("my_routing")
                .noargs();
    }
}

测试

1、生产者发送消息

@Test
public void testDlx(){
    // 存活时间过期后变成死信
    //       rabbitTemplate.convertAndSend("normal_exchange","my_routing","测试死信");
    // 超过队列长度后变成死信
    //       for (int i = 0; i < 20; i++) {
    //           rabbitTemplate.convertAndSend("normal_exchange","my_routing","测试死信");
    //       }
    // 消息拒签但不返回原队列后变成死信
  rabbitTemplate.convertAndSend("normal_exchange","my_routing","测试死信");
}


2、

@Component
public class DlxConsumer {
    @RabbitListener(queues = "normal_queue")
    public void listenMessage(Message message, Channel channel) throws IOException {
        // 拒签消息
        channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
   }
}

延迟队列

延迟队列介绍

什么是延时队列?

延时队列即就是放置在该队列里面的消息是不需要立即消费的,而是等待一段时间之后取出消费

在这里插入图片描述

但RabbitMQ中并未提供延迟队列功能,我们可以使用死信队列实现延迟队列的效果

在这里插入图片描述

延迟交换机主要帮我们解决什么问题

(1)当我们的业务比较复杂的时候, 需要针对不同的业务消息类型设置不同的过期时间策略, name必然我们也需要为不同的队列消息的过期时间创建很多的Queue的Bean对象, 当业务复杂到一定程度时, 这种方式维护成本过高;

(2)就是队列的先进先出原则导致的问题,当先进入队列的消息的过期时间比后进入消息中的过期时间长的时候,消息是串行被消费的,所以必然是等到先进入队列的消息的过期时间结束, 后进入队列的消息的过期时间才会被监听,然而实际上这个消息早就过期了,这就导致了本来过期时间为3秒的消息,实际上过了13秒才会被处理,这在实际应用场景中肯定是不被允许的

适用场景

(1)商城订单超时未支付,取消订单

(2)使用权限到期前十分钟提醒用户

(3)收益项目,投入后一段时间后产生收益

延迟队列_死信队列_的实现

1、创建SpringBoot订单模块,添加SpringMVC、RabbitMQ、 lombok依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

2、编写配置文件

spring:
 rabbitmq:
   host: 127.0.0.1
   port: 5672
   username: admin
   password: 123456
   virtual-host: /
  
# 日志格式
logging:
 pattern:
   console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'

3、创建队列和交换机

@Configuration
public class RabbitConfig {
    // 订单交换机和队列
    private final String ORDER_EXCHANGE = "order_exchange";
    private final String ORDER_QUEUE = "order_queue";
    // 过期订单交换机和队列
    private final String EXPIRE_EXCHANGE = "expire_exchange";
    private final String EXPIRE_QUEUE = "expire_queue";
    // 过期订单交换机
    @Bean(EXPIRE_EXCHANGE)
    public Exchange deadExchange(){
         return ExchangeBuilder
               .topicExchange(EXPIRE_EXCHANGE)
               .durable(true)
               .build();
   }
    // 过期订单队列
    @Bean(EXPIRE_QUEUE)
    public Queue deadQueue(){
        return QueueBuilder
               .durable(EXPIRE_QUEUE)
               .build();
   }
    // 将过期订单队列绑定到交换机
    @Bean
    public Binding bindDeadQueue(@Qualifier(EXPIRE_EXCHANGE) Exchange exchange,@Qualifier(EXPIRE_QUEUE) Queue queue){
        return BindingBuilder
               .bind(queue)
               .to(exchange)
               .with("expire_routing")
               .noargs();
   }
    // 订单交换机
    @Bean(ORDER_EXCHANGE)
    public Exchange normalExchange(){
        return ExchangeBuilder
               .topicExchange(ORDER_EXCHANGE)
               .durable(true)
               .build();
   }
    // 订单队列
    @Bean(ORDER_QUEUE)
    public Queue normalQueue(){
        return QueueBuilder
               .durable(ORDER_QUEUE)
               .ttl(10000) // 存活时间为10s,模拟30min
               .deadLetterExchange(EXPIRE_EXCHANGE) // 绑定死信交换机
               .deadLetterRoutingKey("expire_routing") //死信交换机的路由关键字
               .build();
   }
    // 将订单队列绑定到交换机
    @Bean
    public Binding bindNormalQueue(@Qualifier(ORDER_EXCHANGE) Exchange exchange,@Qualifier(ORDER_QUEUE) Queue queue){
        return BindingBuilder
               .bind(queue)
               .to(exchange)
               .with("order_routing")
               .noargs();
   }
}

4、编写下单的控制器方法,下单后向订单交换机发送消息

@Test
 public String placeOrder(String orderId){
        System.out.println("处理订单数据...");
        // 将订单id发送到订单队列
        rabbitTemplate.convertAndSend("order_exchange", "order_routing", orderId);
        return "下单成功,修改库存";
   }

5、编写监听死信队列的消费者

// 过期订单消费者
@Component
public class ExpireOrderConsumer {
    // 监听队列
    @RabbitListener(queues = "expire_queue")
    public void listenMessage(String orderId){
        System.out.println("查询"+orderId+"号订单的状态,如果已支付则无需处理,如果未支付则需要回退库存");
   }
}

延迟队列_插件实现

在使用死信队列实现延迟队列时,会遇到一个问题:RabbitMQ只会移除队列顶端的过期消息,如果第一个消息的存活时长较长,而第二个消息的存活时长较短,则第二个消息并不会及时执行。

在这里插入图片描述

RabbitMQ虽然本身不能使用延迟队列,但官方提供了延迟队列插件,安装后可直接使用延迟队列
在这里插入图片描述

下载插件

RabbitMQ 实现了一个插件 x-delay-message 来实现延时队列,我们可以从 官网下载到它

https://www.rabbitmq.com/community-plugins.html

https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases

在这里插入图片描述

选择 .ez 格式的文件下载,下载后放置 RabbitMQ 的安装目录下的 plugins 目录下,如我的路径为

docker cp rabbitmq_delayed_message_exchange-3.8.17.8f537ac.ez  rabbitmq1:/plugins

docker exec  rabbitmq1  rabbitmq-plugins enable rabbitmq_delayed_message_exchange

RabbitMQ 配置类

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
@Slf4j
public class RabbitConfig3 {

    /**
     * 交换机
     */
    public static final String DELAY_EXCHANGE = "delay_exchange";

    /**
     * 队列
     */
    public static final String DELAY_QUEUE = "delay_queue";

    /**
     * 路由
     */
    public static final String DELAY_KEY = "delay_key";

    @Bean
    public RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory) {
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> log.info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause));
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> log.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey, replyCode, replyText, message));
        return rabbitTemplate;
    }

    /**
     * 直接模式队列1
     */
    @Bean
    public Queue directOneQueue() {
        return new Queue("cundream");
    }
    /**
     * 延时队列交换机
     *
     * @return
     */
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAY_EXCHANGE, "x-delayed-message", true, false, args);
    }

    /**
     * 延时队列
     *
     * @return
     */
    @Bean
    public Queue delayQueue() {
        return new Queue(DELAY_QUEUE, true);
    }

    /**
     * 给延时队列绑定交换机
     *
     * @return
     */
    @Bean
    public Binding delayBinding(Queue delayQueue, CustomExchange delayExchange) {
        return BindingBuilder.bind(delayQueue).to(delayExchange).with(DELAY_KEY).noargs();
    }
}



RabbitMQ 生产者


import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
@Slf4j
public class RabbitMqServiceImpl implements RabbitMqService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendDelayMessage(Object object, long millisecond) {
        this.rabbitTemplate.convertAndSend("delay_exchange",
                "delay_key",
                object.toString(),
                message -> {
                    message.getMessageProperties().setHeader("x-delay", millisecond);
                    return message;
                }
        );

    }
}

RabbitMQ 消费者

import cn.hutool.json.JSONUtil;
import com.github.cundream.springbootbuilding.common.rabbitmq.RabbitConst;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @className: com.github.cundream.springbootbuilding.common.rabbitmq.consumer-> ReceiveDealyConsumer
 * @description:
 * @author: 李村 
 * @createDate:
 */
@Slf4j
@RabbitListener(queuesToDeclare = @Queue(RabbitConst.DELAY_QUEUE))
@Component
public class ReceiveDealyHandler {
    @RabbitHandler
    public void directHandlerManualAck(Object object, Message message, Channel channel) {
        //  如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉
        final long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            log.info("直接队列1,手动ACK,接收消息:{}", object.toString());
            // 通知 MQ 消息已被成功消费,可以ACK了
            channel.basicAck(deliveryTag, false);
        } catch (IOException e) {
            try {
                // 处理失败,重新压入MQ
                channel.basicRecover();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

测试

通过测试,第一条消息在 5s后接收到,第二条消息在 10s后接收到,说明我们的延时队列已经成功

    @RequestMapping(value = "/delayMessage",method = RequestMethod.GET)
    public void delayMessage() {
        String message1 = "这是第一条消息";
        String message2 = "这是第二条消息";
        rabbitMqService.sendDelayMessage(message1, 5000);
        rabbitMqService.sendDelayMessage(message2, 10000);
    }

RabbitMQ防止消息丢失

消息丢失场景

MQ消息丢失场景主要有三个:

  • 消息生产者,发送消息后,rabbitMq服务器没有收到;导致消息丢失
  • rabbitmq收到消息后,没有持久化保存,导致消息丢失
  • 消费者收到消息后,没来得及处理,消费者宕机,导致消息丢失

生产者发送消息没有发送到rabbit交换机

解决方案:消息异步确认机制(confirm机制)

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
    virtual-host: /
    publisher-confirms: true # 消息异步确认机制(confirm机制)

开启confirm机制后,在生产者每次发送消息,都会调用回调代码;开发人员,需要写回调函数的逻辑,处理发送失败的消息

@Component
@Slf4j
public class RabbitMQConfirmAndReturn implements RabbitTemplate.ConfirmCallback {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
    }

    /**
     * confirm机制只保证消息到达exchange,不保证消息可以路由到正确的queue
     * @param correlationData 发送的消息的信息(交换机,路由,消息体等)
     * @param ack true成功,false失败
     * @param cause 发生错误的信息
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
    	// 失败,一般解决方案,是将发送失败消息,存入定时任务队列;尝试重新发送消息;再多次失败,
    	// 就不再发送,转为人工处理
        if (!ack) {
            log.error("rabbitmq confirm fail,cause:{}", cause);
            // ...... 失败处理逻辑
        }
    }
}

交换机没有发送到队列

解决方案:Return模式,确保消息从交换机发送到队列。

1、开启return模式

#开启 return 机制
spring:
  rabbitmq:
    publisher-returns: true

2、开发回调函数

@Component
public class Sender implements RabbitTemplate.ReturnCallback {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @PostConstruct
    public void init() {
        rabbitTemplate.setReturnCallback(this);
    }
 
    //通过实现ReturnCallback接口,如果消息从交换器发送到对应队列失败时触发(比如根据发送消息时指定的routingKey找不到队列时会触发)
	@Override
	public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
    System.out.println("消息主体message: " + message);
		System.out.println("消息replyCode: " + replyCode);
		System.out.println("描述: " + replyText);
		System.out.println("消息使用的交换器exchange: " + exchange);
		System.out.println("消息使用的路由键routing: " + routingKey);
	}
}

交换机、队列、消息没有设置持久化

交换机、队列、消息没有持久化,当rabbitmq的服务重启之后,这些信息就会丢失。

交换机持久化
在声明交换机的时候,设置持久化属性

	/**
	 * 构造参数说明:
	 * 参数1:交换机名称
	 * 参数2:durable:true表示持久化,false表示不持久化
	 * 参数3:autoDelete:true自动删除,false不自动删除
	 */
    @Bean
    public TopicExchange exchange() {
        return new TopicExchange("exchangeName", true, false);
    }

队列持久化
在声明队列的时候,设置持久化属性

    public Queue queue() {
    	/**
    	 * @param queueName 队列名称
    	 * @param durable 队列持久化,true持久化,false不持久化
    	 * @param exclusive 是否排他, true不排他,false排他;此处配置一般false
    	 * @param autoDelete 是否自动删除,无生产者,队列自动删除
    	 * @param args 队列参数
    	 */
        return new Queue("queueName", true, false, false, args);
    }

消息持久化

消息的持久化是默认持久的。无需配置

消费者接收到消息没有执行业务逻辑,导致消息丢失

解决方案:手动确认消息机制
配置文件配置

**spring.rabbitmq.listener.simple.acknowledge-mode=manual**
spring:
  rabbitmq:
    host: 127.0.0.1
    #host: 10.106.10.91
    port: 5672
    username: admin
    password: 123456
    virtual-host: pub
    publisher-confirms: true   # 开启发送确认
    publisher-returns: true  # 开启发送失败回退
      #开启ack
    listener:
      direct:
        acknowledge-mode: manual
      simple:
        acknowledge-mode: manual #采取手动应答
        #concurrency: 1 # 指定最小的消费者数量
        #max-concurrency: 1 #指定最大的消费者数量
        retry:
          enabled: true # 是否支持重试

@Component
public class Consumer {
	@RabbitHandler
	public void consumeMsg(String msg, Channel channel, Message message) throws IOException {
		//拿到消息延迟消费
		try {
			// .... 消费消息业务逻辑

			/**
			 * deliveryTag	消息的随机标签信息
			 * multiple	是否批量;true表示一次性的将小于deliveryTag的值进行ack
			 */
			channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			
		} catch (InterruptedException e) {
			e.printStackTrace();
			/**
			 * deliveryTag	消息的随机标签信息
			 * multiple	是否批量;true表示一次性的将小于deliveryTag的值进行ack
			 * requeue	被拒绝的消息是否重新入队列
			 */
			channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
		}
	}
}

当业务出现意料之外的一场;消息就会重新回到队列中;会分发到其他正常consumer中进行消费

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

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

相关文章

SpringCloudAlibaba微服务实战系列(一)Nacos服务注册发现

SpringCloudAlibaba微服务实战系列&#xff08;一&#xff09;Nacos服务注册发现 实战前先做一个背景了解。 单体架构、SOA和微服务 单体架构&#xff1a;近几年技术的飞速发展&#xff0c;各种各样的服务已经进入到网络化。单体架构发布时只需要打成一个war或jar包发布即可&a…

2023年的深度学习入门指南(19) - LLaMA 2源码解析

2023年的深度学习入门指南(19) - LLaMA 2源码解析 上一节我们学习了LLaMA 2的补全和聊天两种API的使用方法。本节我们来看看LLaMA 2的源码。 补全函数text_completion源码解析 上一节我们讲了LLaMA 2的编程方法。我们来复习一下&#xff1a; generator Llama.build(ckpt_di…

设计模式 - 工厂模式

一、 简单工厂&#xff08;Simple Factory Pattern&#xff09; 1、概念 一个工厂对象决定创建出哪一种产品类的实力&#xff0c;但不属于GOF23种设计模式。 简单工厂适用于工厂类负责创建的对象较少的场景&#xff0c;且客户端只需要传入工厂类的参数&#xff0c;对于如何创…

fastadmin采坑之接口分页处理

其实不算fastadmin的代码而是thinkphp自带的分页代码 paginate函数就是自带的分页函数&#xff0c;开始我以为这个只能用于渲染模板不能用于接口&#xff0c;后面看到源代码发现请求参数带page就可以 /*** ApiTitle (获取协会会员)* ApiSummary (获取协会会员)* ApiMethod …

Java在线OJ项目(一)、多进程编程实现 做题代码的编译和运行

在线OJ项目&#xff08;一&#xff09;、多进程编程实现 做题代码的编译和运行 一、回顾线程和进程二、进程比线程的优势三、多进程编程样例四、多进程思想 实现对代码 的编译 以及 运行两个功能CommandUtil 由于我们是在线oj&#xff0c;所以得编译用户的代码不仅编译 还需要 …

[ELK使用篇]:SpringCloud整合ELK服务

文章目录 一&#xff1a;前置准备-(参考之前博客)&#xff1a;1.1&#xff1a;准备Elasticsearch和Kibana环境&#xff1a;1.1.1&#xff1a;地址&#xff1a;[https://blog.csdn.net/Abraxs/article/details/128517777](https://blog.csdn.net/Abraxs/article/details/1285177…

云服务器远程nacos服务注册失败/不健康Client not connected, current status:STARTING

文章目录 Nacos报错docker安装不用 docker安装 Nacos报错 docker安装 使用docker在云服务器安装Nacos之后出现Client not connected, current status:STARTING 使用docker 安装之后需要添加映射端口 docker run -e JAVA_OPTS"-Xms256m -Xmx256m"-e MODEstandalone…

领导需求不好接?给你一份“化危为机的自救指南”

领导高瞻远瞩&#xff0c;落地成难&#xff0c;问题&#xff1a;领导常常是急性发挥&#xff0c;时间稍微久一点就思路就模糊了&#xff0c;后续怎么做都不对。 遇到这种情况的小伙伴&#xff0c;公屏打上“pua”。 这种情况下&#xff0c;给出自救指南&#xff1a; 1、引导交…

深度学习——批标准化Batch Normalization

什么是批标准化&#xff1f; 批标准化&#xff08;Batch Normalization&#xff09;是深度学习中常用的一种技术&#xff0c;旨在加速神经网络的训练过程并提高模型的收敛速度。 批标准化通过在神经网络的每一层中对输入数据进行标准化来实现。具体而言&#xff0c;对于每个输…

vue3+elementplus后台管理系统,实现侧边栏菜单显示到主内容区域

目录 1 创建页面2 设置路由3 修改首页4 首页的完整代码总结 我们已经使用vue3和elmentplus初步搭建了首页&#xff0c;上一篇中有个问题没解决&#xff0c;就是在侧边栏导航功能里&#xff0c;如果点击菜单希望是在首页打开页面而不是跳转到新页面。以下是我们希望实现的效果 这…

AI学习笔记二:YOLOV5环境搭建及测试全过程

若该文为原创文章&#xff0c;转载请注明原文出处。 记录yolov5从环境搭建到测试全过程。 一、运行环境 1、系统&#xff1a;windows10 &#xff08;无cpu) 2、yolov5版本&#xff1a;yolov5-5.0 3、python版本&#xff1a;py3.8 在创建虚拟环境前需要先把miniconda3和py…

GUI自动化测试进阶:页面对象模式

本文介绍的是页面对象设计模式及其常见的滥用继承的错误。 本文和语言无关&#xff0c;但作者主要使用python和java。本文假设读者已经具有了一定的python或java基础&#xff0c;知道类和方法是什么。 如果完全没有这方面的基础&#xff0c;请看我的《测试人员如何学Python》。…

【图像分类】基于LIME的CNN 图像分类研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码实现 &#x1f4a5;1 概述 基于LIME&#xff08;Local Interpretable Model-Agnostic Explanations&#xff09;的CNN图像分类研究是一种用于解释CNN模型的方法。LIME是一…

【UE5 多人联机教程】04-加入游戏

效果 步骤 1. 新建一个控件蓝图&#xff0c;父类为“USC_Button_Standard” 控件蓝图命名为“UMG_Item_Room”&#xff0c;用于表示每一个搜索到的房间的界面 打开“UMG_Item_Room”&#xff0c;在图表中新建一个变量&#xff0c;命名为“Session” 变量类型为“蓝图会话结果…

自恢复保险丝(PPTC)的金属材料说明

保险丝大家都是知道的&#xff0c;但保险丝当中的自恢复保险丝&#xff08;PPTC&#xff09;可能就不太了解的。 其实PPTC自恢复保险丝与大家所认识的保险丝一样&#xff0c;都是起到限流作用&#xff0c;达到电路防护效果。简单来说就是一旦电路中的电流超过所规定的电流时&am…

【数据结构】二叉树详解(3)

⭐️ 前言 ✨ 往期链接&#xff1a;【数据结构】二叉树详解(1) 在第一篇二叉树文章中&#xff0c;我们探讨了二叉树的链式结构定义与实现。二叉的遍历包含( 前序/中序/后序遍历 )及代码实现和递归流程图的详细讲解。还有一些二叉树的其他接口定义与实现&#xff0c;包含 Binar…

【vue3】vue3的一般项目结构、成功显示自己的vue3页面

一、vue3的一般项目结构 Vue 3并没有规定特定的项目结构&#xff0c;因此您可以根据项目的需求和个人偏好来组织您的Vue 3项目。以下是一个常见的Vue 3项目结构示例&#xff0c;供参考&#xff1a; your-project/|- public/| |- index.html # 应用程序的入口HTML文件…

【Matlab】基于粒子群优化算法优化BP神经网络的时间序列预测(Excel可直接替换数据)

【Matlab】基于粒子群优化算法优化BP神经网络的时间序列预测&#xff08;Excel可直接替换数据&#xff09; 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码5.1 fun.m5.2 main.m 6.完整代码6.1 fun.m6.2 main.m 7.运行结果 1.模型原理 基于粒子群优化算法&#xff08;Pa…

ubuntu 18.04 磁盘太满无法进入系统

安装了一个压缩包&#xff0c;装了一半提示磁盘空间少导致安装失败。我也没在意&#xff0c;退出虚拟机打算扩展硬盘。等我在虚拟机设置中完成扩展操作&#xff0c;准备进入虚拟机内部进行操作时&#xff0c;发现登录不进去了 shift 登入GUN GRUB设置项的问题 网上都是在开机…

持续贡献开源力量,棱镜七彩加入openKylin

近日&#xff0c;棱镜七彩签署 openKylin 社区 CLA&#xff08;Contributor License Agreement 贡献者许可协议&#xff09;&#xff0c;正式加入openKylin 开源社区。 棱镜七彩成立于2016年&#xff0c;是一家专注于开源安全、软件供应链安全的创新型科技企业。自成立以来&…