RabbitMQ实现延迟消息,RabbitMQ使用死信队列实现延迟消息,RabbitMQ延时队列插件

news2024/11/18 15:27:06

文章目录

  • 一、什么是延迟消息
    • 1、队列的属性
    • 2、消息的属性
    • 3、什么是死信
    • 4、使用死信队列的缺点
    • 5、延时消息插件
  • 二、JavaAPI利用死信队列实现RabbitMQ延迟消息
    • 1、代码实现
    • 2、基本流程
  • 三、JavaAPI利用插件实现RabbitMQ延迟消息
    • 1、代码实现
    • 2、基本原理
  • 四、Springboot利用死信队列实现延迟消息
    • 1、配置实现
  • 五、Springboot利用插件实现延迟消息
    • 1、配置实现

一、什么是延迟消息

假设有一个业务场景:超过30分钟未付款的订单自动关闭,这个功能应该怎么实现?

RabbitMQ使用死信队列,可以实现消息的延迟接收。

1、队列的属性

队列有一个消息过期属性。就像丰巢超过24小时就收费一样,通过设置这个属性,超过了指定事件的消息将会被丢弃。

这个属性交:x-message-ttl

所有队列中的消息超过时间未被消费时,都会过期。不管是谁发送的消息都一视同仁。

@Bean("ttlQueue")
public Queue queue() {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("x-message-ttl", 11000); // 队列中的消息未被消费11秒后过期
    // map.put("x-expire", 30000); // 队列30秒没有使用以后会被删除
    return new Queue("TTL_QUEUE", true, false, false, map);
}

但是这种方式似乎并不是那么的灵活。所以RabbitMQ的消息也有单独的过期时间属性。

2、消息的属性

在生产者发送消息时,可以通过MessageProperties指定消息属性。

MessageProperties messageProperties = new MessageProperties();
messageProperties.setExpiration("4000"); // 消息的过期属性,单位ms
messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
Message message = new Message("这条消息4秒后过期".getBytes(), messageProperties);
rabbitTemplate.send("TTL_EXCHANGE", "test.ttl", message);

那么问题来了:如果队列的TTL是6秒过期,消息的TTL是10秒过期,这个消息会在什么时候被丢弃?
答:如果同时指定了Message TTL和Queue TTL,那么小的那个会生效。

3、什么是死信

上面我们了解到,rabbitMQ的消息可以设置过期时间,消息过期后会被直接丢弃,我们可以通过配置死信队列,将这种消息变成死信(Dead Letter),然后将这种过期的消息丢入死信队列。

队列在创建的时候可以指定一个死信交换机DLX(Dead Letter Exchange)。死信交换机绑定的队列被称为死信队列DLQ(Dead Letter Queue),DLX实际上也是普通的交换机,DLQ也是普通的队列。

在这里插入图片描述
也就是说,如果消息过期了,队列指定了DLX,就会发送到DLX。如果DLX绑定了DLQ,就会路由到DLQ。路由到DLQ之后,我们就可以消费死信队列了。

4、使用死信队列的缺点

(1)如果统一用队列来设置消息的TTL,当梯度非常多的情况下,比如1分钟、2分钟、5分钟、10分钟……需要创建很多交换机和队列来路由消息,这时可以考虑使用消息的TTL。
(2)如果单独设置消息的TTL,则可能会造成队列中的消息阻塞——前一条消息没有出队(没有被消费),后面的消息无法投递(比如第一条消息的过期时间是30分钟,第二条消息的过期时间是10分钟。10分钟后,即使第二条消息应该投递了,但是由于第一条消息还未出队,所以无法投递)。
(3)可能存在一定的时间误差。

5、延时消息插件

在RabbitMQ 3.5.7及以后的版本提供了一个插件(rabbitmq-delayed-message-exchange)来实现延时队列功能(Linux和Windows都可以用)。同时插件依赖Erlang/OPT 18.0及以上。

插件源码地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange
插件下载地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
找到对应版本的插件,然后下载。


# 下载到plugins目录
cd rabbitmq_server-3.7.7/plugins

wget https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v3.8.0/rabbitmq_delayed_message_exchange-3.8.0.ez

# 启用插件
./rabbitmq-plugins enable rabbitmq_delayed_message_exchange

# 停用插件
./rabbitmq-plugins disable rabbitmq_delayed_message_exchange

此时,在管理界面的创建交换机页面,会出现一个x-delayed-message类型的交换机:
在这里插入图片描述

二、JavaAPI利用死信队列实现RabbitMQ延迟消息

1、代码实现

引包:

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.6.0</version>
</dependency>
import com.gupaoedu.util.ResourceUtil;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.impl.AMQBasicProperties;
import java.util.HashMap;
import java.util.Map;

/**
 * 消息生产者,通过TTL测试死信队列
 */
public class DlxProducer {

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUri(ResourceUtil.getKey("rabbitmq.uri"));// rabbitmq.uri=amqp://admin:admin@192.168.56.10:5672

        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        String msg = "Hello world, Rabbit MQ, DLX MSG";

        // 设置属性,消息10秒钟过期
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2) // 持久化消息
                .contentEncoding("UTF-8")
                .expiration("5000") // TTL
                .build();

        // 发送消息,普通队列
        channel.basicPublish("ORI_USE_EXCHANGE", "ORI_USE_QUEUE", properties, msg.getBytes());

        channel.close();
        conn.close();
    }
}

import com.gupaoedu.util.ResourceUtil;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 5秒钟后,消息会从正常队列 ORI_USE_QUEUE 到达死信交换机 DEAD_LETTER_EXCHANGE ,然后路由到死信队列 DEAD_LETTER_QUEUE
 *
 */
public class DlxConsumer {

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUri(ResourceUtil.getKey("rabbitmq.uri")); // rabbitmq.uri=amqp://admin:admin@192.168.56.10:5672
        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        // 指定队列的死信交换机
        Map<String,Object> arguments = new HashMap<String,Object>();
        arguments.put("x-dead-letter-exchange","DEAD_LETTER_EXCHANGE");
        // arguments.put("x-expires",9000L); // 设置队列的TTL
        // arguments.put("x-max-length", 4); // 如果设置了队列的最大长度,超过长度时,先入队的消息会被发送到DLX

        // 声明交换机
        // String exchange, String type, boolean durable, boolean autoDelete, Map<String, Object> arguments
        channel.exchangeDeclare("ORI_USE_EXCHANGE","direct",false, false, null);
        // 声明队列(默认交换机AMQP Direct)
        // String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        channel.queueDeclare("ORI_USE_QUEUE", false, false, false, arguments);
        // 绑定队列和交换机,以及routingKey
        channel.queueBind("ORI_USE_QUEUE","ORI_USE_EXCHANGE","ORI_USE_QUEUE");

        // 声明死信交换机
        channel.exchangeDeclare("DEAD_LETTER_EXCHANGE","topic", false, false, false, null);
        // 声明死信队列
        channel.queueDeclare("DEAD_LETTER_QUEUE", false, false, false, null);
        // 绑定,此处 Dead letter routing key 设置为 #
        channel.queueBind("DEAD_LETTER_QUEUE","DEAD_LETTER_EXCHANGE","#");
        System.out.println(" Waiting for message....");

        // 创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("Received message : '" + msg + "'");
            }
        };

        // 开始获取消息,消费死信队列
        // String queue, boolean autoAck, Consumer callback
        channel.basicConsume("DEAD_LETTER_QUEUE", true, consumer);
    }
}

2、基本流程

利用消息的过期时间,过期之后投递到死信交换机(DLX),路由到死信队列(DLQ),我们消费者监听死信队列(DLQ),实现延迟消息。

消息的流转流程:生产者- 原交换机 - 原队列(超过TTL之后) - 死信交换机 - 死信队列 - 最终消费者。

在这里插入图片描述

三、JavaAPI利用插件实现RabbitMQ延迟消息

1、代码实现

import com.rabbitmq.client.*;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 *  使用延时插件实现的消息投递-消费者
 *  必须要在服务端安装rabbitmq-delayed-message-exchange插件
 *  先启动消费者
 */
public class DelayPluginConsumer {

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUri("amqp://admin:admin@192.168.56.10:5672");
        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        // 声明x-delayed-message类型的exchange
        Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-delayed-type", "direct");
        channel.exchangeDeclare("DELAY_EXCHANGE", "x-delayed-message", false,
                false, argss);

        // 声明队列
        channel.queueDeclare("DELAY_QUEUE", false,false,false,null);

        // 绑定交换机与队列
        channel.queueBind("DELAY_QUEUE", "DELAY_EXCHANGE", "DELAY_KEY");

        // 创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                System.out.println("收到消息:[" + msg + "]\n接收时间:" +sf.format(new Date()));
            }
        };

        // 开始获取消息
        // String queue, boolean autoAck, Consumer callback
        channel.basicConsume("DELAY_QUEUE", true, consumer);
    }
}
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 *  使用延时插件实现的消息投递-生产者
 *  必须要在服务端安装rabbitmq-delayed-message-exchange插件
 *  先启动消费者
 */
public class DelayPluginProducer {

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUri("amqp://admin:admin@192.168.56.10:5672");

        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        // 延时投递,比如延时10秒
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, +10);// 10秒
        Date delayTime = calendar.getTime();

        // 定时投递,把这个值替换delayTime即可
        // Date exactDealyTime = new Date("2019/01/14,22:30:00");

        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String msg = "发送时间:" + sf.format(now) + ",投递时间:" + sf.format(delayTime);

        // 延迟的间隔时间,目标时刻减去当前时刻
        Map<String, Object> headers = new HashMap<String, Object>();
        headers.put("x-delay", delayTime.getTime() - now.getTime());

        AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder()
                .headers(headers);
        channel.basicPublish("DELAY_EXCHANGE", "DELAY_KEY", props.build(),
                msg.getBytes());

        channel.close();
        conn.close();
    }
}

2、基本原理

rabbitMQ的延迟消息插件,可以有效的避免消息堵塞问题。

相当于投递给一个延迟消息的交换机,并指定延迟时间,大大简化了开发。

四、Springboot利用死信队列实现延迟消息

1、配置实现

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * 死信队列 DLX DLQ
 */
@Configuration
public class DlxConfig {
    @Bean
    public ConnectionFactory connectionFactory() throws Exception {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setUri(ResourceUtil.getKey("rabbitmq.uri"));
        return cachingConnectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }

    @Bean("oriUseExchange")
    public DirectExchange exchange() {

        return new DirectExchange("ORI_USE_EXCHANGE", true, false, new HashMap<>());
    }

    @Bean("oriUseQueue")
    public Queue queue() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("x-message-ttl", 10000); // 10秒钟后成为死信
        map.put("x-dead-letter-exchange", "DEAD_LETTER_EXCHANGE"); // 队列中的消息变成死信后,进入死信交换机
        return new Queue("ORI_USE_QUEUE", true, false, false, map);
    }

    @Bean
    public Binding binding(@Qualifier("oriUseQueue") Queue queue,@Qualifier("oriUseExchange") DirectExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("ori.use");
    }

    /**
     * 队列的死信交换机
     * @return
     */
    @Bean("deatLetterExchange")
    public TopicExchange deadLetterExchange() {
        return new TopicExchange("DEAD_LETTER_EXCHANGE", true, false, new HashMap<>());
    }

    @Bean("deatLetterQueue")
    public Queue deadLetterQueue() { // 消费者只监听该队列即可
        return new Queue("DEAD_LETTER_QUEUE", true, false, false, new HashMap<>());
    }

    @Bean
    public Binding bindingDead(@Qualifier("deatLetterQueue") Queue queue,@Qualifier("deatLetterExchange") TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("#"); // 无条件路由
    }

}

import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = "com.dlx.ttl")
public class DlxSender {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DlxSender.class);
        RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);

        // 随队列的过期属性过期,单位ms
        rabbitTemplate.convertAndSend("ORI_USE_EXCHANGE", "ori.use", "测试死信消息");

    }
}

五、Springboot利用插件实现延迟消息

1、配置实现

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

// 配置类
@Configuration
public class DelayPluginConfig {
    @Bean
    public ConnectionFactory connectionFactory() throws Exception {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setUri("amqp://admin:admin@192.168.56.10:5672");
        return cachingConnectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }

    @Bean("delayExchange")
    public TopicExchange exchange() {
        Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-delayed-type", "direct");
        return new TopicExchange("DELAY_EXCHANGE", true, false, argss);
    }

    @Bean("delayQueue")
    public Queue deadLetterQueue() {
        return new Queue("DELAY_QUEUE", true, false, false, new HashMap<>());
    }

    @Bean
    public Binding bindingDead(@Qualifier("delayQueue") Queue queue, @Qualifier("delayExchange") TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("#"); // 无条件路由
    }

}

import com.rabbitmq.client.*;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 消费者
 */
public class DelayPluginConsumer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUri("amqp://admin:admin@192.168.56.10:5672");
        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        // 创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                System.out.println("收到消息:[" + msg + "]\n接收时间:" +sf.format(new Date()));
            }
        };

        // 开始获取消息
        // String queue, boolean autoAck, Consumer callback
        channel.basicConsume("DELAY_QUEUE", true, consumer);
    }
}
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;


/**
 * 生产者
 * 延时消息插件,去管控台队列看有无收到消息
 */
@ComponentScan(basePackages = "com.dlx.delayplugin")
public class DelayPluginProducer {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DelayPluginProducer.class);
        RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);

        // 延时投递,比如延时4秒
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, +4);
        Date delayTime = calendar.getTime();

        // 定时投递,把这个值替换delayTime即可
        // Date exactDealyTime = new Date("2019/06/24,22:30:00");
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String msg = "延时插件测试消息,发送时间:" + sf.format(now) + ",理论路由时间:" + sf.format(delayTime);

        MessageProperties messageProperties = new MessageProperties();
        // 延迟的间隔时间,目标时刻减去当前时刻
        messageProperties.setHeader("x-delay", delayTime.getTime() - now.getTime());
        Message message = new Message(msg.getBytes(), messageProperties);

        // 不能在本地测试,必须发送消息到安装了插件的服务端
        rabbitTemplate.send("DELAY_EXCHANGE", "#", message);

    }
}

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

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

相关文章

5分钟自建可视化平台,在线拖拽组件也太方便了!

一、前言 我们在完成 C 端用户的业务需求时&#xff0c;发现大量电商推广页面的逻辑复杂度和交互程度都比较低&#xff0c;却要消耗大量的开发资源。同时在后台管理系统的开发过程中&#xff0c;我们也发现一些相同套路的页面在不同场景下需要重复开发&#xff0c;即使我们封装…

如何提升企业采购绩效?提高采购绩效的方法

采购是任何企业的一个重要方面&#xff0c;它在确保企业平稳高效运行方面发挥着重要作用。作为一名采购专业人员&#xff0c;你有责任确保你的企业以正确的质量、数量和价格获得所需的货物和服务。 本文将讨论一些最佳做法&#xff0c;这些做法可以帮助你提高采购专业人员的绩…

【032】C++高级开发之多态技术详解(虚函数最全讲解)

C的多态技术&#xff08;虚函数&#xff09;详解 引言一、多态的概念二、虚函数2.1、父类指针保存子类空间地址 带来的问题2.2、虚函数的定义2.3、虚函数的动态绑定机制2.4、重载、重定义、重写的区别 三、纯虚函数3.1、纯虚函数的定义方式3.2、纯虚函数的案例&#xff1a;饮品…

mvnd 安装和 idea配置mvnd

一、mvnd 安装 1、mvnd 下载地址 https://github.com/apache/maven-mvnd 2、安装 解压并配置环境变量到path 打开CMD终端&#xff0c;输入 mvnd -v 看到如下信息提示则安装成功 默认使用的是内置的maven&#xff0c;配置修改&#xff0c;兼容本地的maven 打开 Maven-mvn…

基于Java医院医患管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

如何测试webservice接口

看过网上很多对Web Service的解释&#xff0c;有些文章写得通俗易懂&#xff0c;但是缺少深度解读&#xff1b;有的文章图文并茂&#xff0c;但是没有现成案例&#xff1b;有的文章非常详细&#xff0c;但是没有直观感受。 于是&#xff0c;我想从测试一个web service接口的角…

【复习《剑指Offer》6-12题】【每天40分钟,我们一起用50天刷完 (剑指Offer)】第七天 7/50

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

围“桌”详谈|本周六直播,一起聊聊「发版」那些事儿!

&#x1f30d; 你想要深入了解 ShardingSphere 、与业内技术大咖「面对面」探讨数据库领域的前沿趋势吗&#xff1f;那就一定不能错过由 ShardingSphere 社区出品、SphereEx 公司赞助的全球软件工程师必看的直播节目——《对谈时刻》&#xff01;每期节目&#xff0c;我们都会邀…

VRRP缺省配置

一、VRRP缺省配置 表&#xff1a;VRRP参数缺省值 二、VRRP场景作用 表&#xff1a;VRRP场景作用 三、VRRP配置缺省参数汇总 1&#xff09;配置VRRP备份组最大数 缺省情况下&#xff0c;设备只支持配置256个VRRP备份组&#xff08;VRRP4和VRRP6备份组的总和&#xff09; 当需…

计算机原理二:磁盘和内存速度差多少?

前言 作为程序员&#xff0c;我们不深究硬件&#xff0c;无需深入了解硬件的各个电路信号等细节&#xff0c;只了解存储器的基本相关知识。 存储器的层次结构 寄存器 计算机的一种硬件组件&#xff0c;用于存储和快速访问处理器的数据&#xff0c;位于CPU内部的小型存储器单…

火拼折叠屏:国产手机的杀手锏还是遮羞布?

刚过去的618&#xff0c;手机市场一反常态。 过去&#xff0c;国产安卓旗舰上演“大跳水”&#xff0c;苹果价格坚挺&#xff1b;现在&#xff0c;安卓旗舰优惠力度小&#xff0c;苹果却大降价&#xff0c;iPhone 14 Pro在各平台的优惠力度达到上千元。 IDC中国研究经理郭天翔…

安卓应用中的版本问题

用android Studio 做了一个应用&#xff0c;想发布在google play 里&#xff0c;就碰到一个版本的问题。 我上传了一个版本&#xff0c;更新了一点点&#xff0c;结果有这样一个错误&#xff1a;Version code 1 has already been used. Try another version code。本文就这问题…

【pytest学习总结2.3 】- 如何使用固定装置 fixtures (1)

目录 2.3 如何使用固定装置 fixtures 2.3.1 请求并调用固定装置 fixture 2.3.2 自动固定装置&#xff08;不需要你请求调用&#xff09; 2.3.3 固定装置的范围&#xff1a;function, class, module, package or session. 2.3.4 测试后置 teardown/cleanup 2.3.5 安全的 t…

Redis列表类型(list)模拟队列操作

文章目录 Redis列表类型模拟队列操作1. 使用用lpush和rpop模拟队列的操作1.1 lpush介绍1.2 rpop介绍1.3 llen介绍1.4 lrange介绍1.5 del命令介绍 2. 使用用rpush和lpop模拟队列的操作2.1 rpush介绍2.2 lpop介绍 Redis列表类型模拟队列操作 Redis的列表类型&#xff08;list&…

JavaScript 中内存泄漏的几种情况?

一、是什么 内存泄漏&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;由于设计错误&#xff0c;导致在释放该段内存之前就失…

emoji表情符号,unicode表情符号

目录 &#x1f60e;前言&#x1f468;‍&#x1f4bb;使用✈️Unicode 1.1 版本新增☙ Unicode 3.0 版本新增♻️Unicode 3.2 版本新增☕Unicode 4.0 版本新增&#x1f028;Unicode 5.1 版本新增⚽ Unicode 5.2 版本新增&#x1f30a;Unicode 6.0 版本新增&#x1f619;Unicode…

集合专题----List篇

1、Collection常用方法 package com.example.collection.Collection;import java.util.ArrayList; import java.util.List;public class Collection03 {public static void main(String[] args) {List list new ArrayList();//接口可以指向实现该接口的类//add:添加单个元素l…

BEVFromer论文研读

1. 总体结构 上图为BEVFormer在t时刻的网络结构。图(a) 表示的是BEVFormer的encoder层。BEVFormer有6个encoder层&#xff0c;每一个encoder除了本文自定义的三个组件外都和传统的transformers结果一致。自定义的三个组件分别是网格状的BEV queries&#xff0c;TSA和SCA。其中B…

【智慧交通项目实战】 《 OCR车牌检测与识别》(三):基于改进CRNN的车牌识别

&#x1f468;‍&#x1f4bb;作者简介&#xff1a; CSDN、阿里云人工智能领域博客专家&#xff0c;新星计划计算机视觉导师&#xff0c;百度飞桨PPDE&#xff0c;专注大数据与AI知识分享。✨公众号&#xff1a;GoAI的学习小屋 &#xff0c;免费分享书籍、简历、导图等&#xf…

接口测试实战篇,吐血整理汇总,接口测试你知多少?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口测试&#xf…