【RabbitMQ(二)】:Exchange 详解 | Message Convert 消息转换器

文章目录

    • 03. 使用 Java 代码去操控 RabbitMQ
      • 3.1 快速入门
        • 3.1.1 创建父子项目
        • 3.1.2 编写代码
      • 3.2 Work 模型
      • 3.3 RabbitMQ 中的三类交换机
        • 3.3.1 Fanout 扇出交换机
        • 3.3.2 Direct 交换机
        • 3.3.3 Topic 交换机
      • 3.4 声明队列交换机
        • 3.4.1 方式一:书写 Config 类
        • 3.4.2 方式二:注解方式创建
      • 3.5 消息转换器

03. 使用 Java 代码去操控 RabbitMQ

💡 Spring 提供了对 RabbitMQ 的支持:Spring AMQP


💡 AMQP Spring AMQP

  • AMQP(Advanced Message Queuing Protocol):是用在应用程序之间传递业务消息的开放标准。该协议与语言和平台无关,更符合微服务中独立性的要求。
  • Spring AMQP:是 Spring 基于 AMQP 设计的一套 API 规范,提供了 发送和接收消息 的 API。
    • 声明式配置:Spring AMQP 提供了一组注解和 XML 配置来声明和配置消息队列、交换机、绑定等 AMQP 元素,使得配置变得简单和灵活。
    • 消息发送和接收:Spring AMQP 提供了 AmqpTemplate 接口,用于发送和接收消息。开发者可以使用 AmqpTemplate 接口中的方法来发送消息到队列或交换机,并从队列或交换机中接收消息。
    • 消息监听容器:Spring AMQP 提供了 @RabbitListener 注解和 SimpleMessageListenerContainer 类来简化消息监听的配置和管理。开发者可以使用 @RabbitListener 注解将方法标记为消息监听器,并通过配置 SimpleMessageListenerContainer 来管理消息监听器的运行。
    • 消息转换:Spring AMQP 提供了一组消息转换器(MessageConverter),用于在消息发送和接收之间进行消息 格式 的转换。开发者可以使用消息转换器来将消息对象转换为字节流,或将字节流转换为消息对象,以实现消息的序列化和反序列化。
    • 事务管理:Spring AMQP 提供了对事务的支持,开发者可以使用 RabbitTransactionManager 类来管理消息发送和接收的事务,以确保消息的可靠传递。

3.1 快速入门

3.1.1 创建父子项目

💡 这里使用 maven 创建父子项目来演示,父项目命名为 mq-demo,下面包含两个子项目 publisherconsumer 来分别扮演消息发送者和消息消费者。

👉 创建父项目

👉 依赖中选择 Lombok Spring Web Spring for RabbitMQ

👉 删除掉不需要的部分

👉 更改 pom.xml 文件中的打包方式为 pom,且需要注册 modules

<packaging>pom</packaging>
<modules>
    <module>publisher</module>
    <module>consumer</module>
</modules>

👉 创建子项目 publisher,无需选择依赖

👉 修改其父项目为 mq-demo

	<parent>
		<groupId>com.example</groupId>
		<artifactId>mq-demo</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

👉 以相同的方法创建 consumer Module

👉 打开 maven 的 Group Modules 选项

👉 最终父项目配置文件参考

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>mq-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mq-demo</name>
    <modules>
        <module>publisher</module>
        <module>consumer</module>
    </modules>
    <packaging>pom</packaging>
    <description>mq-demo</description>
    <properties>
        <java.version>21</java.version>
    </properties>
    <dependencies>
        <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>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.15.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.1.2 编写代码

💡 这里使用将消息添加到队列和监听队列的方式去演示

上面父项目中已经导入了相关的包,子项目可以直接引用

👉 在 application.yaml 中配置 RabbitMQ 服务器

spring:
  rabbitmq:
    host: 192.168.88.3 # 服务器的 ip 地址
    port: 5672 # 服务器端口
    virtual-host: /myHost # 主机名
    username: root # 账号
    password: 123456 # 密码

👉 创建一个测试使用的队列

👉 将 publisher 的代码写在 test 文件中,首先要引入 RabbitTemplate,这是对请求代码的 高级封装

@SpringBootTest
class PublisherApplicationTests {
    @Resource
    RabbitTemplate rabbitTemplate;

    @Test
    void contextLoads() {
        String queueName = "simple.queue";
        String message = "Hello RabbitMq!";
        rabbitTemplate.convertAndSend(queueName, message);
    }
}

👉 consumer 中的代码写在 /listeners/MqListener 中,使用 @RabbitListener 来指定监听的队列

package com.example.consumer.listeners;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class MqListener {
    @RabbitListener(queues = "simple.queue")
    public void listen(String message) {
        log.info("收到消息{}", message);
    }
}

💡 需要注意的是两个子模块的配置文件中都需要表明服务器的相关配置。

👉 运行 consumerpublisher 中的代码,检查是否成功接收到消息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.2 Work 模型

💡 RabbitMQ Work 模型是一种简单的消息队列模型,也被称为简单队列模式(Simple Queue Pattern)。在 Work 模型中,多个消费者共享同一个队列,并从该队列中获取消息进行处理。

  • 这个模型有一个特点叫做 消息均衡消费,即:RabbitMQ 会将消息均匀地分配给各个消费者进行处理,以实现负载均衡。即使消费者的处理能力不同RabbitMQ 也会尽量将消息均匀分发,以保证每个消费者都能够参与到消息的处理中。

👉 先来测试一下这个模型,在 consumer 中创建两个 方法 去监听同一个端口,一个方法处理完后休眠 1000ms,另一个不休眠,统计它们处理消息的数量:

    int listener1 = 1;
    int listener2 = 1;

    @RabbitListener(queues = "work.queue")
    public void listenerWorkQueue1(String msg) throws InterruptedException {
        log.info("listener1 收到消息{},目前共处理消息{}条", msg, listener1++);
    }

    @RabbitListener(queues = "work.queue")
    public void listenerWorkQueue2(String msg) throws InterruptedException {
        log.error("listener2 收到消息{},目前共处理消息{}条", msg, listener2++);
        Thread.sleep(1000);
    }

💡 后面不再每次强调队列的创建,大家可以自主创建队列来进行模拟或者参考我代码中的队列。

👉 利用 publisher 发送消息

    @Test
    void publishTest() throws InterruptedException {
        String queueName = "work.queue";
        for (int i = 0; i < 50; i++) {
            String message = "Hello RabbitMq! id = " + i;
            rabbitTemplate.convertAndSend(queueName, message);
        }
    }

👉 观察控制台,可以发现两个方法分别处理了 25 条消息,即使它们的处理速度差别非常大。

❗ 但肯定希望能力强的 consumer 多处理一些部分,这就需要了解 RabbitMQ 的预取机制,在 RabbitMQ 中,预取(Prefetch)是指消费者在处理消息之前,从 RabbitMQ 服务器预取一定数量的消息到本地缓冲区的行为。预取机制允许消费者一次性从服务器获取多个消息,以提高消息处理的效率和吞吐量。

💡 Spring AMQP 会根据消费者的数量来 平均 分配预取的数量,这就导致了上面出现的各 25 条的现象。

👉 所以为了使得预取数量可以按照自己的能力来取得,可以手动将预取的数量设定为 1 也就是 consumer 处理完这一条消息后再去取下一条,这样就实现了按照工作能力来分配消息。

application.xml

spring:
  rabbitmq:
    host: 192.168.88.3
    port: 5672
    virtual-host: /myHost # 主机名
    username: root
    password: 123456
    listener:
      simple:
        prefetch: 1

👉 再次测试

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 处理速度快的直接就处理了 49 条消息。

3.3 RabbitMQ 中的三类交换机

💡 真实的生产活动中都是通过 exchange 也就是交换机,来发送消息

❓为什么要通过交换机来发消息而不是直接发送到队列呢?

  • 首先是后续拓展的问题,后续如果加入一个 consumer 而它的队列又不是已有的队列,这时候就需要新增 publisher 中的代码;而通过交换机则不同 consumer 可以将自己指定的队列接入到交换机(这在 consumer 方是很容易实现的),publisher 只需要将消息发布到 exchange 就可以实现分配,实现了生产者和消费者的 解耦,提高了系统的灵活性。
  • 交换机可以 根据路由规则 将消息转发到符合条件的队列中,从而实现消息的 过滤和转发。通过合理设置交换机的绑定规则,可以实现精确的消息路由和分发,满足不同业务场景下的需求。
  • RabbitMQ 提供了不同类型的交换机,如直连交换机(Direct Exchange)、主题交换机(Topic Exchange)、扇出交换机(Fanout Exchange)等。每种交换机类型都有不同的路由规则和特点,可以根据实际需求选择合适的交换机类型来实现灵活的消息路由。
3.3.1 Fanout 扇出交换机

💡 Fanout Exchange 会将其接收到的消息发布到每一个和其 bind 的队列

  • 可以实现发布/订阅模式(Pub/Sub),publisher 将消息通过该交换机广播出去,所以也被称为广播模式。

👉 监听端和上面的代码相同,没什么变化,仍然指向监听的队列即可,将这两个队列绑定到 amq.fanout

    @RabbitListener(queues = "fanout.queue1")
    public void listenerFanoutQueue1(String msg) throws InterruptedException {
        log.info("Fanout listener1 收到消息{}", msg);
    }

    @RabbitListener(queues = "fanout.queue2")
    public void listenerFanoutQueue2(String msg) throws InterruptedException {
        log.error("Fanout listener2 收到消息{}", msg);
    }

💡 amq.fanout 是系统创建时默认携带的交换机,当然也可以根据业务去创建自己的交换机

  • 通过在图形化控制界面指定新创建的交换机的 type 来创建不同的交换机

👉 编写 publisher 代码

    @Test
    void FanoutTest() throws InterruptedException {
        String exchangeName = "amq.fanout";
            String message = "Hello Everyone!";
            rabbitTemplate.convertAndSend(exchangeName, "",message);
            Thread.sleep(20);
    }

💡 这里仍然使用上面的 convertAndSent 的 API

  • 通过指定 routingKey(必须要有,没有的话以空字符串填充)和 exchangeName

  • 这里看一下两个重写方法的区别:

    // 交换机
    rabbitTemplate.convertAndSend(exchangeName, "",message);
    // 队列
    rabbitTemplate.convertAndSend(queueName, message);
    

👉 发送信息,查看效果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.3.2 Direct 交换机

💡 显然只通过广播方式无法满足实际开发的需求

  • Direct 交换机可以根据消息的路由键(Routing Key)将消息直接发送到与之匹配的队列中。
  • Direct 的路由键就是其绑定队列时的绑定键,这与后面提到的 Topic 交换机有区别
  • Direct 交换机通常用于 点对点 的消息传递模式,即一条消息只能被一个消费者处理,当生产者发送一条消息时,Direct 交换机会根据消息的路由键将消息发送到一个特定的队列中,然后只有这个队列的一个消费者能够接收和处理该消息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

👉 按照上图的形式去创建队列

  • 但一般不会采用这种将一个 routing key 去绑定多个队列的场景,这里只是作为演示。

👉 编写 publishertest 方法

    @Test
    void DirectTest() throws InterruptedException {
        String exchangeName = "amq.direct";
        String message = "Hello Everyone!";
        rabbitTemplate.convertAndSend(exchangeName, "blue",message);
        Thread.sleep(20);
    }

👉 编写接收消息的方法,仍然是只需要指定监听的队列

    @RabbitListener(queues = "direct.queue1")
    public void listenerDirectQueue1(String msg) throws InterruptedException {
        log.error("Direct listener1 收到消息{}", msg);
    }

    @RabbitListener(queues = "direct.queue2")
    public void listenerDirectQueue2(String msg) throws InterruptedException {
        log.info("Fanout listener2 收到消息{}", msg);
    }

    @RabbitListener(queues = "direct.queue3")
    public void listenerDirectQueue3(String msg) throws InterruptedException {
        log.error("Fanout listener3 收到消息{}", msg);
    }

👉 发送消息并且测试
在这里插入图片描述

3.3.3 Topic 交换机

💡 Topic 交换机是 RabbitMQ 中的一种高级交换机类型,它允许消息根据路由键(Routing Key)的模式匹配将消息路由到 一个或多个队列 中。Topic 交换机使用 通配符 匹配路由键,使得消息能够更灵活地进行路由和分发。

  • Topic 交换机通常用于 多对多 的消息传递模式,即一条消息可以被多个消费者处理。

  • 可以将 Topic Exchange 看作是对 Direct Exchange 的一种拓展,它的 Routing Key 可以是多个单词,中间以 . 来进行分割,比如 china.newschina.weather,而 Binding Key 支持 通配符

    • # 代表一个或者多个单词
    • * 代表一个单词

    比如将 Binding Key 设置为 china.#Routing Key 为两个中的任意一个都会导航到这个队列

👉 编写 publisher 代码

    @Test
    void TopicTest() throws InterruptedException {
        String exchangeName = "amq.topic";
        String news = "Hello Everyone!";
        String weather = "晴朗";
        rabbitTemplate.convertAndSend(exchangeName, "china.weather", weather);
        rabbitTemplate.convertAndSend(exchangeName, "china.news", news);
        Thread.sleep(20);
    }

👉 编写 consumer 代码

    @RabbitListener(queues = "topic.queue1")
    public void listenerTopicQueue1(String msg) throws InterruptedException {
        log.info("Topic listener1 收到消息{}", msg);
    }

    @RabbitListener(queues = "topic.queue2")
    public void listenerTopicQueue2(String msg) throws InterruptedException {
        log.info("Topic listener2 收到消息{}", msg);
    }

👉 发送消息测试结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.4 声明队列交换机

💡 Spring AMQP 提供了一些 API 去创建新的队列和交换机可以在代码中去创建新的交换机

  • 在生产环境(development environment)中使用的 RabbitMQ 和运行环境中使用的肯定是不同的,所以在部署的时候需要重新设置交换机和队列,而在控制台上创建这些队列就可能会导致很多由于人工出现的错误。
  • 所以可以选择在代码中去创建类和属性,这样可以避免大部分的人工问题。
3.4.1 方式一:书写 Config 类

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

💡 Spring AMQP 提供了几个类用来声明队列、交换机以及其绑定关系

  • Queue 类:用于声明队列,可以使用 QueueBuilder 中的 build() 方法
  • Exchange 类:声明交换机
  • Binding 类:声明队列与交换机的关系

👉 通过将这些 Bean 交给 spring 去管理就可以实现自主的创建。

package com.example.consumer.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FanoutConfiguration {
    @Bean
    public FanoutExchange fanoutExchange() {
//        ExchangeBuilder.fanoutExchange("").build();
        return new FanoutExchange("fanout2");
    }

    @Bean
    public Queue fanout2Queue() {
//        return QueueBuilder.durable("").build();
        return new Queue("fanout2.queue1");
    }

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(fanout2Queue()).to(fanoutExchange());
    }
}
3.4.2 方式二:注解方式创建

👉 代码示例

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "object.queue1", declare = "true"),
            exchange = @Exchange(name = "object", type = ExchangeTypes.DIRECT),
            key = {"object"}
    ))    
	public void listenerObject(Map<String, String> map) {
        System.out.println(map.get("name"));
    }

💡 通过这样的注解就可以声明出队列、交换机和它们之间的 binding

  • 通过 value 指定 queue,后面的 declare 是是否持久化的选项,默认为 true(后面详细讲)

  • 通过 exchange 指定交换机,ExchangeTypes 为枚举类,指定创建的类型

  • 通过 key 来指定 Binding Key,是一个字符串数组,可以指定多个 Binding Key

  • 当使用 @RabbitListener 注解时,如果指定的队列不存在,Spring Boot自动创建该队列。如果指定的交换机不存在,Spring Boot 也会 自动创建该交换机。然后将 队列与交换机进行绑定,以便消息可以正确地路由到指定的队列。

3.5 消息转换器

💡 在发送 对象 的时候 Spring 对消息对象的处理是基于 JDK 的 ObjectOutputStream 来序列化的

  • 这就导致了下面几个问题

    • JDK 序列化有安全风险

    • JDK 序列化后的消息体积很大

    • JDK 序列化的消息可读性很差

👉 所以在处理发送对象的时候使用 JSON 格式来对信息进行序列化,首先引入依赖

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

👉 配置消息转换器到 ApplicationContext 中,即声明 Bean

  • 可以通过写 Config 类的方式实现,这里直接将声明写到主方法中
@SpringBootApplication
public class PublisherApplication {

    public static void main(String[] args) {
        SpringApplication.run(PublisherApplication.class, args);
    }

    @Bean
    public MessageConverter MessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}
@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    @Bean
    public MessageConverter MessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

❗ 注意:在 publisherconsumer 中的序列化以及反序列化的转换器 必须相同

👉 实现对象的传递

    @Test
    void ObjectTest() throws JsonProcessingException {
        Map<String, String> map = new HashMap<>();
        map.put("name", "Jack");
        map.put("age", "18");
        String exchangeName = "object";
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(exchangeName, "object", map);
    }
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "object.queue1", declare = "true"),
            exchange = @Exchange(name = "object", type = ExchangeTypes.DIRECT),
            key = {"object"}
    ))
    public void listenerObject(Map<String, String> map) {
        System.out.println(map.get("name"));
    }

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

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

相关文章

【软件使用】【edge】如何让edge的某个网页作为应用安装

【背景】 有些常用网页希望用双击快捷方式的形式打开更加效率&#xff0c;我的浏览器主要是edge&#xff0c;研究了两种方法来实现这个需求。 【Edge自带方法】 点击Edge的右上角三点水-》应用-》将此站点作为应用安装。 点击安装&#xff0c;可以选择是否加到开始屏幕等。 …

Linux 从日志中抽取信息,批量生成SQL语句并执行

这里写目录标题 一. 需求分析二. 从日志中抽取出指定字段&#xff0c;并切分为若干个子文件三. 生成查询执行计划四. 生成查询的SQL语句五. 检查并执行 一. 需求分析 有如下日志文件&#xff0c;假设日志文件中有10000条数据&#xff0c;要求将全部的TRANSACTIONID抽取出来&am…

vue3 之 商城项目—二级分类

二级分类功能描述 配置二级路由 准备组件模版 <script setup></script><template><div class"container "><!-- 面包屑 --><div class"bread-container"><el-breadcrumb separator">"><el-bre…

python coding with ChatGPT 打卡第19天| 二叉树:合并二叉树

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 python coding with ChatGPT 打卡第15天| 二叉树&#xff1a;翻转…

在虚拟机上完成Centos安装

Linux学习和使用 前言如何安装Centos初始化操作 使用VMware备份操作系统快照克隆 内容总结参考链接 本人介绍:2023年全国大学生数学建模竞赛国家二等奖,2022年蓝桥杯省二等奖,这里是一个和你一起不断努力,不断前进的程序猿一枚 前言 简单介绍一下本片文章将会讲到的内容:本章节…

关于创建vue项目报错command failed: npm install --loglevel error

一、首先 在这个目录下有个文件叫.vuerc 二、其次 进去之后把里面的"useTaobaoRegistry": false,修改下&#xff0c;我之前是true&#xff0c;后来改成了false才成功。

【大厂AI课学习笔记】【1.6 人工智能基础知识】(1)人工智能、机器学习、深度学习之间的关系

6.1 人工智能、机器学习与深度学习的关系 必须要掌握的内容&#xff1a; 如上图&#xff1a;人工智能>机器学习>深度学习。 机器学习是人工智能的一个分支&#xff0c;该领域的主要研究对象是人工智能&#xff0c;特别是如何在经验学习中改进具体算法的性能。 深度学习…

【MySQL进阶之路】生产案例:数据库无法连接,Too many connections

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

【leetcode】965. 单值二叉树

题目链接 965. 单值二叉树 bool isUnivalTree(struct TreeNode* root) {// if (root->left ! NULL && root->right ! NULL) {// return root->val root->left->val// && root->val root->right->val// && isUnivalTr…

算法学习——LeetCode力扣二叉树篇3

算法学习——LeetCode力扣二叉树篇3 116. 填充每个节点的下一个右侧节点指针 116. 填充每个节点的下一个右侧节点指针 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树…

【开源】JAVA+Vue.js实现衣物搭配系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 衣物档案模块2.2 衣物搭配模块2.3 衣物收藏模块 三、系统设计3.1 用例设计3.2 E-R图设计3.3 数据库设计3.3.1 衣物档案表3.3.2 衣物搭配表3.3.3 衣物收藏表 四、系统实现4.1 登录页4.2 衣物档案模块4.3 衣物搭配模块4.4…

C语言每日一题(54)对称二叉树

力扣网 101 对称二叉树 题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false提…

国际物流数字化运输方式选择指南 | 箱讯科技

国际物流涉及多种运输方式&#xff0c;每种方式都有其独特的优势和适用场景。选择合适的运输方式对于确保货物安全、及时到达目的地并控制成本至关重要。以下是对六种主要国际运输方式的简要介绍和选择建议&#xff1a; 国际快递&#xff1a;适用于小件、高价值或急需的货物。…

游戏服务器租用价格表_TOP3费用对比

游戏服务器租用多少钱一年&#xff1f;1个月游戏服务器费用多少&#xff1f;阿里云游戏服务器26元1个月、腾讯云游戏服务器32元&#xff0c;华为云26元&#xff0c;游戏服务器配置从4核16G、4核32G、8核32G、16核64G等配置可选&#xff0c;游戏专业服务器公网带宽10M、12M、15M…

python+django人力资源管理系统7w5x3

技术栈 后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django Python版本&#xff1a;python3.7 数据库&#xff1a;mysql5.7 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm .设计框架&#xff1a;Vue 1. 表现层&#xff1a;写多…

微软 CMU - Tag-LLM:将通用大语言模型改用于专业领域

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 论文地址&#xff1a;https://arxiv.org/abs/2402.05140 Github 地址&#xff1a;https://github.com/sjunhongshen/Tag-LLM 大语言模型&#xff08…

vue3初识

目录 一、前言二、主观感受三、vue3初探 原文以及该系列教程文章后续可点击这里查看&#xff1a;vue初识 一、前言 Vue.js是一款流行的前端框架&#xff0c;最初由尤雨溪&#xff08;Evan You&#xff09;于2014年创建&#xff0c;非常的年轻。官网为vue3&#xff0c; 但要注…

Java集合 Collection接口

这里写目录标题 集合Collection接口创建一个性表增加元素删除元素修改元素判断元素遍历集合实例判断元素是否存在 集合 Java中的Collection接口是集合类的一个顶级接口&#xff0c;它定义了一些基本的操作&#xff0c;如添加、删除、查找等。Collection接口主要有以下几个常用…

【数据结构与算法】【小白也能学的数据结构与算法】迭代算法专题

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

算法之双指针系列1

目录 一&#xff1a;双指针的介绍 1&#xff1a;快慢指针 2&#xff1a;对撞指针 二&#xff1a;对撞指针例题讲述 一&#xff1a;双指针的介绍 在做题中常用两种指针&#xff0c;分别为对撞指针与快慢指针。 1&#xff1a;快慢指针 简称为龟兔赛跑算法&#xff0c;它的基…