【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 {@ResourceRabbitTemplate rabbitTemplate;@Testvoid 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 发送消息

    @Testvoid 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.3port: 5672virtual-host: /myHost # 主机名username: rootpassword: 123456listener: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 代码

    @Testvoid 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 方法

    @Testvoid 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 代码

    @Testvoid 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 {@Beanpublic FanoutExchange fanoutExchange() {
//        ExchangeBuilder.fanoutExchange("").build();return new FanoutExchange("fanout2");}@Beanpublic Queue fanout2Queue() {
//        return QueueBuilder.durable("").build();return new Queue("fanout2.queue1");}@Beanpublic 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);}@Beanpublic MessageConverter MessageConverter() {return new Jackson2JsonMessageConverter();}
}
@SpringBootApplication
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}@Beanpublic MessageConverter MessageConverter() {return new Jackson2JsonMessageConverter();}
}

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

👉 实现对象的传递

    @Testvoid 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"));}

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

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

相关文章

webgis后端安卓系统部署攻略

目录 前言 一、将后端项目编译ARM64 二、安卓手机安装termux 1.更换为国内源 2.安装ssh远程访问 3.安装文件远程访问 三、安装postgis数据库 1.安装数据库 2.数据库配置 3.数据导入 四、后端项目部署 五、自启动设置 总结 前言 因为之前一直做的H5APP开发&#xf…

LLM之RAG实战(二十五)| 使用LlamaIndex和BM25重排序实践

本文&#xff0c;我们将研究高级RAG方法的中的重排序优化方法以及其与普通RAG相比的关键差异。 一、什么是RAG&#xff1f; 检索增强生成&#xff08;RAG&#xff09;是一种复杂的自然语言处理方法&#xff0c;它包括两个不同的步骤&#xff1a;信息检索和生成语言建模。这种方…

爬虫2—用爬虫爬取壁纸(想爬多少张爬多少张)

先看效果图&#xff1a; 我这个是爬了三页的壁纸60张。 上代码了。 import requests import re import os from bs4 import BeautifulSoupcount0 img_path "./壁纸图片/"#指定保存地址 if not os.path.exists(img_path):os.mkdir(img_path) headers{ "User-Ag…

【九章斩题录】Leetcode:判定是否互为字符重排(C/C++)

面试题 01.02. 判定是否互为字符重排 ✅ 模板&#xff1a;C class Solution { public:bool CheckPermutation(string s1, string s2) {} }; 「 法一 」排序 &#x1f4a1; 思路&#xff1a;看到题目中说 "重新排列后能否变成另一个字符串"&#xff0c;等等……重新…

如何升级 gpt4?快速升级至ChatGPT Plus指南,爆火的“ChatGPT”到底是什么?

提到 ChatGPT。想必大家都有所耳闻。自从 2022 年上线以来&#xff0c;就受到国内外狂热的追捧和青睐&#xff0c;上线2个月&#xff0c;月活突破1个亿&#xff01;&#xff01;&#xff01; 而且还在持续上涨中。因为有很多人都在使用 ChatGPT 。无论是各大头条、抖音等 App、…

UUID算法:独一无二的标识符解决方案

引言 在分布式系统和大数据环境下&#xff0c;唯一标识符的生成和管理是一项关键任务。UUID&#xff08;Universally Unique Identifier&#xff09;算法应运而生&#xff0c;成为了解决重复数据和标识符冲突的有效工具。本文将探讨UUID算法的优势和劣势&#xff0c;分析其在分…

【Spring】

目录 前言 1.Spring框架中的单例bean是线程安全的吗? 2.什么是AOP? 3.你们项目中有没有使用到AOP&#xff1f; 4.Spring中的事务是如何实现的&#xff1f; 5.Spring中事务失效的场景有哪些&#xff1f; 6.Spring的bean的生命周期。 7.Spring中的循环引用 8.构造方法…

休斯顿NASA太空机器人进入最后测试阶段,或可模拟人类执行外星任务!

美国宇航局开发研制的太空智能机器人目前正在德州休斯顿的约翰逊航天中心接受最后的运行测试&#xff0c;距离太空智能化时代又要更进一步了&#xff01; NASA表示&#xff0c;日前在德州休斯顿附近的约翰逊航天中心进行测试的机器人名为Valkyrie&#xff0c;是以北欧神话中的一…

Postgresql 的编译安装与包管理安装, 全发行版 Linux 通用

博客原文 文章目录 实验环境信息编译安装获取安装包环境依赖编译安装安装 contrib 下工具代码 创建用户创建数据目录设置开机自启动启动数据库常用运维操作 apt 安装更新源安装 postgresql开机自启修改配置修改密码 实验环境信息 Ubuntu 20.04Postgre 16.1 编译安装 获取安装…

.NET命令行(CLI)常用命令

本文用于记录了.NET软件开发全生命周期各阶段常用的一些CLI命令&#xff0c;用于开发速查。 .NET命令行&#xff08;CLI&#xff09;常用命令 项目创建&#xff08;1&#xff09;查看本机SDK&#xff08;2&#xff09;查看本机可以使用的.NET版本&#xff08;3&#xff09;生成…

159基于matlab的基于密度的噪声应用空间聚类(DBSCAN)算法对点进行聚类

基于matlab的基于密度的噪声应用空间聚类(DBSCAN)算法对点进行聚类&#xff0c;聚类结果效果好&#xff0c;DBSCAN不要求我们指定集群的数量&#xff0c;避免了异常值&#xff0c;并且在任意形状和大小的集群中工作得非常好。它没有质心&#xff0c;聚类簇是通过将相邻的点连接…

Android:内存泄漏检查内存优化

3.17Android优化 手机移动设备的内存是有限的,需要避免内存泄漏,优化内存使用。 1.java中四种引用类型 强引用、软引用、弱引用、虚引用。 强引用:使用类构造方法,创建对象,当内存超出了,也不会释放对象所占内存空间; String str = new String(‘1223’); 切断引用str=…

代码随想录算法训练营第44天 | 完全背包理论基础 518.零钱兑换II 377.组合总和 Ⅳ

完全背包理论基础 完全背包与01背包只相差在物品是无限取用的。因此和01背包相比第二层对背包容量的遍历应该是正序的&#xff0c;而且正因为这个正序&#xff0c;使得在纯完全背包问题中&#xff0c;背包容量和物品的遍历是可以倒过来的。 #include <bits/stdc.h> usi…

网络的基本概念和socket编程

网络的基本概念 1.协议1.1 协议的基本概念1.2 常见的协议 2.分层模型2.1网络七层OSI 7层模型&#xff1a;物数网传会表应(口诀)2.2TCP/IP模型2.3数据通信的过程2.4网络的设计模式2.5以太网帧的格式 3.SOCKET编程3.1网络字节序3.2 相关结构体和函数3.3 代码实现 1.协议 1.1 协议…

前端JavaScript篇之对象继承的方式有哪些?

目录 对象继承的方式有哪些&#xff1f;1. 原型链继承2. 借用构造函数3. 组合继承4. 原型式继承5. 寄生式组合继承 对象继承的方式有哪些&#xff1f; 1. 原型链继承 当使用原型链继承时&#xff0c;子类型的原型对象被设置为父类型的一个实例。这意味着子类型通过其原型可以…

VUE学习——数组变化侦测

官方文档 变更方法&#xff1a; 使用之后&#xff0c;ui可以直接发生改变。改变原数组 替换数组&#xff1a; 使用之后需要接受重新赋值&#xff0c;不然ui不发生改变。不改变原数组

第70讲axios后端请求工具类封装

axios工具类封装&#xff1a; // 引入axios import axios from axios;// 创建axios实例 const httpService axios.create({// url前缀-http:xxx.xxx// baseURL: process.env.BASE_API, // 需自定义baseURL:http://localhost:80/,// 请求超时时间timeout: 3000 // 需自定义 })…

嵌入式系统:挑战与机遇并存的领域

嵌入式系统&#xff1a;挑战与机遇并存的领域嵌入式系统是一个既具有挑战性又充满前景的领域。要成为一名合格的嵌入式系统工程师&#xff0c;需要经过大量的学习和实践。然而&#xff0c;进入这个领域时&#xff0c;刚入行可能会面临许多困境。让我们一起探讨一下嵌入式系统工…

Python爬虫实战 | 京东平台电商API接口采集京东商品京东工业商品详情数据

item_get-获得JD商品详情API测试 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cac…

【Maven】依赖、构建管理 继承与聚合 快速学习(3.6.3 )

文章目录 Maven是什么&#xff1f;一、Maven安装和配置本地配置文件设置idea配置本地maven 二、基于IDEA的Maven工程创建2.1 Maven工程GAVP属性2.2 Idea构建Maven JavaEE工程 三、Maven工程项目结构说明四、Maven核心功能依赖和构建管理4.1 依赖管理和配置4.2 依赖传递和冲突4.…