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

news2025/4/16 10:30:42

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中进行消费

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知网的caj格式怎么转化成pdf格式?两个方法简单快捷!

在使用知网等学术资源时&#xff0c;我们常常会遇到CAJ格式的文件&#xff0c;然而CAJ格式并不是常见的文件格式&#xff0c;给我们的查阅和分享带来一些不便。为了更方便地处理这些文件&#xff0c;我们可以将其转换为常见的PDF格式。在本文中&#xff0c;我将为您介绍两种简单…

Docker——网络原理

Docker——网络 一、Docker 网络1&#xff0e;Docker 网络实现原理2&#xff0e;Docker 的网络模式 二、网络模式详解1&#xff0e;host模式2&#xff0e;container模式3&#xff0e;none模式4&#xff0e;bridge模式5&#xff0e;自定义网络 三、资源控制1&#xff0e;CPU 资源…

做测开的3年总结

大家好&#xff0c;我是洋子 进入测试行业刚满3年了&#xff0c;毕业北漂也满3年了&#xff0c;回顾一下这3年的工作和生活 下面文章我将会从QA这个岗位的角度复盘一下这3年来的所思所想&#xff0c;有刚入行的小伙伴可能不太清楚QA&#xff08;Quality Assurance&#xff09…

【RabbitMQ】之高可用集群搭建

目录 一、RabbitMQ 集群原理 1、默认集群原理2、镜像集群原理3、负载均衡方案 二、RabbitMQ 高可用集群搭建 1、RabbitMQ 集群搭建2、配置镜像队列3、HAProxy 环境搭建4、Keepalived 环境搭建 一、RabbitMQ 集群简介 1、默认集群原理 3-1、RabbitMQ 集群简介 单台 RabbitM…

第二招牌——创造企业结构性增长的有效战略工具

在现代商业环境中&#xff0c;企业面临着激烈的竞争和不断变化的市场条件。为了在这个竞争激烈的环境中生存和持续发展&#xff0c;企业必须不断寻求创新和增长的机会。这就需要企业创造结构性增长&#xff0c;不仅仅是依靠传统的业务模式和产品线&#xff0c;还要不断的拓展新…

elasticsearch报错问题

标题1.报错问题 标题2.新建一个配置类 package cn.itcast.hotel.config;import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import o…

SecureCRT如何将复制的内容粘贴到word中仍然保持原有字体颜色

SecureCRT如何将复制的内容粘贴到word中仍然保持原有字体颜色 QQ 109792317 说明&#xff1a;当SecureCRT加载了配色文件后&#xff0c;输出的关键字会被不同颜色高亮显示&#xff0c;但是如果复制粘贴到word中会发现成了纯文本&#xff0c;字体颜色消失了。 如何保留 &#x…

真空压力控制技术在三轴仪土壤制样、力学和渗透性能测试中的应用

摘要&#xff1a;在当前的各种三轴测试仪中&#xff0c;对月壤和月壤模拟物的样品制备和力学性能测试还无法实现样品的真空制备、测试过程中的可变围压控制和样品的超真空度准确控制。为此本文提出了实现这些功能的解决方案&#xff0c;解决方案采用不同气体流量控制技术以及特…

npm i babel-plugin-import -D之后报错

替换modules/.bin/XX文件 1.vue-cli-service #!/bin/sh basedir$(dirname "$(echo "$0" | sed -e s,\\,/,g)")case uname in*CYGWIN*) basedircygpath -w "$basedir";; esacif [ -x "$basedir/node" ]; then"$basedir/node"…

Cilium系列-4-Cilium本地路由

系列文章 Cilium 系列文章 前言 在前文中我们提到, cilium install 默认安装后, Cilium 功能启用和禁用情况如下: datapath mode: tunnel: 因为兼容性原因&#xff0c;Cilium 会默认启用 tunnel&#xff08;基于 vxlan) 的 datapatch 模式&#xff0c;也就是 overlay 网络结…

【主成分分析(PCA)】

主成分分析&#xff08;PCA&#xff09; 摘要 在现代数据科学中&#xff0c;维度灾难常常是数据处理与分析的一大难题。主成分分析&#xff08;PCA&#xff09;是一种广泛使用的数据降维技术&#xff0c;它通过将原始数据转换为新的低维空间&#xff0c;保留最重要的信息&…

【docker】docker部署tomcat

目录 1.1 搜索tomcat镜像1.2 拉取tomcat镜像1.3 创建容器&#xff0c;设置端口映射、目录映射1.4 测试 1.1 搜索tomcat镜像 docker search tomcat1.2 拉取tomcat镜像 docker pull tomcat1.3 创建容器&#xff0c;设置端口映射、目录映射 # 在/root目录下创建tomcat目录用于存…

网络:TCP/IP协议

1. OSI七层参考模型 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 2. TCP/IP模型 应用层 传输层 网络层 数据链路层 物理层 3. 各链路层对应的名称 应用层对应的是协议数据单元 传输层对应的是数据段 网络层对应的是数据包 链路层对应的是数据帧 物理层对应的是比特…

领域驱动设计(五) - 战术设计 - 【2/3】领域服务、事件、模块

一、领域服务 当操作不适合放在聚合和值对象上时&#xff0c;最好的方式就是使用领域服务。领域服务是一个无状态的操作&#xff0c;一个领域服务有可能操作多个领域对象&#xff0c;它用于实现特定于某个领域的任务。领域服务需要处理逻辑&#xff0c;不建议做为soap接口对外…

C++day7(异常处理机制、Lambda表达式、类型转换、STL标准库模板、迭代器、list)

#include <iostream>using namespace std; template <typename T> class vector { private:T* first;T* last;T* end; public:vector():first(new T),last(first),end(first){cout<<"无参构造"<<endl;}//无参构造vector(T* f):first(f),last…

如何写好代码

一、什么是好代码 抛开性能、并发、一致性等技术因素&#xff0c;好的业务代码&#xff0c;应当如一篇显浅易懂的业务叙实文章&#xff0c;满足以下几个基本条件&#xff1a; 词要达意&#xff1a;最基础的变量、函数、类的命名&#xff0c;是否名达其意。 结构清晰&#xff…

supersqli

这个题&#xff0c;其实之前做过&#xff0c;这里只是换了个名字而已 输入1&#xff0c;提交后可以明显发现url发生变化 &#xff0c;可以猜测SQL注入 源码提示sqlmap是没有灵魂的&#xff0c;说明确实是sql注入 万能密码可以可以到&#xff0c;所包含的表 利用order by查看&a…

安卓之事件分发机制

安卓之事件分发机制 简介 事件分发的”事件“是指什么&#xff1f; 答&#xff1a;点击事件&#xff08;Touch事件&#xff09;。当用户触摸屏幕&#xff08;VIew或ViewGroup&#xff09;时&#xff0c;将产生点击事件&#xff0c;即Touch事件。Touch事件的细节&#xff08;如…

SpringBoot整合MyBatisPlus入门

SpringBoot整合MyBatisPlus入门 1. MyBatisPlus概述1.1 MyBatis介绍1.2 MyBatisPlus特性 2. SpringBoot整合MyBatisPlus入门2.1 创建新模块&#xff0c;选择Spring初始化&#xff0c;并配置模块相关基础信息2.2 选择当前模块需要使用的依赖&#xff08;JDBC即可&#xff09;2.3…

【前端知识】React 基础巩固(三十一)——store数据的订阅和Redux的优化

React 基础巩固(三十一)——store数据的订阅和Redux的优化 一、store数据的订阅 store/index.js const { createStore } require("redux");// 初始化数据 const initialState {name: "test",title: "hello redux", };function reducer(state …