EMQX 性能调优:TCP SYN 队列与 Accept 队列

在上一篇博客(EMQX 性能调优:最大连接与文件描述符),我们深入研究了 MQTT 连接与文件描述符之间的关系,介绍了如何修改文件描述符相关的内核参数来突破默认的最大连接数量限制。

但你可能会发现,在某些情况下,即便当前服务端的 MQTT 连接总数并未达到文件描述符限制,客户端的连接请求仍然失败。当你运行以下命令,你将看到以下 Overflowed 和 SYN Dropped 计数在不断增加:

$ watch -d 'netstat -s | grep -i "listen"'2091 times the listen queue of a socket overflowed3418 SYNs to LISTEN sockets dropped

本文将介绍导致这一现象的原因以及如何通过内核参数调优来解决此问题。

SYN 队列与 Accept 队列

前文提到的这两个计数,通常意味着 SYN 队列和 Accept 队列发生了溢出,一旦溢出,客户端的正常连接就会受到影响。

所以首先,我们需要了解 SYN 队列和 Accept 队列分别是什么?

MQTT 作为构建在 TCP 之上的应用层协议,我们总是必须先建立 TCP 连接,然后才能发送 MQTT 协议的 CONNECT 报文。建立 TCP 连接,需要经过三次握手:

tcp 3 way handshake

  1. 客户端向服务器发送 SYN(同步)报文,表示要建立连接。
  2. 服务器用 SYN-ACK(同步确认)报文进行响应,表示愿意建立连接。
  3. 客户端回复 ACK(Acknowledge)报文,表示已收到 SYN-ACK 报文,连接已建立。

由于网络延迟的存在,从服务端发出 SYN-ACK 报文到收到客户端回复的 ACK 报文总是需要一段时间。在这段时间内,服务端需要暂存本次连接的关键信息,例如 TCP 四元组、MSS(Maximum Segment Size,最大分段大小)和窗口缩放因子(Window Scale)。所以,Linux 内核维护了一个 SYN 队列来存放这些连接信息。

当服务端收到客户端回复的 ACK 报文,连接建立完成,就会将连接从 SYN 队列取出,然后放入同样由内核维护的 Accept 队列,直到上层应用调用 accept() 将连接从 Accept 队列中取出。Accept 队列在这里的主要作用是解耦了网络层和应用层,使 TCP 三次握手和实际的数据传输可以并行进行,有效提高连接请求的处理效率。

syn queue and accept queue

但服务端的资源是有限的,不管是 SYN 队列还是 Accept 队列,它们都有着最大长度限制。如果客户端一直只发送 SYN 报文而不回复最后的 ACK 报文,或者服务端的应用程序没有及时地调用 accept(),那么这两个队列就会有溢出的风险。

SYN 队列溢出时将发生什么?

服务端在 SYN 队列溢出时的行为,主要由 net.ipv4.tcp_syncookies 选项决定。

由于 SYN 队列的长度总是有限的,所以一些攻击者会尝试采用发送大量 SYN 报文的方式来对服务端发起攻击,企图耗尽服务端的 SYN 队列来阻止合法连接的建立,这也就是我们常说的 SYN 泛洪攻击 (SYN Flood Attack)。

SYN Cookie 机制被设计用于解决这一问题。简单来说,当启用这一机制后,Linux 在收到 SYN 报文时将基于时间戳、四元组等信息计算出一个 Cookie,然后作为 SYN-ACK 报文的序列号后返回给客户端。客户端在 ACK 中将序列号加一返回,服务端只需要减一就可以逆推出原始的 Cookie。因此,服务端不需要再将连接请求放入 SYN 队列。

syn cookie

但 SYN Cookie 机制也存在一些弊端:

  1. Cookie 的计算并未包含 SACK(Selective Acknowledgment,选择性确认) 和 Window Scale (窗口缩放因子)这些 TCP 选项,启用 SYN Cookie 后,服务端将不会保存这些选项,所以这些功能将无法使用。虽然从 Linux 内核 v2.6.26 开始,我们可以启用 TCP Timestamps 选项(net.ipv4.tcp_timestamps),借助 32 Bit 时间戳的低 6 bit 来存放这些 TCP 选项,但 TCP Timestamps 需要客户端和服务端的共同支持才会真正启用。
  2. 用于生成 Cookie 的 Hash 计算增加了服务端的负载。

所以目前 net.ipv4.tcp_syncookies 选项一共有三个可取值:

  • net.ipv4.tcp_syncookies = 0,表示关闭 SYN Cookie 机制,如果 SYN 队列已满,那么新到的 SYN 报文将被丢弃。
  • net.ipv4.tcp_syncookies = 1,表示 SYN Cookie 机制仅在 SYN 队列满时才正式启用。
  • net.ipv4.tcp_syncookies = 2,表示无条件启用 SYN Cookie 机制。

不同类型和版本的操作系统中,net.ipv4.tcp_syncookies 的默认值可能不同,你可以运行以下命令查看当前值:

sysctl -n net.ipv4.tcp_syncookies

考虑到 SYN Cookie 可能带来的副作用,通常我们建议仅在 SYN 队列满时才启用 SYN Cookie(将 net.ipv4.tcp_syncookies 设置为 1),优先尽可能地增加 SYN 队列最大长度。运行以下命令以修改此选项:

sysctl -w net.ipv4.tcp_syncookies=1

RTT 对 SYN 队列的影响

连接请求在 SYN 队列中停留的时间,基本等同于服务端发出 SYN-ACK 报文到收到客户端返回的 ACK 报文的时间。换句话说,它完全取决于报文在客户端与服务端之间的往返时间(Round Trip Time,RTT)。

RTT 越长,那么连接请求将越容易占满 SYN 队列。假设 RTT 为 200 ms,SYN 队列最大长度为 512,那么只要每秒向服务端发起连接请求的客户端数量超过 2560 个就会造成 SYN 队列溢出。

查看当前 SYN 队列大小

Linux 没有提供相应的内核参数供我们直接查看当前 SYN 队列大小,但通过前文我们可以知道 SYN 队列中的连接都处于 SYN-RECEIVED 状态,因此我们可以借助 netstat 命令统计 SYN-RECEIVED 状态的连接数量来间接获得当前 SYN 队列的大小:

sudo netstat -antp | grep SYN_RECV | wc -l

如何确认 SYN 队列发生溢出?

当 SYN 报文因为 SYN 队列满而被丢弃时,服务端中的以下计数会相应增加:

$ netstat -s | grep "LISTEN"<Number> SYNs to LISTEN sockets dropped

不过 SYN 报文也可能因为 Accept 队列满而被丢弃,所以还需要结合 Accept 队列的情况综合判断。

需要注意的是,在 CentOS 中,即便启用了 SYN Cookie 机制,服务端已经不会再丢弃 SYN 报文,此 SYN 丢弃计数仍然可能增加:

  • SYN Cookie 设置为 1 时,如果 SYN 队列已满,那么新到的 SYN 报文仍然会使此计数增加。这可以帮助我们评估是否需要增加 SYN 队列的最大长度。
  • SYN Cookie 设置为 2 时,此计数不存在任何意义。

Accept 队列溢出时将发生什么?

服务端在 Accept 队列溢出时的行为,主要由 net.ipv4.tcp_abort_on_overflow 选项决定。

通常情况下,此选项的默认值为 0,即当 Accept 队列溢出时,服务端将直接丢弃第三次握手的 ACK 报文,并视作从未收到该 ACK 报文,因此服务端将重传 SYN-ACK 报文,最大重传次数由 net.ipv4.tcp_synack_retries 选项决定。

accept queue over flow

虽然服务端丢弃了 ACK 报文,但是对客户端来说,三次握手已经完成,所以它可以发送后续的应用数据。不过这些携带了应用数据的 PSH 也会和 ACK 报文一样被服务端直接丢弃。由于收不到响应,客户端将不断地重传 PSH 报文,PSH 报文的最大重传次数由 net.ipv4.tcp_retries2 选项决定。

accept queue over flow

将 net.ipv4.tcp_abort_on_overflow 设置为 0 的好处是如果在 SYN-ACK 或 PSH 报文达到最大重传次数前,上层应用及时地取出连接使 Accept 队列出现空位,那么连接可以直接恢复,这更有利于应对突发流量。但如果 Accept 队列过短,导致客户端和服务端过早地重传报文,也会浪费流量以及降低连接效率。

相反,如果将 net.ipv4.tcp_abort_on_overflow 设置为 1,那么服务端将在 Accept 队列溢出时直接向客户端返回 RST 报文来关闭连接。

accept queue over flow

所以通常我们建议将 net.ipv4.tcp_abort_on_overflow 设置为 0,除非你确信服务端在短时间内无法从繁忙中恢复并且希望尽快通知客户端。

不同类型和版本的操作系统在这方面的行为可能不同。以上行为在 CentOS 中得到验证,但 Ubuntu 似乎采用了另一种行为模式,并且更改 net.ipv4.tcp_abort_on_overflow 似乎并不会改变 Ubuntu 的行为。

如果你对此感兴趣,我们在 这里 提供了用于模拟 Accept 队列溢出的示例代码和操作步骤,你可以在自己的环境中自行试验。

另外,当 Accept 队列溢出时,即使启用了 SYN Cookie 机制,服务端也不会再接受新的连接请求,即到达服务端的 SYN 报文将被直接丢弃,这会使得 SYN 丢弃计数增加。

查看当前 Accept 队列大小

我们可以使用 ss 命令来查看当前 Accept 队列的情况。对于监听状态的套接字,ss 命令获得的第二列 Recv-Q 表示当前 Accept 队列的大小,第二列 Send-Q 则表示 Accept 队列的最大长度:

$ ss -lnt
LISTEN   0    1024   *:1883         *:*

如何确认 Accept 队列发生溢出?

每当服务端因为 Accept 队列溢出而丢弃报文时,不管是第一次握手的 SYN 报文,还是第三次握手的 ACK 报文,又或者是 PSH 报文,服务端中的以下计数都会相应加 1:

$ netstat -s | grep "overflowed"<Number> times the listen queue of a socket overflowed

因此我们可以通过观察这个计数是否增长来判断 Accept 队列是否发生溢出。

如何增加 SYN 队列和 Accept 队列的大小?

Accept 队列比较简单,它的最大长度由监听函数(例如 listen(fd, backlog))中的 backlog 参数和 net.core.somaxconn 这个 Linux 内核参数决定。Linux 总是取 backlog 和 net.core.somaxconn 中对的较小值作为 Accept 队列的最大长度。

在 EMQX 中,我们可以为每个监听器都单独设置 backlog,以默认的 TCP 监听器为例,我们只需要在 emqx.conf 中添加以下配置即可:

listeners.tcp.default {tcp_options {backlog = 1024}
}

如果想要修改其他监听器的 backlog,只需要使用对应的协议名和监听器名称即可:

listeners.[tcp | ssl | ws | wss | quic].<Listener Name> {tcp_options {backlog = 1024}
}

SYN 队列略为复杂,它的最大长度并不由某个内核参数直接决定,而是受到 net.ipv4.tcp_max_syn_backlognet.core.somaxconn 等参数的综合影响。在不同的操作系统中,这些参数的效果还会有所差异。在 CentOS 中,SYN 队列的最大长度有着以下计算公式:

Max SYN Queue Size = roundup_pow_of_two(max(min(somaxconn, backlog, sysctl_max_syn_backlog), 8) + 1)

roundup_pow_of_two(Num) 表示将 Num 向上取整到 2 的幂。例如,当 Num 为 6,7 或 8 时,roundup_pow_of_two(Num) 总是返回 8。

因此,如果我们将 somaxconn 设置为 64,tcp_max_syn_backlog 设置为 128,而 listen() 函数的 backlog 设置为 256 时,那么在 CentOS 中最终 SYN 队列的最大长度将是 256。

而在 Ubuntu 中,SYN 队列的长度必须小于 Accept 队列的最大长度,并且小于等于 0.75 倍的 net.ipv4.tcp_max_syn_backlog。我们可以转换为以下公式:

Max SYN Queue Size = min(min(somaxconn, backlog), 0.75 * tcp_max_syn_backlog + 1)

如果我们将 somaxconn 设置为 64,tcp_max_syn_backlog 设置为 512,而 backlog 设置为 256 时,Accept 队列的最大长度为 64,小于 0.75 倍的 tcp_max_syn_backlog,所以此时 SYN 队列的最大长度为 64。

如果我们将 somaxconn 设置为 1024,tcp_max_syn_backlog 设置为 256,backlog 设置为 512 时,Accept 队列的最大长度为 512,大于 tcp_max_syn_backlog,所以此时 SYN 队列的最大长度为 193。

验证 SYN 队列最大长度

我们可以通过以下方式来验证 SYN 队列的最大长度:

首先我们需要一个简单的 TCP 服务端,它监听 12345 端口,但从不调用 accept() 从 Accept 队列中获取连接。注意将 backlog 修改为你期望的值:

import socket
import timedef start_server(host, port, backlog):server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind((host, port))server_socket.listen(backlog)print(f'Server is listening on {host}:{port}')while True:time.sleep(3)if __name__ == '__main__':start_server('0.0.0.0', 12345, 256)

然后我们需要在服务端进行以下操作:

# 如果你使用的是 Ubuntu,那么需要关闭 SYN Cookie,以便接下来可以看到计数变化
echo 0 > /proc/sys/net/ipv4/tcp_syncookies# 将 somaxconn 和 tcp_max_syn_backlog 设置为你期望的值
echo 64 > /proc/sys/net/core/somaxconn
echo 512 > /proc/sys/net/ipv4/tcp_max_syn_backlog# 为服务端的 eth0 接口设置 200ms 的网络延迟,使 SYN 队列更容易溢出。
#
# 当你不再需要此延迟时,你可以运行以下命令删除它:
# sudo tc qdisc delete dev eth0 root
sudo tc qdisc add dev eth0 root netem delay 200ms

完成以上设置后,运行以下命令启动 TCP 服务器:

python3 ./server.py

另起一个终端窗口,运行以下命令用于观察 SYN 队列是否溢出:

watch -n 1 -d 'netstat -s | grep -i "listen"

在客户端中安装 hping3 工具:

apt-get install hping3

运行以下命令以指定速率发送指定数量的 SYN 报文:

# -S,发送 SYN 报文
# -p <Port>,指定端口
# -c <Count>,发送报文的数量
# -i u100, 以 100us 的间隔发送报文
hping3 -S -p 12345 -c 65 -i u100 <Your Hostname>

如果你使用的是 Ubuntu,并且 somaxconn 等参数的值与以上示例保持一致,那么在运行 hping3 命令后,你将看到 SYN 丢弃计数加 1,因为此时 SYN 队列最大长度为 64。

令改动永久生效

echo 64 > /proc/sys/net/core/somaxconn 和 sysctl -w net.core.somaxconn=64 都只是临时性的改动,一旦用户注销或者系统重启,我们改动就会失效。如果我们确认当前值满足最终期望,那么可以将它们写入 /etc/sysctl.conf

net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096

然后运行 sysctl -p 使改动立即永久生效。

总结

在现代操作系统中, net.ipv4.tcp_syncookies 通常默认为 1,net.ipv4.tcp_abort_on_overflow 则通常默认为 0,所以除了 Accept 队列溢出导致服务端拒绝后续连接请求以外,我们很难直接观察到客户端连接失败的情况。

当 SYN Cookie 机制生效时,虽然 SYN 报文不会再被丢弃,但 TCP 的部分功能可能会受到限制,并且服务端的负载会相应增加。特别在物联网、车联网这类 RTT 较高的场景中,SYN 队列会更加容易溢出。所以及时关注 SYNs Dropped 计数并调整 SYN 队列大小是非常有必要的。

Accept 队列通常在服务端繁忙时更容易溢出,tcp_abort_on_overflow 等于 0 的情况下,较短的 Accept 队列可能会使客户端和服务端过早地进入报文重传,反而增加网络负载。如果 Accept 队列不断溢出,但服务端的 CPU 并未饱和,那么可以适当增大 Accept 队列。

另外,将 SYN 队列和 Accept 队列设置为一个非常非常大的值并不是一件好事。在遭受泛洪攻击或者突发流量导致服务端繁忙时,对这两个队列施加合理的大小限制反而是对服务端的保护。

在后续的博客中,我们将继续带来更多 Linux 系统中影响 EMQX 性能表现的内核参数的优化指南。

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

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

相关文章

Java 面向对象进阶 03 static 注意事项、重新认识main方法(黑马)

案例代码如下&#xff1a; 非静态的方法虚拟机会自带一个关键字&#xff0c;如下&#xff1a; 但是不能手动出来&#xff0c;它的类型就是当前类&#xff1b; 通过输出this&#xff0c;得到当前s1,s2 的地址&#xff1b; 所以在输出时会有隐藏的this&#xff0c;所以this所指向…

Redis -- 单线程模型

失败是成功之母 ——法国作家巴尔扎克 目录 单线程模型 Redis为什么这么快 单线程模型 redis只使用一个线程&#xff0c;处理所有的命令请求&#xff0c;不是说redis服务器进场内部真的就只有一个线程&#xff0c;其实也有多个线程&#xff0c;那就是处理网络和io的线程。 R…

MBR分区转换为GPT分区

这里有一个ecs-test用于测试MBR转换为GPT 新增一块数据盘 将数据盘以MBR分区格式分区 将整块磁盘以mbr形式分区 格式化&#xff0c;挂载等 上传文件&#xff0c;方便测试(以便后续转换格式类型&#xff0c;防止文件丢失) 取消挂载 将MBR转换为GPT 需先下载gdisk yum instal…

前端常见的栈溢出报错

什么是栈溢出&#xff1f; 在前端开发中&#xff0c;栈溢出是指JavaScript引擎执行代码时&#xff0c;调用栈&#xff08;call stack&#xff09;变得太大&#xff0c;超过了浏览器或JavaScript引擎所分配的栈空间&#xff0c;从而导致栈溢出错误。调用栈是一种数据结构&#x…

Spring-集成Web

一、引子 前面我们在Spring集成Junit中为读者引出了Spring善于集成其它框架的优势&#xff0c;而Spring项目不可能仅限于小范围的某个方法的测试&#xff0c;终究会落脚于Web项目上。于是&#xff0c;我们就从这里正式进入Spring集成Web的话题。由于笔者会从原生的Java Web开发…

@Test Annotation属性- dependsOnMethods属性

目录 语法 如果test1&#xff08;&#xff09;失败了怎么办&#xff1f;test2&#xff08;&#xff09;测试会发生什么&#xff0c;因为它依赖于test1&#xff1f; 如果我们在test1&#xff08;&#xff09;方法上不使用Test注释&#xff0c;但在test 2&#xff08;&#xf…

【产业实践】使用YOLO V5 训练自有数据集,并且在C# Winform上通过onnx模块进行预测全流程打通

使用YOLO V5 训练自有数据集,并且在C# Winform上通过onnx模块进行预测全流程打通 效果图 背景介绍 当谈到目标检测算法时,YOLO(You Only Look Once)系列算法是一个备受关注的领域。YOLO通过将目标检测任务转化为一个回归问题,实现了快速且准确的目标检测。以下是YOLO的基…

B2119 删除单词后缀

网址如下&#xff1a; B2119 删除单词后缀 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 算是找了个操作字符串的题来做 可以说C的string类确实是好用&#xff0c;但是还是不够熟悉&#xff0c;我不清楚怎么删去string类的后面几个字符 用C的常规做法是在相应地点设\0&…

校园二手交易小程序|基于微信小程序的闲置物品交易平台设计与实现(源码+数据库+文档)

校园二手交易小程序目录 目录 基于微信小程序的闲置物品交易平台设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、商品信息管理 3、公告信息管理 4、论坛信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕…

R语言基础学习-02 (此语言用途小众 用于数学 生物领域 基因分析)

变量 R 语言的有效的变量名称由字母&#xff0c;数字以及点号 . 或下划线 _ 组成。 变量名称以字母或点开头。 变量名是否正确原因var_name2.正确字符开头&#xff0c;并由字母、数字、下划线和点号组成var_name%错误% 是非法字符2var_name错误不能数字开头 .var_name, var.…

蓝桥杯备战——11.NE555测频

1.分析原理图 我们可以看到&#xff0c;上图就是一个NE555构建的方波发生电路&#xff0c;输出方波频率1.44/2(R8Rb3)C,如果有不懂NE555内部结构&#xff0c;工作原理的&#xff0c;可以到B站学习。实在不懂仿真也行&#xff0c;比如我下面就是仿真结果&#xff1a; 然后就是下…

与数组相关经典面试题

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

su导入3d模型为什么不圆滑---模大狮模型网

在将SU(SketchUp)模型导入到其他3D软件中时&#xff0c;可能会遇到模型没有圆滑的情况。这可能是由于以下原因&#xff1a; 模型导出设置不正确&#xff1a;在导出SU模型时&#xff0c;需要确保正确选择导出设置。例如&#xff0c;在导出为.obj格式时&#xff0c;需要选择正确的…

免费的ChatGPT网站 ( 7个 )

ChatGPT的核心功能是基于用户在输入时的语言或文本生成相应的回复或继续内容。此外&#xff0c;它还能够完成多种任务&#xff0c;如撰写邮件、视频脚本、文案、翻译、代码编写以及撰写论文等。 博主归纳总结了7个国内非常好用&#xff0c;而且免费的chatGPT网站&#xff0c;AI…

Unity 访问者模式(实例详解)

文章目录 实例1&#xff1a;简单的形状与统计访客实例2&#xff1a;游戏对象组件访问者实例4&#xff1a;Unity场景对象遍历与清理访客实例5&#xff1a;角色行为树访问者 访问者模式&#xff08;Visitor Pattern&#xff09;在Unity中主要用于封装对一个对象结构中各个元素的操…

VS之调用程序对DLL中全局变量的使用

接上篇《VS生成C动态链接库DLL》&#xff0c;能够生成DLL&#xff0c;且能调用后&#xff0c;遇到一个问题&#xff0c;即在DLL程序中定义了一些全局变量&#xff0c;应用程序需要使用&#xff0c;本以为可以直接使用&#xff0c;没想到&#xff0c;还是需要设置才可以&#xf…

秦始皇帝陵K0007陪葬坑文物展览与文物预防性保护的璀璨交汇

秦始皇帝陵博物院近日迎来了一场引人注目的展览——“何止秦俑——秦陵苑囿之K0007陪葬坑”。此次展览首次集中展示了K0007陪葬坑出土的别具一格的陶俑、鲜活灵动的青铜水禽等珍贵文物。然而&#xff0c;这些文物的安全展出离不开高科技的监测平台与实时终端的24小时不间断保护…

搜维尔科技:第九届元宇宙数字人大赛,参赛小组报名确认公告!

各位参赛选手大家好&#xff0c;近期已收到新增报名信息如下表&#xff0c;请各位参赛选手确认&#xff0c;如果信息有误或信息不完整请电话联系赛务组工作人员进行更正 随着元宇宙时代的来临&#xff0c;数字人设计成为了创新前沿领域之一。为了提高大学生元宇宙虚拟人角色策划…

Asp.net移除Server, X-Powered-By, 和X-AspNet-Version头

移除X-AspNet-Version很简单,只需要在Web.config中增加这个配置节: <httpRuntime enableVersionHeader"false" />移除Server在Global.asax文件总增加&#xff1a; //隐藏IIS版本 protected void Application_PreSendRequestHeaders() {HttpContext.Current.Res…

熟悉MATLAB 环境

一、问题描述 熟悉MATLAB 环境。 二、实验目的 了解Matlab 的主要功能&#xff0c;熟悉Matlab 命令窗口及文件管理&#xff0c;Matlab 帮助系统。掌握命令行的输入及编辑&#xff0c;用户目录及搜索路径的配置。了解Matlab 数据的特点&#xff0c;熟悉Matlab 变量的命名规则&a…