服务器的异步通信——RabbitMQ

news2024/10/5 10:17:44

目录

一、同步通信 VS 异步通信

二、MQ——消息队列

RabbitMQ 

RabbitMQ安装 

RabbitMQ的整体架构

常见消息模型 

 基本消息队列(BasicQueue)

工作消息队列(WorkQueue)

 发布、订阅(Publish、Subscribe)

 Fanout Exchange

Direct Exchange 

Topic Exchange 

SpringAMQP-消息转换器 


一、同步通信 VS 异步通信

同步通信:双方在同一个时钟信号的控制下,进行数据的接收和发送,来一个时钟,发送端发送,接收端接收,他们彼此之间的工作状态是一致的,例如直播、打电话。

优点:

  • 时效性强,能够立即得到结果

缺点:

  • 耦合性较高:每次加入新的需求,都需要修改原有代码
  • 性能下降:调用者需要等待服务提供者响应,若调用链过长则响应时间等于每次调用时间之和
  • 资源利用率低:调用链中的每个服务在等待响应的过程中,不能释放请求占用的资源,高并发的情况下会造成资源的极度浪费
  • 级联失败:如果服务提供者出现问题,所有的调用方也会跟着出问题

适用场景:业务要求时效性高

异步通信:异步通信在发送字符时,所发送的字符之间的时间间隔可以是任意的。例如微信聊天。

在异步调用过程常见的实现就是事件驱动模式,系统中发生的事件会触发相应的事件处理器或监听器
,从而实现特定的业务逻辑或功能。

例如在如下的支付场景中,当有请求发送给支付服务时,支付服务就会通知Broker,接着后续的订阅事件就会接收到请求,开始同时处理业务,但是支付服务不用等到后续订阅事件完成后再返回,而是将请求通知给Broker之后支付服务就会返回结果。

优点:

  • 服务解耦
  • 性能提升,吞吐量提高
  • 服务之间没有强依赖,不用担心级联失败问题(故障隔离)
  • 流量削峰

缺点:

  • 依赖于Broker的可靠性、安全性和吞吐能力
  • 结构复杂后,业务没有了明显的流水线,难以追踪管理

适用场景:对于并发和吞吐量的要求高,时效性的要求低

二、MQ——消息队列

MQ(消息队列):存放消息的队列,也是事件驱动架构的Broker。

常见的消息队列实现对比:

RabbitMQ 

RabbitMQ是基于Erlang语言开发的消息通信中间件,RabbitMQ的性能以及可用性较好,国内应用较为广泛,所以对RabbitMQ进行重点学习。

RabbitMQ的官网地址:https://www.rabbitmq.com

RabbitMQ安装 

可以根据自己的需求在RabbitMQ的官网进行查看:下载和安装 RabbitMQ — 兔子MQ

RabbitMQ的整体架构

​ 首先,Publisher会把消息发送给exchange(交换机),exchange负责路由再把消息投递到queue(队列),queue负责暂存消息,Consumer会从队列中获取消息并处理消息。

RabbitMQ中的几个概念:

channel :操作 MQ 的工具
exchange :路由消息到队列中
queue :缓存消息
virtual host :虚拟主机,是对 queue exchange 等资源的逻辑分组

常见消息模型 

RabbitMQ的官方文档中给出了5个MQ的Demo实例,可以分为如下:

  • 基本消息队列(BasicQueue)
  • 工作消息队列(WorkQueue)
  • 发布订阅(Publish、Subscribe),又根据交换机类型不同分为三种:

                Fanout Exchange:广播

                Direct Exchange:路由

                Topic Exchange:主题

 基本消息队列(BasicQueue)

官方的HelloWorld是基于最基础的消息队列模型来实现的,只包括三个角色:

  • publisher:消息发布者,将消息发送到队列queue
  • queue:消息队列,负责接受并缓存消息
  • consumer:订阅队列,处理队列中的消息

 
在RabbitMQ中需要了解的端口:

在使用端口时,需要在云服务器上开放所用的端口 

基本消息队列的消息发送流程:

  1. 建立Connection
  2. 创建Channel
  3. 利用Channel声明队列
  4. 利用Channel向队列中发送消息

代码实现:

public class PublisherTest {
    @Test
    public void testSendMessage() throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("x.x.x.x");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("xx");
        factory.setPassword("xx");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.发送消息
        String message = "hello, rabbitmq!";
        channel.basicPublish("", queueName, null, message.getBytes());
        System.out.println("发送消息成功:【" + message + "】");

        // 5.关闭通道和连接
        channel.close();
        connection.close();

    }
}

运行结果:

基本消息队列的消息接收流程: 

  1. 建立Connection
  2. 创建Channel
  3. 利用Channel声明队列
  4. 定义Consumer的消费行为handleDelivery()
  5. 利用Channel将消费者与队列进行绑定

代码实现:

public class ConsumerTest {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("x.x.x.x");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("xx");
        factory.setPassword("xx");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.订阅消息
        channel.basicConsume(queueName, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 5.处理消息
                String message = new String(body);
                System.out.println("接收到消息:【" + message + "】");
            }
        });
        System.out.println("等待接收消息。。。。");
    }
}

运行结果:

上述实现方式相对比较复杂,就引入了SpringAMQP来实现。

AMQP:是用于在应用程序之间传递业务消息的开放标准。该协议与语言和平台无关,更符合微服务中独立性的要求。 

SpringAMQP:SpringAMQP是基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息。包含两部分,其中spring-amqp是基础抽象,spring-rabbit是底层的默认实现。

SpringAMQP的官方地址

那么利用SpringAMQP来实现基本消息队列的流程如下:

  1. 在父工程中引入spring-amqp的依赖
  2. 在publisher服务中利用RabbitTemplate发送消息到simple.queue这个队列
  3. 在consumer服务中编写消费逻辑,绑定simple.queue这个队列

具体实现:

1、在父工程中引入spring-amqp的依赖:

        <!--AMQP依赖,包含RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2、在publisher中编写测试方法,向simple.queue发送消息:

在publisher服务的配置文件中添加mq的连接信息:

spring:
  rabbitmq:
    host:  # rabbitMQ的ip地址
    port: 5672 # 端口
    username: # 用户名
    password: # 密码
    virtual-host: # 虚拟主机

在publisher服务中新建一个测试类,编写测试方法:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSendMessage2SimpleQueue() {
        String queueName = "simple.queue";
        String message = "hello, spring amqp!";
        rabbitTemplate.convertAndSend(queueName, message);
    }
}

在RabbitMQ中的simple队列中查询信息:

3、在consumer服务中编写消费逻辑,监听simple.queue

在consumer服务的配置文件中添加mq连接信息:

spring:
  rabbitmq:
    host:  # rabbitMQ的ip地址
    port: 5672 # 端口
    username: # 用户名
    password: # 密码
    virtual-host: # 虚拟主机

在consumer服务中新建一个类,编写具体的消费逻辑:

@Component
public class SpringRabbitListener { 
   @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueue(String msg) throws InterruptedException {
        System.out.println("消费者接收到消息:【" + msg + "】" + LocalTime.now());
        Thread.sleep(20);
    }
}

运行启动类:

工作消息队列(WorkQueue)

下面场景中如果queue中有50条请求消息,但是consumer1只能处理40条,剩余的10条就可以由consumer进行处理,所以说工作消息队列可以提高消息的处理速度,避免队列消息堆积

模拟Workqueue,实现一个队列绑定多个消费者,基本实现思路如下:

  1. 在publisher服务中定义测试方法,每秒产生50条消息,发送到simple.queue中
  2. 在consumer服务中定义两个消息监听者,都监听simple.queue队列
  3. 消费者1每秒处理50条消息,消费者2每秒处理10条消息

代码实现:

在publisher服务中定义测试方法,每秒产生50条消息,发送到simple.queue中

    public void testSendMessage2WorkQueue() throws InterruptedException {
        String queueName = "simple.queue";
        String message = "hello, message__";
        for (int i = 1; i <= 50; i++) {
            rabbitTemplate.convertAndSend(queueName, message + i);
            Thread.sleep(20);
        }
    }
在consumer服务中定义两个消息监听者,都监听simple.queue队列,设置消费者1每秒处理50条消息,消费者2每秒处理10条消息
    @RabbitListener(queues = "simple.queue")
    public void listenWorkQueue1(String msg) throws InterruptedException {
        System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
        Thread.sleep(20);
    }

    @RabbitListener(queues = "simple.queue")
    public void listenWorkQueue2(String msg) throws InterruptedException {
        System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
        Thread.sleep(200);
    }

修改application.yml文件,设置preFetch这个值,可以控制预取消息的上限,确保消费者2取消息时只能取一条,提高效率(“能者多劳”):

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1

运行结果:

 发布、订阅(Publish、Subscribe)

发布订阅模式与之前案例的区别就是允许将同一消息发送给多个消费者。实现方式是加入了exchange(交换机)。

常见exchange类型包括:

  • Fanout:广播
  • Direct:路由
  • Topic:话题

exchange负责消息路由,而不是存储,路由失败则消息丢失 

 Fanout Exchange

 Fanout Exchange会将接收到的消息路由到每一个跟其绑定的queue中,如下:

基本实现思路如下:

  1. 在consumer中,利用代码声明队列、交换机,将二者进行绑定
  2. 在consumer中,编写两个消费方法,分别监听fanout.queue1和fanout.queue2
  3. 在publisher中编写测试方法,向fanout发送消息

代码实现:

在consumer中,利用代码声明队列、交换机,将二者进行绑定

@Configuration
public class FanoutConfig {
    // itcast.fanout
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("itcast.fanout");
    }

    // fanout.queue1
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }

    // 绑定队列1到交换机
    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    // fanout.queue2
    @Bean
    public Queue fanoutQueue2(){
        return new Queue("fanout.queue2");
    }

    // 绑定队列2到交换机
    @Bean
    public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}

在consumer中,编写两个消费方法,分别监听fanout.queue1和fanout.queue2

    @RabbitListener(queues = "fanout.queue1")
    public void listenFanoutQueue1(String msg) {
        System.out.println("消费者接收到fanout.queue1的消息:【" + msg + "】");
    }
    @RabbitListener(queues = "fanout.queue2")
    public void listenFanoutQueue2(String msg) {
        System.out.println("消费者接收到fanout.queue2的消息:【" + msg + "】");
    }

在publisher中编写测试方法,向fanout发送消息

    @Test
    public void testSendFanoutExchange() {
        // 交换机名称
        String exchangeName = "itcast.fanout";
        // 消息
        String message = "hello, every one!";
        // 发送消息
        rabbitTemplate.convertAndSend(exchangeName, "", message);
    }

运行结果:

Direct Exchange 

Direct Exchange会将接收到的消息根据规则路由到指定的Queue,因此被称为路由模式

  • l每一个Queue都与Exchange设置一个BindingKey
  • l发布者发送消息时,指定消息的RoutingKey
  • lExchange将消息路由到BindingKey与消息RoutingKey一致的队列

基本实现思路如下:

  1. 利用@RabbitListener声明ExchangeQueueRoutingKey
  2. consumer服务中,编写两个消费者方法,分别监听direct.queue1direct.queue2
  3. publisher中编写测试方法,向itcast. direct发送消息

代码实现:

在consumer服务中,编写两个消费者方法,分别监听direct.queue1direct.queue2,并利用@RabbitListener声明ExchangeQueueRoutingKey

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
            key = {"red", "blue"}
    ))
    public void listenDirectQueue1(String msg){
        System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
            key = {"red", "yellow"}
    ))
    public void listenDirectQueue2(String msg){
        System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
    }

在publisher服务发送消息到DirectExchange

    @Test
    public void testSendDirectExchange() {
        // 交换机名称
        String exchangeName = "itcast.direct";
        // 消息
        String message = "hello, red!";
        // 发送消息
        rabbitTemplate.convertAndSend(exchangeName, "red", message);
    }

运行结果:

Topic Exchange 

Topic Exchange与Direct Exchange类似,区别在于Topic Exchange的routingKey必须是多个单词的列表,并且以.分割

QueueExchange指定BindingKey时可以使用通配符:

#:代指0个或多个单词

*:代指一个单词

基本实现思路如下:

  1. 利用@RabbitListener声明ExchangeQueueRoutingKey
  2. 在consumer服务中,编写两个消费者方法,分别监听topic.queue1topic.queue2
  3. 在publisher中编写测试方法,向itcast. topic发送消息

代码实现:

利用@RabbitListener声明ExchangeQueueRoutingKey,在consumer服务中,编写两个消费者方法,分别监听topic.queue1topic.queue2

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue1"),
            exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
            key = "china.#"
    ))
    public void listenTopicQueue1(String msg){
        System.out.println("消费者接收到topic.queue1的消息:【" + msg + "】");
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue2"),
            exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
            key = "#.news"
    ))
    public void listenTopicQueue2(String msg){
        System.out.println("消费者接收到topic.queue2的消息:【" + msg + "】");
    }

在publisher中编写测试方法,向itcast. topic发送消息

    @Test
    public void testSendTopicExchange() {
        // 交换机名称
        String exchangeName = "itcast.topic";
        // 消息
        String message = "今天天气不错,我的心情好极了!";
        // 发送消息
        rabbitTemplate.convertAndSend(exchangeName, "china.weather", message);
    }

运行结果:

SpringAMQP-消息转换器 

SpringAMQP的发送方法中,接收消息的类型是Object,也就是说我们可以发送任意对象类型的消息,SpringAMQP会帮我们序列化为字节后发送。

Spring的对消息对象的处理是由org.springframework.amqp.support.converter.MessageConverter来处理的。而默认实现是SimpleMessageConverter,基于JDKObjectOutputStream完成序列化。

如果要修改只需要定义一个MessageConverter 类型的Bean即可。

推荐用JSON方式序列化,实现步骤如下:

在父工程中引入依赖

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

在publisher和consumer服务中声明MessageConverter:

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

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

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

相关文章

【CANoe使用大全】——工程新建

文章目录 1、硬件连接2、通道配置2.1通道协议选择2.2映射通道配置2.3.波特率采样点配置 1、硬件连接 前提条件&#xff1a;软件、驱动均已经安装完成 硬件通过UBS接入电脑&#xff0c;Status状态灯为黄色闪烁状态说明硬件设备与电脑连接正常 2、通道配置 2.1通道协议选择 …

架构师的36项修炼-01大型架构演进之路

本课时的主题是大型互联网系统架构的演进之路&#xff0c;主要包含三部分内容。 第一部分是大型互联网系统的特点&#xff0c;分析大型互联网有哪些特点和挑战&#xff0c;它们是现在一些技术和架构方案产生的原因。 第二部分是系统处理能力提升的两种途径&#xff0c;提供了…

Elasticsearch:使用 Gemini、Langchain 和 Elasticsearch 进行问答

本教程演示如何使用 Gemini API创建 embeddings 并将其存储在 Elasticsearch 中。 我们将学习如何将 Gemini 连接到 Elasticsearch 中存储的私有数据&#xff0c;并使用 Langchian 构建问答功能。 准备 Elasticsearch 及 Kibana 如果你还没有安装好自己的 Elasticsearch 及 Ki…

C#winform上位机开发学习笔记6-串口助手的断帧功能添加

1.功能描述 按照设定时间对接收数据进行断帧(换行) 应用于需要接收完整数据包的场景&#xff0c;例如下位机发送一包数据为1秒&#xff0c;每100ms发送一组数据 大部分用于接收十六进制数据时 2.代码部分 步骤1&#xff1a;添加计时器&#xff0c;设置默认时间为500ms 步骤…

【多商户开源-BSD- Fecmall 电商平台】

关于Fecmall Fecmall 关于&#xff0c;Fecmall介绍 Fecbbc开源BSD多商户系统&#xff0c;真正开源&#xff0c;商用免费授权的多商户系统 Fecmall系统简介&#xff1a; 全称为Fancy ECommerce Shop&#xff0c; 着重于电商架构的研发优化&#xff0c;全新定义商城的架构体系&…

react18介绍

改进已有属性&#xff0c;如自动批量处理【setState】、改进Suspense、组件返回undefined不再报错等 支持Concurrent模式&#xff0c;带来新的API&#xff0c;如useTransition、useDeferredValue等 如何升级React 18 npm install reactlatest react-domlatestnpm install ty…

学会使用ubuntu——ubuntu22.04使用WebCatlog

Ubuntu22.04使用WebCatlog WebCatlog是适用于Gnu / Linux&#xff0c;Windows或Mac OS X系统的桌面程序。 引擎基于铬&#xff0c;它用于在我们的桌面上处理Web服务。简单点就是把网页单独一个窗口出来显示&#xff0c;当一个app用。本文就是利用WebCatlog安装后的notion编写的…

技术驱动宠物健康:宠物在线问诊系统的高效搭建手册

在数字化时代&#xff0c;技术正在催生出许多创新的医疗服务&#xff0c;而宠物在线问诊系统便是其中一项引领潮流的创举。本文将为你提供一份高效搭建宠物在线问诊系统的手册&#xff0c;通过技术代码示例&#xff0c;让你轻松打造一套技术驱动的宠物健康管理系统。 1. 架构…

中仕教育:事业编招考全流程介绍

一、报名阶段 1. 了解查看招聘信息&#xff1a;查看各类事业编岗位的招聘信息&#xff0c;包括岗位职责、招聘条件、报名时间等。 2. 填写报名表&#xff1a;按照要求填写报名表&#xff0c;包括个人信息、教育背景、工作经历等内容。 3. 提交报名材料&#xff1a;将报名表及…

探索文件与交互:使用PyQt5构建一个高级文件选择器

在当今的应用程序开发中&#xff0c;文件管理和交互是一个重要的组成部分。特别是对于桌面应用程序&#xff0c;提供一个直观、功能丰富的文件选择器是提高用户体验的关键。 本篇博客&#xff0c;我将介绍如何使用Python和PyQt5来构建一个高级的文件选择器&#xff0c;它不仅能…

cad二次开发autolisp(二)

目录 一、选择集1.1 选择集的创建1.2 选择集的编辑1.3 操作选择集 二、命令行设置对话框2.1 设置图层2.2 加载线型2.3 设置字体样式2.4 设置标注样式&#xff08;了解即可&#xff09; 三、符号表3.1 简介3.2 符号表查找3.2 符号表删改增 一、选择集 定义&#xff1a;批量选择…

Springboot 项目启动成功,但是访问接口404

搭建了一个子项目运行&#xff0c;启动没有任何问题&#xff0c;但是访问restful接口404&#xff0c;查阅网上是启动类与包路径问题&#xff0c;刚开始按照网上说的是扫描不到&#xff0c;但是检测我创建的路径并没有问题&#xff0c;具体怎么确定是启动时在扫描创建beanDefini…

Java框架篇面试题

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于java面试题系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基…

需求分析的结构化和面向对象方法

需求分析的方法&#xff0c;主要有结构化&#xff08;SA&#xff09;方法、面向对象分析&#xff08;OOA&#xff09;方法。另外有面向问题域的分析(Problem Domain Oriented Analysis&#xff0c;PDOA)方法&#xff0c;还有一些形式化方法例如VDM ( Vienna Design Method)和Z等…

银行数据仓库体系实践(4)--数据抽取和加载

1、ETL和ELT ETL是Extract、Transfrom、Load即抽取、转换、加载三个英文单词首字母的集合&#xff1a; E&#xff1a;抽取&#xff0c;从源系统(Souce)获取数据&#xff1b; T&#xff1a;转换&#xff0c;将源系统获取的数据进行处理加工&#xff0c;比如数据格式转化、数据精…

AP5191 降压恒流 双灯 12V5A 一切一LED车灯汽车大灯驱动方案

AP5191是一款PWM工作模式,高效率、外围简 单、内置功率MOS管&#xff0c;适用于4.5-150V输入的高 精度降压LED恒流驱动芯片。输出功率150W&#xff0c; 电流6A。 AP5191可实现线性调光和PWM调光&#xff0c;线性调 光脚有效电压范围0.55-2.6V. AP5191 工作频率可以通过RT 外部…

Programming Abstractions in C阅读笔记:p248-p253

《Programming Abstractions in C》学习第69天&#xff0c;p248-p253总结&#xff0c;总计6页。 一、技术总结 “A generalized program for two-player games”如标题所示&#xff0c;该小节强调要学会从一个复杂的程序中抽象出通用的内容——这也是本书的主旨——“Program…

C语言进阶——数据结构之链表

前言 hello&#xff0c;大家好呀&#xff0c;我是Humble 在之前的两篇博客&#xff0c;我们学完了数据结构中的顺序表&#xff0c;还对它进行了一个应用&#xff0c;做了一个通讯录的小项目 那今天我们再来学习一个新的数据结构——链表 引入 我们来回忆一下顺序表 对于顺…

工业智能网关储能物联网应用实现能源的高效利用及远程管理

储能电力物联网是指利用物联网技术和储能技术相结合&#xff0c;实现对电力系统中各种储能设备的智能管理和优化控制。随着可再生能源的不断发展和应用&#xff0c;电力系统面临着越来越大的电力调度和储能需求而储能电力物联网的出现可以有效解决这一问题&#xff0c;提高电力…

领势 跨越|美创科技数据安全管理平台2023年度回顾

领势 跨越 数据安全管理平台的2023 以数字化安全平台为基础的数据安全体系是未来安全建设的重要方向。 研究咨询机构IDC指出&#xff1a;未来&#xff0c;数据安全基础设施管理平台将逐步发展成为各组织数据安全建设的基础设施。 Gartner《中国数据安全治理解析》预测&…