RabbitMq学习(第一天)

文章目录

  • 1、mq(消息队列)概述
  • 2、RabbitMQ环境搭建
  • 3、java基于AMQP协议操作RabbitMQ
  • 4、基于Spring AMQP操作RabbitMQ
  • 5、代码中创建队列与交换机
    • ①、配置类创建
    • ②、基于@RabbitListener注解创建
  • 6、RabbitMQ详解
    • ①、work模型
    • ②、交换机
      • 1、Fanout(广播)交换机
      • 2、Direct(定向)交换机
      • 3、Topic(话题)交换机
  • 7、消息转换器
  • 总结

1、mq(消息队列)概述

MQ 是 Message Queue(消息队列)的简称,是一种用于异步通信和解耦的中间件技术。它的核心功能是通过队列结构存储和传输消息,允许生产者(发送消息的一方)和消费者(接收消息的一方)在不同时间进行数据交换,而无需直接连接

MQ作用:

①、异步调用:

异步调用方式其实就是基于消息通知的方式,一般包含三个角色:

  • 消息发送者:投递消息的人,就是原来的调用方
  • 消息代理:管理、管理存储、转发消息的中间件
  • 消息接收者:接收和处理消息的人,就是原来的服务提供方

在这里插入图片描述

优点

  • 异步调用,无需等待,性能好
  • 故障隔离,下游服务故障不影响上游业务

缺点

  • 不能立即得到调用结果,时效性差
  • 确定下游业务执行是否成功
  • 业务安全依赖于Broker的可靠性

②、削峰/降流
在电子商务一些秒杀、促销活动中,合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。如下图所示:
在这里插入图片描述

③、降低系统耦合性

对于发送方来说,只需要将自己的消息发送到消息队列就ok了,而对于接收方来说,只需要接收消息即可,而无需关注谁发的,极大降低了发送接收方的耦合性。

④、顺序保证

消息队列保证数据按照特定的顺序被处理,适用于那些对数据顺序有严格要求的场景。大部分消息队列,例如 RocketMQ、RabbitMQ、Pulsar、Kafka,都支持顺序消息。

⑤、延时/定时处理
消息发送后不会立即被消费,而是指定一个时间,到时间后再消费。大部分消息队列,例如 RocketMQ、RabbitMQ、Pulsar、Kafka,都支持定时/延时消息。

⑥、即时通讯

MQTT(消息队列遥测传输协议)是一种轻量级的通讯协议,采用发布/订阅模式,非常适合于物联网(IoT)等需要在低带宽、高延迟或不可靠网络环境下工作的应用。它支持即时消息传递,即使在网络条件较差的情况下也能保持通信的稳定性。RabbitMQ 内置了 MQTT 插件用于实现 MQTT 功能(默认不启用,需要手动开启)

四大mq产品对比:

在这里插入图片描述

2、RabbitMQ环境搭建

我们在docker环境下通过docker pull来快速获取RabbitMQ的镜像。

拉取:

docker pull rabbitmq:3.8-management

运行:

docker run -e RABBITMQ_DEFAULT_USER=root -e RABBITMQ_DEFAULT_PASS=123456 -v mq-plugins:/plugins  --name mq --hostname mq -p 15672:15672 -p 5672:5672 -d rabbitmq:3.8-management

其中,15672端口是图形化界面的端口,而5672是发送接收消息的端口。

在这里插入图片描述

登录之后,界面如下:

在这里插入图片描述

  • publisher: 消息发送者

  • consumer: 消息的消费者

  • queue: 队列,存储消息

  • exchange: 交换机,负责路由消息

  • connectors: 生产者或者消费者和消息队列建立连接的情况

  • channels: 消息通道,生产者消费者进行通信需要建立一个通道。

  • Admin: 管理虚拟主机,添加和查看已有的用户

RabbitMQ架构:
在这里插入图片描述

3、java基于AMQP协议操作RabbitMQ

AMQP(Advanced Message Queuing Protocol),是用于在应用程序之间传递消息的开放标准协议。协议以语言和平台无关,更符合互联网的要求。

官网文档教程:
java中操作RabbitMQ

新建java的maven项目,添加依赖:

	<dependencies><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.9.0</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.17</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>2.0.17</version></dependency></dependencies>

这几个依赖必须导入。

发送方(Send.java) :

public class Send {private final static String QUEUE_NAME = "hello";public static void main(String[] args) {ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.138.133");factory.setPort(5672);factory.setUsername("root");factory.setPassword("123456");// 建立连接,创建管道try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {channel.queueDeclare(QUEUE_NAME, false, false, false, null);String message = "Hello RabbitMQ!";channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));System.out.println(" [x] Sent '" + message + "'");} catch (Exception e) {throw new RuntimeException(e);}}}

接收方(Recv.java) :

public class Recv {private final static String QUEUE_NAME = "hello";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.138.133");factory.setPort(5672);factory.setUsername("root");factory.setPassword("123456");// 建立连接,创建管道Connection connection = factory.newConnection();Channel channel = connection.createChannel();channel.queueDeclare(QUEUE_NAME, false, false, false, null);System.out.println(" [*] Waiting for messages. To exit press CTRL+C");DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), StandardCharsets.UTF_8);System.out.println(" [x] Received '" + message + "'");};channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });}
}

host和用户、密码等依据自己情况修改
先运行Recv,此时接收方会处于等待接收的状态,随后Send发送消息,接收成功。
运行结果:

 [x] Sent 'Hello RabbitMQ!'[x] Received 'Hello RabbitMQ!'

4、基于Spring AMQP操作RabbitMQ

Spring AMQP是基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息。包含两部分,其中spring-amqp是基础抽象,spring-rabbit是底层的默认实现。因此RabbitMQ中我们可以通过Spring进行操作。

	<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--AMQP依赖,包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>

消息发送者

	@Resourceprivate RabbitTemplate rabbitTemplate;@Testpublic void testSend() {// 队列名称String queueName = "hello";// 消息String message = "hello RabbitMQ!";// 发送消息rabbitTemplate.convertAndSend(queueName, message);}

消息消费者(其实就是通过监听队列来获取信息):
@RabbitListener()中的queues参数里面的值就是队列的名字。

@Slf4j
@Component
public class Consumer {@RabbitListener(queues = {"hello"}) //这里参数是队列的名字,填写的时候按自己情况来。public void testConsumer(String msg) {log.info("消费者收到消息:" + msg);}}

如果想要在代码中创建队列的话,可以在config类中定义:

@Configuration
public class MQConfig {//代表创建一个叫queue的队列@Beanpublic Queue queue() {return new Queue("queue");}
}

5、代码中创建队列与交换机

①、配置类创建

@Configuration
public class MQConfig {// 声明交换机@Beanpublic FanoutExchange fanoutExchange() {return new FanoutExchange("test.fanout");}// 声明队列1@Beanpublic Queue fanoutQueue1() {return new Queue("fanout.queue1");}// 声明队列2@Beanpublic Queue fanoutQueue2() {return new Queue("fanout.queue2");}// 绑定队列1与交换机@Beanpublic Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);}// 绑定队列2与交换机@Beanpublic Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);}
}

如上就是通过在一个config类中定义bean实现注入。

②、基于@RabbitListener注解创建

@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue1"),exchange = @Exchange(name = "test.direct", type = ExchangeTypes.DIRECT),key = {"red", "blue"}
))

该注解用配置类等价于:

@Configuration
public class MQConfig {// 声明交换机@Beanpublic DirectExchange fanoutExchange() {return new DirectExchange("test.direct");}// 声明队列1@Beanpublic Queue fanoutQueue1() {return new Queue("direct.queue1");}// 绑定队列1与交换机@Beanpublic Binding bindingQueue1(Queue fanoutQueue1, DirectExchange directExchange) {return BindingBuilder.bind(fanoutQueue1).to(directExchange).with("red");}@Beanpublic Binding bindingQueue2(Queue fanoutQueue1, DirectExchange directExchange) {return BindingBuilder.bind(fanoutQueue1).to(directExchange).with("blue");}
}

可以说极大简化了开发。

6、RabbitMQ详解

①、work模型

work模型就是多个消费者绑定到一个队列,加快消息处理速度,通过设置prefech来控制消费者领取消息的数量。
而prefetch默认值为250,这个可以自己来控制调整。

发送方代码:

	@Testpublic void testWorkQueue() throws Exception {String queueName = "work.queue";for (int i = 0; i < 50; i++) {String message = "hello, worker, message_" + i;rabbitTemplate.convertAndSend(queueName, message);Thread.sleep(50);}}

接收方代码:

    @RabbitListener(queues = {"work.queue"}) //这里参数是队列的名字,填写的时候按自己情况来。public void workConsumer(String msg) throws InterruptedException {System.out.println("work.queue队列1收到消息:" + msg);Thread.sleep(20);}@RabbitListener(queues = {"work.queue"}) //这里参数是队列的名字,填写的时候按自己情况来。public void workConsumer2(String msg) throws InterruptedException {System.err.println("work.queue队列2收到消息:" + msg);Thread.sleep(200);}

最后发现两个接收方无论处理快还是慢,最后每个都只能处理25个消息,而我们一共发了50条消息,这样会导致处理效率很低。
在这里插入图片描述
解决方法,在properties中加一个配置:

spring:listener:simple:prefetch: 1

运行结果:
在这里插入图片描述
这样处理的话,效率就高多了,基本就是发一条消息,谁有空谁来处理即可。

②、交换机

1、Fanout(广播)交换机

Fanout交换机会将接收到的消息广播到每一个与其绑定的queue,也叫广播模式。

在这里插入图片描述

这里我们在队列中声名两个queue,分别叫fanout.queue1和fanout.queue2。
交换机使用amq.fanout,为fanout类型
在这里插入图片描述
在这里插入图片描述

记得先将交换机与队列进行绑定:

在这里插入图片描述

接收方代码:

    @RabbitListener(queues = {"fanout.queue1"}) //这里参数是队列的名字,填写的时候按自己情况来。public void fanoutConsumer(String msg) throws InterruptedException {System.out.println("fanout.queue队列1收到消息:" + msg);}@RabbitListener(queues = {"fanout.queue2"}) //这里参数是队列的名字,填写的时候按自己情况来。public void fanoutConsumer2(String msg) throws InterruptedException {System.err.println("fanout.queue队列2收到消息:" + msg);}

发送方代码:

    @Testpublic void testFanoutQueue() throws Exception {String exchange = "amq.fanout";String message = "hello, everyone";rabbitTemplate.convertAndSend(exchange, "", message);}

运行结果:

fanout.queue队列2收到消息:hello, everyone
fanout.queue队列1收到消息:hello, everyone

2、Direct(定向)交换机

Direct Exchange 会将接收到的消息根据规则路由到指定的Queue,因此称为定向路由。
每一个Queue都与Exchange设置一个BindingKey,发布者发送消息时,指定消息的RoutingKey,Exchange将消息路由到BindingKey与消息RoutingKey一致的队列

在这里插入图片描述
当交换机中的key对应的值和queue中的bingdingKey值相同时,消息就发送到对应的消费者手中,同时不同queue的bingdingKey值可以是相同的,同一个bindingKey可以有多个值。

创建两个direct.queue:
在这里插入图片描述
绑定到amq.direct上:
在这里插入图片描述
接收消息代码:

 @RabbitListener(queues = {"direct.queue1"}) //这里参数是队列的名字,填写的时候按自己情况来。public void DirectConsumer(String msg) throws InterruptedException {System.out.println("fanout.queue队列1收到消息:" + msg);}@RabbitListener(queues = {"direct.queue2"}) //这里参数是队列的名字,填写的时候按自己情况来。public void DirectConsumer2(String msg) throws InterruptedException {System.err.println("fanout.queue队列2收到消息:" + msg);}

发送消息代码:

    @Testpublic void testDirectQueue() throws Exception {String exchange = "amq.direct";String message1 = "hello, Red";String message2 = "hello, Blue";String message3 = "hello, Yellow";rabbitTemplate.convertAndSend(exchange, "red", message1);rabbitTemplate.convertAndSend(exchange, "blue", message2);rabbitTemplate.convertAndSend(exchange, "yellow", message3);}

运行结果:

direct.queue队列1收到消息:hello, Red
direct.queue队列1收到消息:hello, Blue
direct.queue队列2收到消息:hello, Red
direct.queue队列2收到消息:hello, Yellow

3、Topic(话题)交换机

TopicExchange与DirectExchange类似,区别在于routingKey可以是多个单词的列表,并且以“.”分割。

Queue与Exchange指定BindingKey时可以使用通配符:

#: 代指0个或多个单词
*: 代指一个单词

在这里插入图片描述
新建两个队列:
在这里插入图片描述
使用amq.topic绑定:
在这里插入图片描述

接收方代码:

    @RabbitListener(queues = {"topic.queue1"}) //这里参数是队列的名字,填写的时候按自己情况来。public void TopicConsumer(String msg) throws InterruptedException {System.out.println("topic.queue队列1收到消息:" + msg);}@RabbitListener(queues = {"topic.queue2"}) //这里参数是队列的名字,填写的时候按自己情况来。public void TopicConsumer2(String msg) throws InterruptedException {System.err.println("topic.queue队列2收到消息:" + msg);}

发送方代码:

    @Testpublic void testTopicQueue() throws Exception {String exchange = "amq.topic";String message = "Japan's news";String message2 = "China's news";String message3 = "China's weather";String message4 = "Japan's weather";rabbitTemplate.convertAndSend(exchange, "Japan.news", message);rabbitTemplate.convertAndSend(exchange, "China.news", message2);rabbitTemplate.convertAndSend(exchange, "China.weather", message3);rabbitTemplate.convertAndSend(exchange, "Japan.weather", message4);}

运行结果:

topic.queue队列2收到消息:Japan's news
topic.queue队列2收到消息:China's news
topic.queue队列1收到消息:China's news
topic.queue队列1收到消息:China's weather

7、消息转换器

发送java对象代码:

    @Testpublic void testSendObject() {Map<String, Object> msg = new HashMap<>();msg.put("name", "jack");msg.put("age", 18);rabbitTemplate.convertAndSend("object.queue", msg);}

接收消息为乱码:

在这里插入图片描述
解决方法:
我们引入消息转换器,使用json来处理消息。

引入对应依赖:

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

接收方和消费方都配置消息转换器:

import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;@Configuration
public class messageConfig {@Beanpublic MessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}
}

在这里插入图片描述

总结

重要点:

  1. 搭建环境,熟悉RabbitMQ面板与配置
  2. 使用SpringBoot集成开发配置
  3. 重点学会@RabbitListener的使用
  4. 熟悉常见交换机和队列
  5. 配置使用消息转换器

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

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

相关文章

缓存置换:用c++实现最不经常使用(LFU)算法

在探讨缓存置换算法时&#xff0c;我们曾详细解读过LRU&#xff08;Least Recently Used&#xff09;算法&#xff0c;它凭借 “最近最少使用” 的策略在缓存管理领域大放异彩。今天&#xff0c;让我们将目光聚焦于另一种重要的缓存置换算法 ——LFU&#xff08;Least Frequent…

深度学习模型的部署实践与Web框架选择

引言 在深度学习项目的完整生命周期中&#xff0c;模型训练只是第一步&#xff0c;将训练好的模型部署到生产环境才能真正发挥其价值。本文将详细介绍模型部署的核心概念、常见部署方式以及三种主流Python Web框架的对比分析&#xff0c;帮助开发者选择最适合自己项目的技术方…

多功能气体检测报警系统,精准监测,守护安全

在化学品生产、石油化工、矿山、消防、环保、实验室等领域&#xff0c;有毒有害气体泄漏风险严重威胁工作人员和环境安全。化工企业生产中易产生大量可燃有毒气体&#xff0c;泄漏达一定浓度易引发爆炸、中毒等重大事故&#xff1b;矿井下瓦斯、一氧化碳等有害气体的浓度实时监…

lvgl多语言设置

搭建开发环境 安装node.js 安装node.js&#xff0c;点击进入官网地址 安装lv_i18n lv_i18n项目地址&#xff1a;Github&#xff1a;https://github.com/lvgl/lv_i18ngit运行命令安装lv_i18n&#xff1a;npm i lv_i18n -g。测试命令&#xff1a;lv_i18n -h 搭建过程报错 …

线程池技术

线程池基本概念 线程池就是在任务还没有到来前&#xff0c;预先创建一定数量的线程放入空闲列表。这些线程都是处于阻塞状态&#xff0c;不消耗CPU&#xff0c;但占用较小的内存空间。 当新任务到来时&#xff0c;缓冲池选择一个空线程&#xff0c;把任务传入此线程中运行&…

Go语言中的并发编程--详细讲解

文章目录 Go语言并发编程**简单介绍**goroutine channel 实现并发和并行for循环开启多个协程Channel管道goroutine 结合 channel 管道**goroutine 结合 channel打印素数**单向管道Select多路复用Goroutine Recover解决协程中出现的PanicGo中的并发安全和互斥锁 Go语言并发编程 …

C# NX二次开发:投影曲线和偏置曲线UFUN函数详解

大家好&#xff0c;今天要讲的是关于投影曲线和偏置曲线相关的函数。 &#xff08;1&#xff09;UF_CURVE_create_proj_curves1&#xff1a;这个函数的定义为创建投影曲线。 Defined in: uf_curve.h Overview Creates projection curves. Objects to project may be poi…

用R语言+随机森林玩转遥感空间预测-基于R语言机器学习遥感数据处理与模型空间预测技术及实际项目案例分析

遥感数据具有高维度、非线性及空间异质性等特点&#xff0c;传统分析方法往往难以充分挖掘其信息价值。机器学习技术的引入为遥感数据处理与模型预测提供了新的解决方案&#xff0c;其中随机森林&#xff08;Random Forest&#xff09;以其优异的性能和灵活性成为研究者的首选工…

unity 导入图片后,可选择精灵表自动切片,并可以导出为png

脚本源代码&#xff1a; #if UNITY_EDITOR using UnityEditor; using UnityEngine; using System.IO; using UnityEditorInternal; using System.Collections.Generic; using System;public class TextureImporterWindow : EditorWindow {private string folderPath "D:…

使用 Azure DevSecOps 和 AIOps 构建可扩展且安全的多区域金融科技 SaaS 平台

引言 金融科技行业有一个显著特点&#xff1a;客户期望能够随时随地即时访问其财务数据&#xff0c;并且对宕机零容忍。即使是短暂的中断也会损害用户的信心和忠诚度。与此同时&#xff0c;对数据泄露的担忧已将安全提升到整个行业的首要地位。 在本文中&#xff0c;我们将探…

基于Django框架开发的B2C天天生鲜电商平台

天天生鲜 介绍 天天生鲜是一个基于Django框架开发的B2C(Business-to-Customer)电商平台&#xff0c;专注于生鲜食品的在线销售。该项目采用了主流的Python Web开发框架Django&#xff0c;结合MySQL数据库、Redis缓存等技术&#xff0c;实现了一个功能完整、界面友好的电商网站…

ASP.NET MVC4 技术单选及多选题目汇编

一、单选题&#xff08;共50题&#xff0c;每题2分&#xff09; 1、ASP.NET MVC4 的核心架构模式是什么&#xff1f; A. MVP B. MVVM C. MVC D.三层架构 答案&#xff1a;C 2、在 MVC4 中&#xff0c;默认的路由配置文件名是&#xff1f; A. Global.asax B. RouteConfig.cs C.…

26届秋招收割offer指南

26届暑期实习已经陆续启动&#xff0c;这也意味着对于26届的同学们来说&#xff0c;“找工作”已经提上了日程。为了帮助大家更好地准备暑期实习和秋招&#xff0c;本期主要从时间线、学习路线、核心知识点及投递几方面给大家介绍&#xff0c;希望能为大家提供一些实用的建议和…

数据中心机电建设

电气系统 供配电系统 设计要求&#xff1a;数据中心通常需要双路市电供电&#xff0c;以提高供电的可靠性。同时&#xff0c;配备柴油发电机组作为备用电源&#xff0c;确保在市电停电时能及时为关键设备供电。根据数据中心的规模和设备功耗&#xff0c;精确计算电力负荷&…

每日一题洛谷P1025 [NOIP 2001 提高组] 数的划分c++

P1025 [NOIP 2001 提高组] 数的划分 - 洛谷 (luogu.com.cn) #include<iostream> using namespace std; int n, k; int res 0; void dfs(int num,int step,int sum) {//判断if (sum n) {if (step k) {res;return;}}if (sum > n || step k)return;//搜索for (int i …

大模型推理--从零搭建大模型推理服务器:硬件选购、Ubuntu双系统安装与环境配置

自从大模型火了之后就一直想自己组装一台机器去深入研究一下大模型&#xff0c;奈何囊中羞涩&#xff0c;迟迟也没有行动。在下了很大的勇气之后&#xff0c;终于花了接近4万块钱组装了一台台式机&#xff0c;下面给大家详细介绍一下我的装机过程。 1.硬件配置 研究了一周&am…

第35周Zookkeeper+Dubbo Dubbo

Dubbo 详解 一、Dubbo 是什么 官网与定义 Dubbo 是一款高性能、轻量级的开源服务框架&#xff0c;其官网为 double.apache.org&#xff0c;提供中文版本&#xff08;网址含 “zh”&#xff09;。 核心能力 Dubbo 具备六大核心能力&#xff1a; 面向接口代理的高性能 RPC …

NX二次开发——BlockUI 弹出另一个BlockUI对话框

最近在研究&#xff0c;装配体下自动导出BOM表格中需要用到BlockUI 弹出另一个BlockUI对话框。通过对网上资料进行整理总结&#xff0c;具体如下&#xff1a; 1、明确主对话框、子对话框1和子对话框2 使用BlockUI创建.cpp和.hpp文件&#xff0c;dlx文件内容如下所示 主对话框…

PostgreSQL 系统管理函数详解

PostgreSQL 系统管理函数详解 PostgreSQL 提供了一系列强大的系统管理函数&#xff0c;用于数据库维护、监控和配置。这些函数可分为多个类别&#xff0c;以下是主要功能的详细说明&#xff1a; 一、数据库配置函数 1. 参数管理函数 -- 查看所有配置参数 SELECT name, sett…

【2025软考高级架构师】——计算机网络(9)

摘要 全文主要围绕计算机网络相关知识展开&#xff0c;包括域名服务器查询方式、网络规划与设计的关键技术、双协议栈与隧道技术、层次化网络设计、网络冗余设计以及高可靠和高可用性等方面&#xff0c;旨在为软考高级架构师的备考提供知识参考。 1. 通信网络架构图 2. 通信架…