RabbitMQ消息应答与发布

消息应答

RabbitMQ一旦向消费者发送了一个消息,便立即将该消息,标记为删除.

消费者完成一个任务可能需要一段时间,如果其中一个消费者处理一个很长的任务并仅仅执行了一半就突然挂掉了,在这种情况下,我们将丢失正在处理的消息,后续给消费者发送的消息也就无法接收到了.

为了确保消息不丢失,我们引入了消息应答机制.

消息应答就是:消费者在接收到生产者的消息并且处理该消息之后,告诉RabbitMQ已经处理完成了,rabbitMQ可以进行删除了.

一个生产者,两个消费者.

/**
消费者1
*/
public class Consumer1 {
 public static final String QueueName = "TaskQueueName";
 public static void main(String[] args) throws IOException, TimeoutException {
     ConnectionFactory factory = new ConnectionFactory();
     factory.setHost("127.0.0.1");
     factory.setUsername("DGZ");
     factory.setPassword("Dgz@#151");
     Connection connection = factory.newConnection();
     Channel channel = connection.createChannel();
     //声明接收消息
     System.out.println("消费者1等待接收消息时间较短");
     DeliverCallback deliverCallback = (consumerTag,message) -> {
         Util.sleep(1);  //等待1s
         System.out.println("正在处理该消息" + new String(message.getBody()));

            /**
             * 手动应答
             * 1.消息的标记Tag
             * 2.是否批量应答 false表示不批量应答信道中的消息
             */
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        };
        //取消消息时的回调
        CancelCallback cancelCallback = consumerTag ->{
            System.out.println("消息消费被中断");
        };
        /**
         * 消费者消费消息
         * 1.消费哪个队列
         * 2.消费成功之后是否要自动应答true:代表自动应答      false:代表手动应答
         * 3.消费者未成功消费的回调
         * 4.消费者取消消费的回调
         */
        boolean autoAck = false;
        channel.basicConsume(QueueName,autoAck,deliverCallback,cancelCallback);
    }
}

/**
消费者2
*/
public class Consumer2 {
 public static final String QueueName = "TaskQueueName";
 public static void main(String[] args) throws IOException, TimeoutException {
     ConnectionFactory factory = new ConnectionFactory();
     factory.setHost("127.0.0.1");
     factory.setUsername("DGZ");
     factory.setPassword("Dgz@#151");
     Connection connection = factory.newConnection();
     Channel channel = connection.createChannel();
     System.out.println("消费者2等待接收消息时间较长");
     //声明接收消息
     DeliverCallback deliverCallback = (consumerTag,message) -> {
         Util.sleep(10); //等待10s
         System.out.println("正在处理该消息" + new String(message.getBody()));

            /**
             * 手动应答
             * 1.消息的标记Tag
             * 2.是否批量应答 false表示不批量应答信道中的消息
             */
            //当该消息还没有被处理的时候,如果此时这个应用挂掉,
            // 由于这个手动应答的机制,就不会删除该消息,而是将给消息交给其他应用去处理
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        };
        //取消消息时的回调
        CancelCallback cancelCallback = consumerTag ->{
            System.out.println("消息消费被中断");
        };
        /**
         * 消费者消费消息
         * 1.消费哪个队列
         * 2.消费成功之后是否要自动应答true:代表自动应答     false:代表手动应答
         * 3.消费者未成功消费的回调
         * 4.消费者取消消费的回调
         */
        boolean autoAck = false;
        channel.basicConsume(QueueName,autoAck,deliverCallback,cancelCallback);
    }
}

消费者1 1s处理一个消息,消费者2 10s处理一个消息

/**
生产者
*/
public class Producer {
 //队列名称
 public static final String QueueName = "TaskQueueName";
 public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
     ConnectionFactory factory = new ConnectionFactory();   //创建连接工厂
     factory.setHost("127.0.0.1");  //主机
     factory.setUsername("DGZ"); //用户名
     factory.setPassword("Dgz@#151"); // 密码
     Connection connection = factory.newConnection();  //通过连接工厂创建一个连接
     Channel channel = connection.createChannel();  //获取信道
     /**
      * 生产一个对列
      * 1.对列名称
      * 2.对列里面的消息是否持久化,默认情况下,消息存储在内存中
      * 3.该队列是否只供一个消费者进行消费,是否进行消息共享,true可以多个消费者消费 false:只能一个消费者消费
      * 4.是否自动删除,最后一个消费者端开链接以后,该队列是否自动删除,true表示自动删除
      * 5.其他参数
      */
     channel.queueDeclare(QueueName,false,false,false,null);
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入消息:");
        while(scanner.hasNext()) {
            String message = scanner.next();
            channel.basicPublish("",QueueName,null,message.getBytes());
            System.out.println("生产者发出消息: " + message);
        }
        /**
         * 发送一个消息
         * 1.发送到哪个交换机
         * 2.路由的key值是哪个本次是队列的名称
         * 3.其他参数信息
         * 4.发送消息的消息体
         */
        System.out.println("消息发送完毕");
    }
}

自动应答和手动应答:

自动应答就是MQ只要把消息发出去,不会管消息是否收到,都会立刻把这个消息进行删除.

手动应答就是MQ把消息发送出去之后,消费者在接收到生产者的消息并且处理该消息之后,告诉RabbitMQ已经处理完成了,rabbitMQ可以进行删除了.

RabbitMQ持久化

当RabbitMQ服务突然挂掉之后,消息生产者发送过来的消息如何保证不丢失.

默认情况下当RabbitMQ服务挂掉之后,它会忽略队列和消息,这时刚刚发送的消息和队列都会丢失.

确保消息不丢失我们需要干两件事,就是将队列和消息标记为持久化.

队列持久化

之前创建的队列都是非持久化的,如果RabbitMQ重启的话,队列就是丢失,如果需要实现持久化队列,那么就需要在声明队列的时候把durable参数设置为true.表示代表开启持久化.

public class Producer {
    public static final String QueueName = "CJ_QUEUE";
    public static void main(String[] args) throws IOException, TimeoutException {
        /**
         * RabbitMQ工具类,用来创建连接等信息
         */
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        boolean durable = true;
        //第二个参数为队列持久化的参数,设置为true,表示队列开启持久化,false表示不开启持久化
        channel.queueDeclare(QueueName,durable,false,false,null);
    }
}

此时web管理端就会看见:

 表示队列持久化

 消息持久化

消息持久化是指消息生产者发布消息的时候,开启消息持久化,

public class Producer {
    public static final String QueueName = "CJ_QUEUE";

    public static void main(String[] args) throws IOException, TimeoutException {
        /**
         * RabbitMQ工具类,用来创建连接等信息
         */
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        boolean durable = true;
        //第二个参数为队列持久化的参数,设置为true,表示队列开启持久化,false表示不开启持久化
        channel.queueDeclare(QueueName,durable,false,false,null);
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要发送的消息");
        while (scanner.hasNext()) {
            String message = scanner.next();
            /**
             * 将发布消息的basicPublish方法的第三个参数设置为MessageProperties.PERSISTENT_TEXT_PLAIN
             * 表示开启消息持久化
             */
            channel.basicPublish
                    ("",QueueName, MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes("UTF-8"));
            System.out.println("生产者发出消息" + message);
        }
    }
}

不公平分发

在最开始的时候RabbitMQ采用的是轮询分发,但是在某种场景下这种策略并不是很好,比如说两个消费者在处理消息,此时消费者1处理消息比较慢,而消费者2处理消息比较快,如果这个时候还是采用轮询分发的方式,那么处理慢的消费者就会一直在处理消息,而处理快的消费者就会有很大时间处于空闲状态.

为了解决这个问题,引入了不公平分发

不公平分发: 如果一个工作队列(消费者)还没有处理完一个消息或者没有应答签收一个消息,则RabbitMQ不会分配新的消息给该队列.

如果所有的消费者都没有完成手上的消息,生产者还在不停地生产消息,队列还在不停地添加新任务,这是不会给消费者分发消息,就有可能导致队列被撑爆.

这时就只能添加新的工作队列或者改变存储策略了.

设置不公平分发:

public class Consumer1 {
    public static final String QueueName = "CJ_QUEUE";
    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        System.out.println("消费消息时间较长-----------");
        DeliverCallback deliverCallback = ((consumerTag,message)->{
            RabbitMQUtil.sleep(10);
            System.out.println("消费消息:" + new String(message.getBody()));
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        });
        CancelCallback cancelCallback = consumerTag ->{
            System.out.println("消息消费被中断");
        };
        //设置不公平分发
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //手动应答
        channel.basicConsume(QueueName,false,deliverCallback,cancelCallback);
    }
}

public class Consumer2 {
    public static final String QueueName = "CJ_QUEUE";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        System.out.println("消费消息时间较短-----------");
        DeliverCallback deliverCallback = ((consumerTag,message)->{
            RabbitMQUtil.sleep(1);
            System.out.println("消费消息:" + new String(message.getBody()));
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        });
        CancelCallback cancelCallback = consumerTag ->{
            System.out.println("消息消费被中断");
        };
        //设置不公平分发
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);

        //采取手动应答
        channel.basicConsume(QueueName,false,deliverCallback,cancelCallback);
    }
}

预期值分发

带权的消息分发

默认的消息发送是异步的,所以在任何时候,channel中不止一个来自消费者收到确认的消息,因此这里就存在一个未确认的消息缓冲区.

因此我们希望限制这里的缓冲区的大小,避免缓冲区中无休止的未确认消息.

这时我们就可以通过basicqos()方法来设置(预取计数来完成).

basicqos方法里面设置通道上允许的未确认消息的最大数量,一旦数据达到配置的数量,RabbitMQ将停止在通道上传递更多的消息.除非有未确认的消息被确认.

例如:假设此时通道上未确认的消息有 4,6,7,9,5,10,并且通道上设置预取计数值为6,这时RabbitMQ将不会在该通道上传递消息.除非这里的消息被确认一个,RabbitMQ将感知到这一变化,并且在发送一条信息.

消息应答和Qos预取值对用户的吞吐量有着重大影响.

不公平分发和预取值分发都用到 basic.qos 方法,如果取值为 1,代表不公平分发,取值不为1,代表预取值分发 

public class Consumer2 {
    public static final String QueueName = "CJ_QUEUE";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        System.out.println("消费消息时间较短-----------");
        DeliverCallback deliverCallback = ((consumerTag,message)->{
            RabbitMQUtil.sleep(1);
            System.out.println("消费消息:" + new String(message.getBody()));
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        });
        CancelCallback cancelCallback = consumerTag ->{
            System.out.println("消息消费被中断");
        };
        //设置不公平分发
        /*int prefetchCount = 1;
        channel.basicQos(prefetchCount);*/

        //设置预期值分发 值为4
        int prefetchCount = 4;
        channel.basicQos(prefetchCount);

        //采取手动应答
        channel.basicConsume(QueueName,false,deliverCallback,cancelCallback);
    }
}

发布确认

生产者发送消息到RabbitMQ后,需要RabbitMQ返回一个ack,表示RabbitMQ已经收到生产者发送的消息.这样生产者就知道自己发送的信息成功了.

发布确认逻辑

如果消息和队列是可持久化的,那么确认消息会在消息写入磁盘之后发出.

broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置 basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。

confirm 模式最大的好处在于是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息, 生产者应用程序同样可以在回调方法中处理该 nack 消息。

发布确认默认是没有开启的,如果要开启需要调用方法confirmSelect(),

channel.confirmSelect();

单个发布确认

这是一种简单的确认发布,他是一种同步确认发布的方式,也就是发布一个消息之后,只有它被确认,后续的消息才能被发布出去.

waitforconfirms()这个方法只有在消息被确认的时候才返回.

如果在指定的时间范围内没有返回,则抛出异常.

这种确认的方式就是发布速度特别慢,因为如果没有确认发布的消息,那么其他消息就只能阻塞等待.

这种方式最多每秒不超过百条的发送量.


/**
 * Created with IntelliJ IDEA.
 *
 * @Author: DongGuoZhen
 * @Date: 2024/01/05/10:28
 * @Description: 单个确认发布
 */
//确认发布指的是成功发送到了队列,并不是消费者消费了消息。
public class Producer1 {
    public static final int MESSAGE_MAX = 1000;
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        pushMessage();
    }

    private static void pushMessage() throws IOException, TimeoutException, InterruptedException {
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        String QueueName = UUID.randomUUID().toString();
        channel.queueDeclare(QueueName,false,true,false,null);
        //开启发布确认
        channel.confirmSelect();

        //开始时间
        long start = System.currentTimeMillis();
        //依次发送1000个消息
        for (int i = 0; i < 1000; i++) {
            String message = i+"";
            channel.basicPublish("",QueueName,null,message.getBytes());
            //发送单个消息立马进行发布确认
            boolean flag = channel.waitForConfirms();
            if(flag) {  //如果成功 true
                System.out.println("消息: "+ message + " 成功发送到队列:" + QueueName);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("发布: " + MESSAGE_MAX + "条消息共耗时:"+ (end-start)+ "ms");
    }
}

 

批量发布确认

单个确认发布的速度非常慢,与单个确认等待相比,如果发送一批然后在一起确认,这样就大大提高了消息的发送速度和吞吐量.

当然这种方式的缺点就是,当有一个消息出现问题时,我们无法知道是那个消息没有发布出去.

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: DongGuoZhen
 * @Date: 2024/01/05/10:28
 * @Description: 批量确认发布
 */
//确认发布指的是成功发送到了队列,并不是消费者消费了消息。
public class Producer2 {
    public static final int MESSAGE_MAX = 1000;
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        pushMessage();
    }

    private static void pushMessage() throws IOException, TimeoutException, InterruptedException {
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        String QueueName = UUID.randomUUID().toString();
        channel.queueDeclare(QueueName,false,true,false,null);
        //开启发布确认
        channel.confirmSelect();
        int bachSize = 100;  //批量发布确认的条数
        //开始时间
        long start = System.currentTimeMillis();
        //依次发送1000个消息
        for (int i = 0; i < 1000; i++) {
            String message = i+"";
            channel.basicPublish("",QueueName,null,message.getBytes());
            if((i+1) % bachSize == 0) {  //当发送的消息达到100条,进行确认发布一次
                channel.waitForConfirms();   //发布确认
                System.out.println("第"+ i+"条消息被确认");
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("发布: " + MESSAGE_MAX + "条消息共耗时:"+ (end-start)+ "ms");
    }
}

 

异步发布确认

异步确认虽然编程上逻辑比较复杂,但是性价比最高,可靠性和效率都很好,利用了回调函数来达到消息的可靠性传递.


/**
 * Created with IntelliJ IDEA.
 *
 * @Author: DongGuoZhen
 * @Date: 2024/01/05/11:02
 * @Description: 异步确认发布
 */
public class Producer3 {
    public static final int MESSAGE_MAX = 1000;

    public static void main(String[] args) throws IOException, TimeoutException {
        pushMessage();
    }

    private static void pushMessage() throws IOException, TimeoutException {
        Channel channel = RabbitMQUtil.RabbitMQ_getChannel();
        String QueueName = UUID.randomUUID().toString();
        channel.queueDeclare(QueueName,false,true,false,null);
        channel.confirmSelect();
        long start = System.currentTimeMillis();
        /**
         * deliveryTag 消息的标记
         * multiple 是否为批量确认
         */
        ConfirmCallback ackCallback = (deliveryTag,multiple) ->{
            System.out.println("确认的消息:" + deliveryTag);
        };
        ConfirmCallback nackCallback = (deliveryTag,multiple) ->{
            System.out.println("未确认的消息:" + deliveryTag);
        };
        //消息监听器  监听那些消息成功了,那些消息失败了
        channel.addConfirmListener(ackCallback,nackCallback);
        //批量发送消息
        for (int i = 0; i < 1000; i++) {
            String message = i+"消息";
            channel.basicPublish("", QueueName,null,message.getBytes());
        }
        long end = System.currentTimeMillis();
        System.out.println("异步确认发布: " + MESSAGE_MAX + "条消息共耗时:"+ (end-start)+ "ms");
    }
}
  • 单独发布消息

同步等待确认,简单,但吞吐量非常有限。

  • 批量发布消息

批量同步等待确认,简单,合理的吞吐量,一旦出现问题但很难推断出是哪条消息出现了问题。

  • 异步处理

最佳性能和资源使用,在出现错误的情况下可以很好地控制,但是实现起来稍微难些

注意:应答和发布的区别:

应答功能属于消费者,消费者消费完消息后告诉RabbitMQ已经消费成功

发布功能属于生产者,生产者生产消息到RabbitMQ,RabbitMQ需要告诉生产者已经收到消息

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

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

相关文章

【BIAI】Lecture 6 - Somatosensory systems

Lecture 6- Somatosensory systems 专业术语 somatosensory system 体感系统 Thermoreceptors 温度感受器 Photoreceptors 光感受器 Chemoreceptoprs 化学感受器 hairy skin 毛发皮肤 glabrous skin 光滑皮肤 sensory receptors 感觉受体 dermal 真皮的 epidermal 表皮的 axon…

外包干了2个多月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

创建高打开率邮件标题的技巧:吸引潜在客户的秘诀

邮件打开率是指什么&#xff1f; 邮件打开率是指打开邮件的人数占发送的收件人总人数的比例。 邮件的打开率是衡量营销效果如何的一个非常重要的指标&#xff0c;而邮件标题又是影响邮件打开率非常重要的因素之一。所以&#xff0c;我们要要重视邮件标题。那我们应该如何编辑…

《移动通信原理与应用》——QPSK调制解调仿真

目录 一、QPSK调制与解调流程图&#xff1a; 二、仿真运行结果&#xff1a; 三、MATLAB仿真代码&#xff1a; 一、QPSK调制与解调流程图&#xff1a; QPSK调制流程图&#xff1a; QPSK解调流程图&#xff1a; 二、仿真运行结果&#xff1a; 1、Figure1:为发送端比特流情…

深入了解WPF控件:常用属性与用法(七)

掌握WPF控件&#xff1a;熟练常用属性&#xff08;七&#xff09; Menu 用于为应用程序指定命令或选项的项列表。它允许用户通过选择不同的菜单项来执行不同的命令或操作。 每个 Menu 可以包含多个 MenuItem 控件。 每个 MenuItem 都可以调用命令或调用 Click 事件处理程序。…

竞赛保研 电影评论情感分析 - python 深度学习 情感分类

1 前言 &#x1f525;学长分享优质竞赛项目&#xff0c;今天要分享的是 &#x1f6a9; GRU的 电影评论情感分析 - python 深度学习 情感分类 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 这…

vue3-组件基础

什么是组件 组件允许我们将 UI 划分为独立的、可重用的部分&#xff0c;并且可以对每个部分进行处理。在实际应用中&#xff0c;组件常常被组织成层层嵌套的树状结构。 定义一个组件 我们一般会将 Vue 组件定义在一个单独的 .vue 文件中&#xff0c;这被叫做单文件组件 (简称…

【加速】Ubuntu 22.04 LTS Steam++ Watt Toolkit 加速 github

项目地址 SteamTools: &#x1f6e0;「Watt Toolkit」是一个开源跨平台的多功能 Steam 工具箱。 下载linux版本 wget https://gitee.com/rmbgame/SteamTools/releases/download/3.0.0-rc.3/Steam%20%20_v3.0.0-rc.3_linux_x64.tgz 解压到/opt/steam sudo mkdir /opt/steam…

【C语言】扫雷游戏完整代码实现

目录 1.game.h 2.game.c 3.progress.c 4.运行结果 1.game.h #define _CRT_SECURE_NO_WARNINGS#include <string.h> #include <stdio.h> #include <time.h> #include<stdlib.h>#define ROW 9 #define COL 9 #define ROWS 11 #define COLS 11 #defin…

ctfshow-反序列化(web271-web276)

目录 web271 web272-273 web274 web275 web276 为什么不用分析具体为什么能成功 ,后面会有几个专题 会对php框架进行更深入的了解 这里面会专门的研究 为什么能够实现RCE 前面作为初步的熟悉 首先知道一下他的框架 知道框架的风格 知道啥版本可以用什么来打 首先先不用太研…

CopyOnWriteArrayList源码

CopyOnWriteArrayList源码 介绍 CopyOnWriteArrayList底层采用数组对元素进行存储&#xff0c;采用写时复制技术:写的时候加锁&#xff0c;将原数组拷贝一份&#xff0c;对新数组进行操作&#xff0c;新数组长度为原数组长度1,写入完成后替换原数组&#xff0c;原数组使用vol…

【Linux】Vagrant搭建Linux环境

1. Vagrant Vagrant是一个基于Ruby的工具&#xff0c;用于创建和部署虚拟化开发环境。它使用Oracle的开源VirtualBox虚拟化系统&#xff0c;使用 Chef创建自动化虚拟环境。 1.1 安装Vagrant 从Vagrant官网下载安装包&#xff0c;执行安装。 1.2 安装VirtualBox 从官网下载…

【linux基础】linux root用户密码忘记解决方式

方式一&#xff1a;进入单用户模式 1.开启虚拟机的时候&#xff0c;按下“e”健进入用户引导界面 2.再按下“e”&#xff0c;进入内核的编辑 3.找到kernel开头的选项&#xff0c;再次按下“e” 4. 输入 5. 按下“b”&#xff0c;启动 6. 启动后&#xff0c;输入passwd&#xf…

Git的管理操作

目录 前言 认识工作区、暂存区、版本库 小结&#xff1a; 使用场景--1&#xff1a; git log&#xff1a; 查看.git文件&#xff1a; 使用场景--2&#xff1a; git status&#xff1a; git diff&#xff1a; 进行提交&#xff1a; 总结&#xff1a; 版本回退 退…

搜索(2):宽度优先搜索

目录 1.宽度优先搜索(BFS) 2.马的遍历(经典宽搜) 2.1 建图 2.2 宽搜 2.3 完整代码 3.洛谷BFS 3.1 奇怪的电梯 3.2 Meteor Shower 1.宽度优先搜索(BFS) 宽搜从根进入&#xff0c;向下逐层扩展&#xff0c;逐层访问 宽搜是通过队列来实现的&#xff0c;用queue创建一个队列…

DataStream API(输出算子)

源算子 源算子 转换算子 转换算子 输出算子 1.连接到外部系统 连接外部系统是计算机科学和信息技术领域中常见的一个任务&#xff0c;通常涉及到与外部数据源或服务进行交互。具体的方法和工具会根据不同的应用场景和需求而有所不同。以下是一些常见的连接外部系统的方法&…

什么是 Web3.0

什么是Web3.0 对于 Web3.0 的解释网上有很多&#xff0c;目前来说 Web3.0 是一个趋势&#xff0c;尚未有明确的定义。我们今天讨论下几个核心的点&#xff0c;就能很好的理解 Web3.0 要解决哪些问题 谁创造数据&#xff0c;这里的数据可以是一篇博客&#xff0c;一段视频&…

Linux的例行性工作(计划任务)

目录 一、单一执行的例行性任务--at&#xff08;一 次性&#xff09; 1、安装 2、启动服务 3、at命令详解 1&#xff09;格式 2&#xff09;参数 3&#xff09;时间格式 4、实例 二、循环执行的例行性任务-- crontab&#xff08;周期性&#xff09; 1、crontd服务 2…

【Go面试向】defer与time.sleep初探

【Go面试向】defer与time.sleep初探 大家好 我是寸铁&#x1f44a; 总结了一篇defer传参与time.sleep初探的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 请大家看下面这段代码&#xff0c;看运行结果会出现什么&#xff0c;为什么&#xff1f; 问题 demo package mainim…

基于SpringBoot Vue航空机票预订系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…
最新文章