顶层架构 - 消息集群推送方案
一、推送基础概念简述
在即时通讯(IM)系统中,最基础的一件事就是“如何把消息推送给用户”。为了实现这个过程,我们要先了解两种常见的网络通信方式:HTTP 和 WebSocket。
1. HTTP 是什么?
HTTP 就像一次性对话:
-
客户端发起请求,服务端回复一次后,这条连接就断了。
-
它是“无状态”的,也就是说服务器不会记得你上一次说了什么。
它的好处是使用简单,适合访问网页、发评论这种“说一次就好”的请求。
2. WebSocket 是什么?
WebSocket 则像“打电话”:
-
一旦连接建立,客户端和服务端可以一直保持联系,随时互相发消息。
-
它同样是基于 TCP 的,但是比 HTTP 更适合 IM 这种“随时聊”的场景。
这种方式的好处是响应快,不用每次都重新连接。
3. 单机情况:UID 和 Channel 的绑定
在只有一台服务器的情况下,推送消息很简单:
-
用户上线后会建立一个 WebSocket 连接,系统会记录下他的用户 ID(UID)和对应的连接通道(Channel)。
-
当有新消息时,直接通过这个通道发出去就行。
就像是你进了聊天室,系统知道你在哪个窗口,喊你一声就能听到。
二、集群推送的基本挑战
在上节中我们提到:在只有一台服务器时,系统只要记住用户的 UID 和对应的连接通道(Channel),推送消息非常简单。
但如果用户数量多到需要部署多台 WebSocket 服务器怎么办?这时候系统进入“集群模式”,问题也就随之来了。
1. WebSocket 的“局部性”问题
和 HTTP 不同,WebSocket 是“长连接”,一旦用户连上了某台服务器,他的这条连接就只属于这一台服务器了。
也就是说:
-
用户 A 连的是 Server1;
-
如果你在 Server2 上发消息,是没办法直接推给他的。
这就像你给朋友打电话,他接电话的手机只在一台服务器上,你换台服务器就打不通了。
2. UID 和 Channel 不能中心化存储
很多人第一反应是:“我用 Redis 把 UID 和 Channel 的关系存起来就好了!”
其实不行。为什么?
-
Channel 是服务器本地的一个连接对象,不能序列化,也不能跨服务器使用。
-
换句话说,哪怕你从 Redis 拿到了 UID 对应的 Channel 标识,你也没办法在别的机器上用。
3. 如何找到用户在哪台服务器?
那怎么办?
这时候我们用 Redis 存一张 “UID ↔ 所在服务器 IP” 的表。
比如:
-
UID 1001 在 Server1;
-
UID 1002 在 Server2。
我们叫这个模块“Router 路由服务”。当需要推送时,先查 Redis 知道用户在哪台服务器,然后转发过去。
4. 路由连接数爆炸问题
但是,问题又来了。
如果有 1000 台 WebSocket 服务器,为了支持转发消息,每台 Router 服务都得跟这 1000 台服务器建立连接。
再假设我们有很多台 Router,那连接数就会变得非常庞大,连接被系统自己占满了,真正给用户的反而不够了。
这种情况叫“连接数爆炸”,是一种严重的性能隐患。
总结一下,在进入集群推送后,我们面临几个核心挑战:
-
WebSocket 连接是“黏在服务器”上的,不能随便切换;
-
Channel 是局部对象,无法跨服务器传递;
-
路由虽然能找到人在哪,但会带来连接爆炸的问题。
三、路由层优化方案
上节我们说到,虽然可以通过 Redis 知道用户在哪台服务器,但如果每个路由服务都要和所有 WebSocket 服务器建连接,会导致连接数爆炸,影响性能。
那怎么优化呢?答案是:加一层中转站,让大家不用“全连接”了。
1. 加一层“中间人”:消息中间件
我们可以在路由和 WebSocket 服务器之间,加一层叫做 消息中间件 的东西,常用的有:
-
Kafka
-
Redis 的发布订阅(Pub/Sub)
-
RabbitMQ 等等
这层中间件就像一个“公共广播站”,大家只要:
-
WebSocket 服务器:订阅广播频道(比如订阅“用户1001的推送”)
-
路由服务:往广播频道发消息
就能做到“不需要直接连接,也能把消息推过去”。
2. 怎么发消息?
假设你要给 UID 为 1001 的用户发一条消息,流程是这样的:
-
路由服务查 Redis,发现 UID 1001 在 Server2;
-
路由服务就往“Server2 的频道”发一条消息;
-
Server2 已经订阅了这个频道,收到后立刻推送给用户。
注意:
-
这时候,路由服务 不需要 和 Server2 保持 TCP 连接;
-
所有服务器只要订阅自己的频道即可,不会引发连接爆炸。
3. 多个用户怎么发?
如果你要群发给很多用户,比如 UID 列表是:[1001, 1002, 1003]
做法是:
-
分别查每个用户所在的服务器;
-
按服务器分组,比如 1001 和 1003 在 Server2,1002 在 Server1;
-
给 Server1 和 Server2 各发一条消息,带上用户列表。
WebSocket 服务器拿到后,内部再去一个个推送。
小结一下:
我们通过“消息中间件”解决了连接数爆炸的问题:
-
路由服务发消息 → 中间件;
-
WebSocket 服务器监听消息 → 收到后推送;
-
不需要点对点连接,系统更加轻量稳定。
四、推送策略的两种选择
在给用户发消息时,我们有两种主要的推送方式,每种都有自己的适用场景:
1. 精准推送(点对点)
-
什么是精准推送?
就是给每个用户单独发送消息,确保消息只送到那个用户对应的服务器和连接。 -
优点:
消息精准送达,适合私聊或者重要消息,避免不必要的数据浪费。 -
缺点:
如果用户量很大,需要推送的次数就多,服务器压力大,可能导致延迟或宕机。
2. 集群广播(群发)
-
什么是集群广播?
给一个服务器(或多个服务器)广播消息,让服务器自己决定把消息发给哪些用户。 -
优点:
推送次数少,服务器压力相对低,适合大群聊或通知类消息。 -
缺点:
每台服务器要自己过滤哪些用户能收到消息,效率上可能稍有损耗。
什么时候用哪个?
-
私聊和小范围消息,推荐用精准推送,保证消息不漏发。
-
大群聊或者广播消息,推荐用集群广播,减少系统压力。
简单来说,就是:
-
想“定点发射”,用精准推送;
-
想“全网广播”,用集群广播。
五、消息过滤优化手段
在消息推送过程中,不是所有消息都要发给所有用户,过滤消息非常重要,能帮我们节省网络和服务器资源。
1. 把用户ID放在消息头部(Header)
-
每条消息里带上目标用户的ID(UID)信息。
-
服务器收到消息后,先看UID,只给对应的用户处理,不用把消息内容全部解析,节省时间。
2. 服务器端过滤
-
有些消息队列系统支持在服务器端过滤消息,只有符合条件的消息才会被发送给对应的连接。
-
这样可以减少不必要的数据传输,降低网络负担。
3. 分组或标签过滤
-
给用户分组或打标签,比如“兴趣爱好”、“地理位置”等。
-
发送消息时只给相关组的用户推送,避免无效发送。
4. 动态过滤的挑战
-
动态变化的用户群体和消息目标,让过滤变得复杂。
-
一些消息系统对动态过滤支持不好,需要通过自定义改造来实现。
六、定制化消息中间件的探索方向
消息中间件是负责帮我们传递消息的“邮递员”,但有时候现成的“邮递员”不完全符合我们的需求,所以我们会考虑自己定制或改造它们。
1. 适配业务特点
不同业务对消息传递的要求不同,比如速度、可靠性、扩展性等。定制中间件能更好地满足这些需求。
2. 优化性能
定制消息中间件可以减少不必要的开销,比如精简消息格式、减少网络传输,提升整体效率。
3. 灵活过滤和路由
自定义过滤和路由规则,让消息能更精准快速地送达目标用户,减少资源浪费。
4. 解决扩展性问题
随着用户量和消息量增长,普通中间件可能瓶颈明显。定制化设计能更好地支持大规模集群和多层路由。
5. 可控性和维护性
自己定制中间件,可以更方便地监控、调试和升级,减少对第三方依赖。
相关文章:
顶层架构 - 消息集群推送方案
一、推送基础概念简述 在即时通讯(IM)系统中,最基础的一件事就是“如何把消息推送给用户”。为了实现这个过程,我们要先了解两种常见的网络通信方式:HTTP 和 WebSocket。 1. HTTP 是什么? HTTP 就像一次性…...
C++性能测试工具——Vtune等的介绍
一、介绍 我们在前面的相关文章中对C性能的测试和分析工具(见“C性能测试工具gprof和gperftools基础”等)有一个初步的了解和应用,其实类似的相关工具还有不少。为了进一步的让开发者们掌握更多的相关性能测试分析相关的方法,对另…...
车道线检测----CLRKDNet
今天的最后一篇 车道线检测系列结束 CLRKDNet:通过知识蒸馏加速车道检测 摘要:道路车道是智能车辆视觉感知系统的重要组成部分,在安全导航中发挥着关键作用。在车道检测任务中,平衡精度与实时性能至关重要,但现有方法…...
【AI模型部署】
解决python引入huggingface_hub模块下载超时问题 背景问题解决 背景 AMD Ryzen™ AI处理器通过独特的NPUGPU异构架构,为AI工作负载提供强大的并行计算能力。本方案展示了如何将YOLOv8目标检测、RCAN超分辨率重建和Stable Diffusion文生图三类模型分别部署到NPU和GP…...
排序01:多目标模型
用户-笔记的交互 对于每篇笔记,系统记录曝光次数、点击次数、点赞次数、收藏次数、转发次数。 点击率点击次数/曝光次数 点赞率点赞次数/点击次数 收藏率收藏次数/点击次数 转发率转发次数/点击次数 转发是相对较少的,但是非常重要,例如转发…...
电子电器架构 --- Zonal架构正在开创汽车电子设计新时代
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...
如何阅读、学习 Tcc (Tiny C Compiler) 源代码?如何解析 Tcc 源代码?
阅读和解析 TCC(Tiny C Compiler) 的源代码需要对编译器的基本工作原理和代码结构有一定的了解。以下是分步骤的指南,帮助你更高效地学习和理解 TCC 的源代码: 1. 前置知识准备 C 语言基础:TCC 是用 C 语言编写的&…...
Java 泛型与类型擦除:为什么解析对象时能保留泛型信息?
引言:泛型的“魔术”与类型擦除的困境 在 Java 中,泛型为开发者提供了类型安全的集合操作,但其背后的**类型擦除(Type Erasure)**机制却常常让人困惑。你是否遇到过这样的场景? List<String> list …...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(22):复习
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(22):复习 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)复习(2)復習3、单词(1)日语(2)日语片假名单词4、对话练习5、单词辨析记录6、总结1、前言 (1)情况说明 自己在今年,在日本留学中,目前在语言学校,…...
Java基础学习
Java 基础大纲 1. Java 概述 Java 语言特点(跨平台、面向对象、自动内存管理) JVM、JRE、JDK 的作用与区别 开发环境搭建(安装 JDK、配置环境变量、IDE 使用) 2. 基础语法(已经学习) 变量与数据类型&a…...
MGX:多智能体管理开发流程
MGX的多智能体团队如何通过专家混合系统采用全新方法,彻底改变开发流程,与当前的单一智能体工具截然不同。 Lovable和Cursor在自动化我们的特定开发流程方面取得了巨大飞跃,但问题是它们仅解决软件开发的单一领域。 这就是MGX(MetaGPT X)的用武之地,它是一种正在重新定…...
2025第三届盘古石杯初赛(计算机部分)
前言 比赛的时候时间不对,打一会干一会,导致比赛时候思路都跟不上,赛后简单复现一下,希望大家批批一下 计算机取证 1、分析贾韦码计算机检材,计算机系统Build版本为?【标准格式:19000】 183…...
XML介绍及常用c及c++库
一.xml概述 1.什么是XML? XML(eXtensible Markup Language)是一种标记语言,1998 年 2 月:XML 1.0 发布,用于存储和传输结构化数据。与HTML专注于数据显示不同,XML专注于数据本身及其结构。 它…...
动态规划-63.不同路径II-力扣(LeetCode)
一、题目解析 与62.不同路径不同的一点是现在网格中有了障碍物,其他的并没有什么不同 二、算法解析 1.状态表示 dp[i][j]表示:到[i,j]位置时,不同的路径数 2.状态转移方程 由于多了障碍物,所以我们要判断是否遇到障碍物 3.初…...
海盗王3.0的数据库3合1并库处理方案
原版的海盗王数据库有3个accountserver,gamedb,tradedb,对应到是账号数据库,游戏数据库,商城数据库。 一直都有个想法,如何把这3个库合并到一起,这样可以实现一些功能。 涉及到sqlserver的数据库…...
Vue百日学习计划Day16-18天详细计划-Gemini版
重要提示: 番茄时钟: 每个番茄钟为25分钟学习,之后休息5分钟。每完成4个番茄钟,进行一次15-30分钟的长休息。动手实践: DOM 操作和事件处理的理解高度依赖于实际编码。请务必在浏览器中创建 HTML 页面,并配…...
【C++】15.并发支持库
本篇内容参考自cplusplus 1. thread 1.1 thread thread库底层是对各个系统的线程库(Linux下的pthread库和Windows下Thread库)进行封装。C11thread库的第一个特点是可以跨平台,第二个特点是Linux和Windows下提供的线程库都是面向过程的&…...
Linux系统编程——exec族函数
我们来完整、系统、通俗地讲解 Linux 系统编程中非常重要的一类函数:exec 族函数(也叫 exec family)。 一、什么是 exec? exec 系列函数的作用是: 用一个新的程序,替换当前进程的内容。 也就是说…...
职教实训室中的写实数字人:技术与应用方案
在当今快速发展的数字化时代,职业教育的重要性日益凸显。面对传统教学模式中个性化不足、互动性差等挑战,深声科技基于2D写实交互数字人的解决方案为职教实训室带来了全新的变革。本文将详细介绍该技术方案的核心原理、产品特色及其在职业培训中的实际应…...
Nginx模块配置与请求处理详解
Nginx 作为模块化设计的 Web 服务器,其核心功能通过不同模块协同完成。以下是各模块的详细配置案例及数据流转解析: 一、核心模块配置案例 1. Handler 模块(内容生成) 功能:直接生成响应内容(如静态文件、重定向等) # 示例1:静态文件处理(ngx_http_static_module)…...
54. 螺旋矩阵
题目链接: a54. 螺旋矩阵 题目描述: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 题目分析: 改题目需要判断是否溢出边界,与59不同,59可以判断是否为0…...
virtualbox虚拟机中的ubuntu 20.04.6安装新的linux内核5.4.293 | 并增加一个系统调用 | 证书问题如何解决
参考文章:linux添加系统调用【简单易懂】【含32位系统】【含64位系统】_64位 32位 系统调用-CSDN博客 安装新内核 1. 在火狐下载你需要的版本的linux内核压缩包 这里我因为在windows上面下载过,配置过共享文件夹,所以直接复制粘贴通过共享文…...
代码随想录算法训练营第三十八天打卡
今天是动态规划的第三天,昨天的不同路径与整数分解的几道题目大家理解得如何?如果有疑问大家还是多去想想dp数组究竟是什么含义,还有我的状态转移是否正确,初始化是否正确,这一点很重要,今天的题目依旧是跑…...
【论信息系统项目的整合管理】
论信息系统项目的整合管理 某省机场管理集团航空货运站原有物流生产信息系统无法满足机场货运站生产信息与航空公司、对方航站、进出口航空货物按海关监管要求电子报关等行业信息实时共享发展需要,生产信息需多次重复录入问题已成为业务发展最大瓶颈,急需…...
小学数学题批量生成及检查工具
软件介绍 今天给大家介绍一款近期发现的小工具,它非常实用。 软件特点与出题功能 这款软件体积小巧,不足两兆,具备强大的功能,能够轻松实现批量出题。使用时,只需打开软件,输入最大数和最小数,…...
Python线性回归:从理论到实践的完整指南
Python线性回归:从理论到实践的完整指南 线性回归是数据科学和机器学习中最基础且最重要的算法之一。本文将深入探讨如何使用Python实现线性回归,从理论基础到实际应用,帮助读者全面理解这一重要的统计学和机器学习方法。 什么是线性回归&a…...
python 爬虫框架介绍
文章目录 前言一、Requests BeautifulSoup(基础组合)二、Scrapy(高级框架)三、PySpider(可视化爬虫)四、Selenium(浏览器自动化)五、Playwright(新一代浏览器自动化&…...
强化学习算法实战:一个例子搞懂sarsa、dqn、ddqn、qac、a2c及其区别
简介 在学习强化学习算法:sarsa、dqn、ddqn、qac、a2c、trpo、ppo时,由于有大量数学公式的推导,觉得十分晦涩,且听过就忘记了。 但是当把算法应用于实战时,代码的实现要比数学推导直观很多。 接下来通过不同的算法实现…...
文章记单词 | 第86篇(六级)
一,单词释义 pretty /ˈprɪti/- adj. 漂亮的;相当的 /adv. 相当地labour /ˈleɪbə(r)/- n. 劳动;劳工;分娩 /v. 劳动;努力(英式英语, labor)imaginary /ɪˈmdʒɪnəri/- adj. …...
firewall防火墙
一.Firewalld 防火墙概述 1.firewalld 简介 firewalld 的作用是为包过滤机制提供匹配规则(或称为策略),通过各种不同的规则告诉netfilter 对来自指定源、前往指定目的或具有某些协议特征的数据包采取何种处理方式为了更加方便地组织和管理防火墙,firewa11d 提供了…...
TII-2024《AGP-Net: Adaptive Graph Prior Network for Image Denoising》
推荐深蓝学院的《深度神经网络加速:cuDNN 与 TensorRT》,课程面向就业,细致讲解CUDA运算的理论支撑与实践,学完可以系统化掌握CUDA基础编程知识以及TensorRT实战,并且能够利用GPU开发高性能、高并发的软件系统…...
Pageassist安装(ollama+deepseek-r1)
page-assist网站:https://github.com/n4ze3m/page-assist 首先电脑配置node.js,管理员打开命令窗口输入下面命令下载bun npm install -g buncd 到你想要安装page-assist的地方(推荐桌面) 输入下列命令 git clone https://gith…...
Java—— 方法引用 : :
方法引用是什么 把已经存在的方法拿过来用,当做函数式接口中抽象方法的方法体 方法引用符 :: 方法引用的条件 1.需要有函数式接口 2.被引用方法必须已经存在 3.被引用方法的形参和返回值需要跟抽象方法保持一致 4.被引用方法的功能要满足当前…...
Linux基础开发工具大全
目录 软件包管理器 1>软件包 2>软件生态 3>yum操作 a.查看软件包 b.安装软件 c.卸载软件 4>知识点 vim编辑器 1>基本概念 2>基本操作 3>正常模式命令集 a.模式切换 b.移动光标 c.删除 d.复制 e.替换 f.撤销 g.更改 4>底行模式命令…...
C语言实现INI配置文件读取和写入
一.INI文件介绍 INI配置文件是一种简单的文本文件,用于存储配置信息,通常由一个或多个节(section)组成,每个节包含多个键值对(Key-Value)格式。INI文件易于阅读和编辑,广泛应用于多…...
volatile关键字详解
volatile关键字详解 1. 定义与核心作用 volatile 是Java中的关键字,用于修饰变量,主要解决多线程环境下的内存可见性和指令重排序问题。其核心作用: 保证可见性:确保所有线程读取到变量的最新值。禁止指令重排序:防止…...
二叉树子树判断:从递归到迭代的全方位解析
一、题目解析 题目描述 给定两棵二叉树root和subRoot,判断root中是否存在一棵子树,其结构和节点值与subRoot完全相同。 示例说明 示例1: root [3,4,5,1,2],subRoot [4,1,2] 返回true,因为root的左子树与subRoot完…...
【PhysUnits】4.1 类型级比特位实现解释(boolean.rs)
一、源码 该代码实现了一个类型级(type-level)的布尔系统,允许在编译时进行布尔运算。 //! 类型级比特位实现 //! //! 这些是基础的比特位类型,作为本库中其他数值类型的构建基础 //! //! 已实现的**类型运算符**: //! //! - 来自 core::op…...
(7)python开发经验
文章目录 1 找不到资源文件2 使用subprocess执行时有黑色弹窗3 找不到exec4 pyside6-project lupdate的bug5 找不到pyd模块6 pyd模块编码错误7 运行显示Qt platform plugin "windows" in "8 tr()包含的字符串无法被翻译 更多精彩内容👉内容导航 &…...
【Manim】使用manim画一个高斯分布的动画
1 Manim例子一 最近接触到manim,觉得挺有趣的,来玩一玩把。如下是一个使用manim画的高斯分布的动画。 from manim import * import numpy as npclass GaussianDistribution(Scene):def construct(self):# 创建坐标系axes Axes(x_range[-4, 4, 1],y_ra…...
Day11-苍穹外卖(数据统计篇)
前言: 今天写day11的内容,主要讲了四个统计接口的制作。看起来内容较多,其实代码逻辑都是相似的,这里我们过一遍。 今日所学: Apache ECharts营业额统计用户统计订单统计销量排行统计 1. Apache ECharts 1.1 介绍 A…...
论文阅读:Self-Collaboration Code Generation via ChatGPT
地址:Self-Collaboration Code Generation via ChatGPT 摘要 尽管大型语言模型(LLMs)在代码生成能力方面表现出色,但在处理复杂任务时仍存在挑战。在现实软件开发中,人类通常通过团队协作来应对复杂任务,…...
LocaleContextResolver实现多语言切换-笔记
1. LocaleContextResolver功能简介 org.springframework.web.servlet.LocaleContextResolver是 Spring MVC 中用于解析和管理用户 Locale(语言环境) 的核心接口。 //LocaleContextResolver 接口定义 public interface LocaleContextResolver extends L…...
Vue3中setup运行时机介绍
在 Vue3 中,直接写在 <script setup>...</script> 中的代码运行时机可以分为以下几个关键阶段: 一、执行顺序层级 #mermaid-svg-bF3p98MiNdLfcoSG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#33…...
计算机视觉----感兴趣区域(ROI)、非极大值抑制
感兴趣区域(Region of Interest,ROI)是指在一幅图像或者数据集中,用户关注并希望进行重点分析、处理或者研究的特定区域。以下为你详细介绍它在不同领域的应用: 医学影像领域 在医学影像中,医生可以通过确…...
YOLO11解决方案之对象裁剪探索
概述 Ultralytics提供了一系列的解决方案,利用YOLO11解决现实世界的问题,包括物体计数、模糊处理、热力图、安防系统、速度估计、物体追踪等多个方面的应用。 对象裁剪是指从图像或视频中分离并提取特定的检测对象,YOLO11 模型功能可用于准…...
将单链表反转【数据结构练习题】
- 第 98 篇 - Date: 2025 - 05 - 16 Author: 郑龙浩/仟墨 反转单链表(出现频率非常的高) 文章目录 反转单链表(出现频率非常的高)题目:反转一个链表思路:代码实现(第3种思路): 题目:反转一个链表 将 1->2->3->4->5->NULL反转…...
多网卡管理实战指南:原理、问题分析与实用工具推荐
多网卡管理实战指南:原理、问题分析与实用工具推荐 在现代网络环境中,越来越多的用户面临一个实际问题:一台电脑连接了多个网络接口,如有线 无线、双有线、或实体网卡 虚拟VPN网卡。这种“多网卡”环境虽然提供了更多可能性&am…...
qt5.14.2 opencv调用摄像头显示在label
ui界面添加一个Qlabel名字是默认的label 还有一个button名字是pushButton mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <opencv2/opencv.hpp> // 添加OpenCV头文件 #include <QTimer> // 添加定…...
使用Python实现简单的人工智能聊天机器人
最近研学过程中发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击链接跳转到网站人工智能及编程语言学习教程。读者们可以通过里面的文章详细了解一下人工智能及其编程等教程和学习方法。下面开始对正文内容的…...