尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux环境下JMeter生产级压测平台搭建与性能调优实战指南

Linux环境下JMeter生产级压测平台搭建与性能调优实战指南 1. 项目概述为什么要在Linux上做生产级压测如果你做过性能测试大概率在Windows上用过JMeter点点图形界面跑跑脚本感觉还挺方便。但当你真正要把压测任务搬到生产环境或者需要模拟大规模并发时Windows下的JMeter图形界面GUI模式就显得力不从心了。内存消耗大、资源占用高、稳定性差稍微跑久一点就可能卡死或内存溢出OOM。这时候把压测引擎部署到Linux服务器上以非图形界面Non-GUI模式运行就成了性能测试工程师的“标准操作”。这个实战指南就是为你系统梳理在Linux环境下从零开始搭建一个可用于生产级性能压测的JMeter实战平台的全过程。所谓“生产级”意味着它不再是单次性的验证而是需要满足可重复、可监控、资源可控、结果可靠等一系列严苛要求。我们将绕过那些简单的“安装-运行”教程直接切入企业级实战中你会遇到的核心问题如何选择服务器配置如何编写易于维护的脚本如何高效地分发和管理测试数据如何监控服务器资源并避免压测机自身成为瓶颈以及当测试结束后如何从海量的结果数据中快速定位性能瓶颈我会结合自己多次搭建压测集群、处理TB级测试数据的经验把每一步的操作意图、背后的原理以及最容易踩坑的地方讲清楚。无论你是刚开始接触Linux压测的新手还是想优化现有流程的老手这篇指南都能提供可直接“抄作业”的完整路径。2. 压测环境规划与核心资源评估在动手安装任何软件之前规划是重中之重。盲目找台服务器就开干很可能在压测中途因为资源不足而失败或者得到失真的测试结果。2.1 压测机Slave资源估算压测机是实际产生并发请求的机器。它的资源需求直接取决于你的压测场景。1. 核心考量因素并发用户数Threads这是最直接的指标。但要注意JMeter一个线程模拟一个用户线程数不等于服务器需要支持的进程/线程数。JMeter自身运行需要开销。吞吐量目标TPS/RPS目标每秒事务数或请求数。高吞吐量意味着CPU需要更频繁地进行网络I/O、协议处理和结果计算。脚本复杂度脚本中是否包含大量的前置处理器如JSON提取、后置处理器如正则表达式提取、断言逻辑这些都会消耗CPU。响应数据大小如果每个请求的响应体都很大例如几MB的JSON那么网络带宽和内存消耗会急剧增加。2. 经验性估算公式仅供参考一个比较粗糙但实用的起点是每500-1000个并发线程处理常规HTTP请求建议分配1个CPU核心和1-2GB的堆内存。对于计算密集型或处理大响应的脚本这个比例需要降低。例如计划模拟5000个并发用户CPU建议至少4-8核。因为JMeter是Java应用其性能受JVM垃圾回收GC影响多核有利于分散GC压力和应用线程。内存建议8-16GB。这不仅是给JMeter堆内存如-Xms4g -Xmx8g还要为操作系统、JMeter进程本身、以及可能用到的缓存如测试数据文件留出空间。网络至少千兆1Gbps网卡。计算一下如果目标TPS是1000平均响应体大小为50KB那么单向流量就是 1000 * 50KB/s ≈ 48.8MB/s已经接近400Mbps的带宽了这还没算上请求的流量。所以万兆10Gbps网络对于高吞吐量场景是必要的。磁盘至少50GB SSD。主要用于存放JMeter安装包、测试脚本JMX、测试数据文件CSV和结果文件JTL。结果文件在压测期间会快速增长。注意绝对不要用同一台压测机同时作为控制器Controller和生成器Slave除非测试规模非常小。控制器的资源消耗虽然不大但它需要协调所有Slave如果它卡住了整个测试就乱了。建议使用一台独立的、配置稍低的机器作为控制器。2.2 网络与拓扑设计生产级压测通常意味着需要多台压测机Slave来共同产生负载这就需要组建一个JMeter分布式集群。1. 集群角色控制器Controller一台机器。负责运行JMeter GUI用于脚本调试和启动测试或非GUI模式并控制整个测试流程。它向所有Slave发送指令、脚本和测试数据并汇总结果。生成器/压测机Slave/Agent多台机器。接收Controller的指令执行测试脚本真实地向被测系统SUT发送请求并将原始结果数据回传或写入本地。2. 网络要求Slave与Controller之间需要双向通信。Controller通过RMIRemote Method Invocation协议向Slave发送指令。必须确保相关端口默认1099在防火墙中是开放的并且Slave能解析到Controller的主机名或IP。Slave与被测系统SUT之间需要有低延迟、高带宽的网络连接。理想情况下压测集群和SUT应处于同一个数据中心或可用区以排除公网不稳定性的干扰。Controller与结果存储/监控系统之间如果实时监控结果需要网络连通。3. 一个典型的部署拓扑[Controller机器] (运行JMeter 管理测试) | | (RMI over TCP/IP) | [Slave 1] --- [被测系统 (SUT)] [Slave 2] --- [被测系统 (SUT)] [Slave 3] --- [被测系统 (SUT)]所有Slave的流量汇聚到被测系统。Controller只做管理不产生压力。2.3 Linux发行版与基础环境选择对于JMeter压测机Linux发行版的选择原则是稳定、干净、资源占用少。推荐CentOS 7/8 Stream、Rocky Linux、AlmaLinux或Ubuntu Server LTS。这些系统有长期支持社区资源丰富且默认服务较少更适合做单纯的压测引擎。不推荐在压测机上安装图形化界面如GNOME, KDE这会浪费宝贵的内存和CPU资源。基础环境JavaJMeter 5.x 版本需要Java 8 或 11。建议安装OpenJDK。在Ubuntu上可以用sudo apt install openjdk-11-jdk在CentOS/Rocky上可以用sudo yum install java-11-openjdk-devel。安装后务必用java -version验证。必要工具确保安装wget下载JMeter、vim或nano编辑配置、net-toolsifconfig,netstat、htop监控资源等工具。3. JMeter在Linux上的部署与关键配置部署不仅仅是解压安装包更重要的是进行一系列优化配置以适应高并发压测的需求。3.1 安装与目录规划1. 下载与安装建议直接从Apache官网下载最新的二进制包.tgz格式避免通过包管理器安装可能带来的版本滞后或配置差异。# 以JMeter 5.6.2为例进入你规划的安装目录如 /opt cd /opt sudo wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.2.tgz sudo tar -xzf apache-jmeter-5.6.2.tgz sudo ln -s apache-jmeter-5.6.2 jmeter # 创建软链接便于版本管理2. 目录结构规划一个清晰的目录结构能极大提升脚本管理和执行的效率。我建议在JMeter主目录外建立以下工作目录/opt/jmeter/ # JMeter主程序 /home/jmeter-user/performance/ # 工作目录 ├── scripts/ # 存放所有JMX测试脚本 ├── data/ # 存放CSV等参数化数据文件 ├── results/ # 存放每次压测的原始结果JTL和HTML报告 │ ├── 20240520_LoadTest_Checkout/ │ └── 20240521_StressTest_Login/ ├── lib/ # 存放自定义的Jar包如特定协议的插件 └── logs/ # 存放JMeter运行日志非结果日志通过环境变量JMETER_HOME和修改jmeter.properties中的相关路径可以让JMeter指向这些自定义目录。3.2 JVM内存与GC优化配置这是影响JMeter稳定性和性能的最关键步骤。默认配置完全无法应对生产级压测。配置文件位于$JMETER_HOME/bin/目录下主要是jmeterUnix启动脚本或jmeter.batWindows但我们更应关注jmeter.sh或直接修改jmeter脚本中的JVM参数部分。1. 找到并修改JVM参数编辑$JMETER_HOME/bin/jmeter文件找到类似HEAP的设置部分。通常是一行HEAP-Xms1g -Xmx1g -XX:MaxMetaspaceSize256m2. 生产级配置建议根据之前估算的资源进行调整。例如对于一台16GB内存的Slave# 修改为 HEAP-Xms8g -Xmx12g -XX:MaxMetaspaceSize512m SERVER_HEAP-Xms8g -Xmx12g -XX:MaxMetaspaceSize512m # 分布式Slave模式专用参数-Xms 和 -Xmx分别设置JVM堆内存的初始大小和最大大小。强烈建议将两者设为相同值以避免运行时堆内存动态调整带来的性能波动和GC停顿。最大值-Xmx不应超过机器物理内存的70%要为操作系统和其他进程留出空间。-XX:MaxMetaspaceSize设置元空间上限存放类的元数据。默认较小在大规模测试或使用很多插件时可能溢出适当调大。3. 垃圾回收器GC优化对于高吞吐、低延迟要求的压测客户端推荐使用G1垃圾回收器。# 在HEAP参数后追加GC参数 HEAP-Xms8g -Xmx8g -XX:MaxMetaspaceSize512m -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:G1ReservePercent20-XX:UseG1GC启用G1回收器。-XX:MaxGCPauseMillis200设置期望的最大GC停顿时间目标毫秒。这是一个软目标JVM会尽力达成。-XX:G1ReservePercent20设置堆内存保留百分比降低晋升失败的风险。实操心得调整JVM参数后务必在非GUI模式下进行一轮试压并用jstat -gc pid命令观察GC情况。如果看到频繁的Full GCFGC说明堆内存或GC配置可能仍有问题。压测机的GC停顿会导致请求发送出现“毛刺”影响测试结果的准确性。3.3 系统级参数调优为了让Linux系统能支持JMeter产生的大量网络连接需要修改一些内核参数。编辑/etc/sysctl.conf文件。# 增加或修改以下参数 net.ipv4.tcp_tw_reuse 1 # 允许将TIME-WAIT sockets重新用于新的TCP连接 net.ipv4.tcp_tw_recycle 0 # 不建议启用在NAT环境下可能有问题 net.ipv4.ip_local_port_range 1024 65535 # 扩大本地端口范围 net.ipv4.tcp_fin_timeout 30 # 减少FIN-WAIT-2状态超时时间 net.ipv4.tcp_max_syn_backlog 8192 # 增大SYN队列长度 net.core.somaxconn 8192 # 增大监听队列长度 net.core.netdev_max_backlog 5000 # 每个网络接口接收数据包的速率比内核处理快时允许的最大队列数 vm.swappiness 10 # 降低系统使用交换分区swap的倾向保持内存活跃保存后执行sudo sysctl -p使配置生效。文件描述符限制JMeter每个线程可能打开多个连接如HTTP连接、结果文件句柄。需要提高单个进程和系统总体的文件描述符限制。编辑/etc/security/limits.conf在文件末尾添加* soft nofile 65536 * hard nofile 65536 jmeter-user soft nproc 65536 # 将jmeter-user替换为运行JMeter的系统用户名 jmeter-user hard nproc 65536重新登录后生效可通过ulimit -n和ulimit -u验证。4. 生产级测试脚本设计与数据管理在Linux非GUI环境下脚本的健壮性、可维护性和低资源消耗特性至关重要。4.1 脚本模块化与最佳实践1. 使用测试片段Test Fragment和模块控制器Module Controller将通用的逻辑如登录、登出、数据准备封装成测试片段。在主场景中通过模块控制器进行调用。这样不仅脚本结构清晰修改通用逻辑时只需改动一处。2. 禁用不必要的监听器Listener在GUI模式下设计脚本时我们习惯添加“查看结果树”、“聚合报告”等监听器来调试。但在执行压测时必须禁用或删除这些监听器因为它们会在内存中保存每个请求的详细信息在高压下会迅速耗尽内存。只需保留一个简单的“聚合报告”或“概要报告”用于验证脚本启动即可最终结果应通过命令行参数指定文件输出。3. 合理使用定时器Timer固定定时器Constant Timer用于在请求间插入固定停顿模拟用户思考时间。高斯随机定时器Gaussian Random Timer更符合真实用户行为停顿时间在一定范围内随机分布。同步定时器Synchronizing Timer用于制造瞬间并发模拟秒杀场景。注意同步定时器会阻塞线程直到达到指定的并发用户数才释放这会严重影响吞吐量TPS的计算。通常只在特定场景下使用并且分析结果时需要特别注意。4. 参数化与变量管理CSV数据文件对于大量、可重复使用的测试数据如用户名、商品ID这是标准做法。在Linux下确保CSV文件路径正确并使用相对路径或通过-J命令行参数传递。用户自定义变量User Defined Variables用于配置全局参数如服务器地址、端口、协议等。便于在不同环境测试、预生产间切换。属性Properties比变量更灵活可以在命令行动态传入。例如在脚本中用${__P(threadNum, 50)}来引用线程数然后在命令行用-JthreadNum200来覆盖。4.2 高效数据准备与分发策略当使用多台Slave进行分布式压测时数据文件的分发是个挑战。方案一共享存储推荐用于大型、静态数据NFS在Controller上设置NFS服务将数据目录共享给所有Slave。Slave挂载该目录。优点是数据集中管理更新方便。缺点是网络I/O可能成为瓶颈且存在单点故障。操作步骤简述Controller安装NFS服务sudo yum install nfs-utils(CentOS) 或sudo apt install nfs-kernel-server(Ubuntu)。编辑/etc/exports添加/home/jmeter-user/performance/data *(rw,sync,no_root_squash)。重启服务sudo systemctl restart nfs-server。在每台Slave上安装NFS客户端创建本地挂载点并挂载sudo mount -t nfs controller_ip:/home/jmeter-user/performance/data /mnt/jmeter-data。在JMeter脚本中CSV文件路径指向挂载点如/mnt/jmeter-data/users.csv。方案二同步工具推荐用于中小型数据或需要预分发rsync在测试开始前从Controller将数据文件同步到所有Slave的本地目录。优点是压测时读取本地文件速度快。缺点是数据更新需要额外同步步骤。操作步骤可以写一个简单的shell脚本在启动JMeter Slave前执行。# sync_data.sh #!/bin/bash SLAVES(slave1_ip slave2_ip slave3_ip) LOCAL_DATA_PATH/home/jmeter-user/performance/data REMOTE_DATA_PATH/home/jmeter-user/performance/data for slave in ${SLAVES[]} do rsync -avz -e ssh $LOCAL_DATA_PATH/ jmeter-user$slave:$REMOTE_DATA_PATH/ done方案三内置到JMX中仅适用于极小量数据对于少量参数可以直接使用JMeter的“用户参数”或“随机变量”前置处理器避免文件I/O。4.3 命令行执行与结果收集这是Linux下运行JMeter的核心方式。非GUI模式-n资源消耗极低。1. 基本命令# 在Controller上执行调用远程Slave $JMETER_HOME/bin/jmeter -n -t /path/to/your_test.jmx -l /path/to/results.jtl -j /path/to/jmeter.log -e -o /path/to/html_report_folder -R slave1_ip:1099,slave2_ip:1099 # 参数解释 # -n: 非GUI模式 # -t: 指定测试脚本JMX文件 # -l: 指定结果文件JTL格式 # -j: 指定JMeter运行日志文件 # -e: 测试结束后生成HTML报告 # -o: 指定HTML报告输出目录必须为空或不存在的目录 # -R: 指定远程Slave的IP和端口列表用逗号分隔2. 动态覆盖属性jmeter -n -t test.jmx -l result.jtl -JthreadNum500 -JrampUp60 -Jduration300在脚本中使用${__P(threadNum)}来获取这些值。3. 结果文件JTL管理JTL文件会记录每一个样本请求的详细信息。在高并发长时压测下文件可能非常大几十GB。确保结果存储目录有足够磁盘空间。建议为每次压测生成带有时间戳和场景名称的唯一结果文件例如20240521_1400_Stress_Login.jtl。生成HTML报告-e -o参数非常有用它提供了一个直观的仪表盘。但生成过程会消耗较多CPU和内存对于超大型JTL文件建议在测试结束后用另一台分析机器来生成jmeter -g result.jtl -o report_folder。5. 分布式压测集群搭建与运维单机能力总有上限分布式压测是模拟超大规模并发的唯一途径。5.1 Slave节点配置与启动1. 配置Slave机器在每台Slave机器的$JMETER_HOME/bin/目录下编辑jmeter.properties文件。# 找到并修改以下关键属性 server_port1099 # RMI端口保持默认或自定义 server.rmi.localport40000 # 可自定义避免冲突 server.rmi.ssl.disabletrue # 为简化先禁用SSL此外需要确保Slave机器上的JMeter版本、插件、测试数据如果不同步与Controller一致。2. 启动Slave服务在每台Slave机器上运行$JMETER_HOME/bin/jmeter-server -Djava.rmi.server.hostnameslave_ip_address关键参数-Djava.rmi.server.hostname必须设置为Slave机器对外可访问的IP地址。如果这里设置成127.0.0.1或localhostController将无法连接到它。这是分布式搭建中最常见的坑。3. 验证Slave启动查看Slave上的日志默认输出到控制台或jmeter-server.log看到类似Server failed to start: could not find ApacheJMeter_core.jar的错误通常是JMETER_HOME环境变量未正确设置。看到Starting the test on host slave_ip:1099则说明启动成功等待Controller指令。5.2 Controller集中管理与测试启动1. 修改Controller配置在Controller机器的jmeter.properties中指定所有Slave的地址remote_hosts192.168.1.101:1099,192.168.1.102:1099,192.168.1.103:1099 # 或者可以在命令行中用 -R 参数动态指定优先级更高。2. 启动分布式测试在Controller上执行前面提到的命令行使用-R参数。Controller会依次连接所有Slave发送脚本、数据如果路径是共享的或已同步然后统一开始测试。3. 监控Slave状态在Controller的GUI模式下仅用于管理可以通过“运行”菜单 - “远程启动”来单独或批量启动Slave上的测试也能看到远程Slave的状态。在非GUI模式下主要通过日志和系统监控来观察。Controller的日志会显示与每个Slave的连接和指令发送情况。5.3 资源监控与瓶颈排查压测过程中必须实时监控压测机Slave本身的资源使用情况确保它们不是瓶颈。1. 基础监控命令htop直观查看CPU、内存、线程实时使用情况。比top更友好。vmstat 2每2秒输出一次系统进程、内存、分页、块IO、CPU活动等信息。iostat -xz 2监控磁盘I/O状况查看是否因写入结果文件JTL导致磁盘瓶颈。sar -n DEV 2监控网络接口吞吐量rxkB/s, txkB/s确认网络是否打满。netstat -an | grep :1099 | wc -l查看JMeter RMI端口的连接数。2. JVM监控jps列出Java进程找到JMeter的PID。jstat -gcutil pid 2s每2秒输出一次JVM垃圾回收统计信息重点关注O老年代使用率和FGCFull GC次数。如果O持续很高且FGC频繁说明堆内存可能不足或存在内存泄漏。jmap -heap pid查看堆内存详细配置和使用情况生产环境慎用可能触发STW。3. 编写监控脚本可以写一个简单的Shell脚本定期收集上述指标并输出到文件便于后续分析。#!/bin/bash # monitor_slave.sh INTERVAL5 LOG_FILEslave_monitor_$(date %Y%m%d_%H%M%S).log echo Timestamp, CPU(%), Memory(%), LoadAvg, NetRx(KB/s), NetTx(KB/s), JVM-O, JVM-FGC $LOG_FILE while true; do TS$(date %Y-%m-%d %H:%M:%S) CPU$(top -bn1 | grep Cpu(s) | awk {print $2} | cut -d% -f1) MEM$(free | grep Mem | awk {printf %.1f, $3/$2 * 100.0}) LOAD$(uptime | awk -Fload average: {print $2} | xargs) NET$(sar -n DEV 1 1 | grep Average: | grep -v lo | awk {rx$5; tx$6} END {printf %.0f,%.0f, rx/2, tx/2}) # 假设JMeter进程PID已知这里需要替换为实际获取PID的逻辑例如 pgrep -f jmeter-server PID$(pgrep -f jmeter-server) if [ ! -z $PID ]; then JVM_STAT$(jstat -gcutil $PID | tail -1) O$(echo $JVM_STAT | awk {print $4}) FGC$(echo $JVM_STAT | awk {print $9}) else ON/A FGCN/A fi echo $TS, $CPU, $MEM, $LOAD, $NET, $O, $FGC $LOG_FILE sleep $INTERVAL done6. 结果分析与性能瓶颈定位指南压测结束拿到JTL结果文件和HTML报告真正的分析工作才开始。6.1 核心性能指标解读1. 吞吐量Throughput请求级Requests per Second每秒请求数衡量服务器处理能力。事务级Transactions per Second每秒完成的事务数一个事务可能包含多个请求。TPS是衡量系统性能的核心指标。在聚合报告中Throughput列即是TPS。分析时需关注其曲线是否平稳是否达到预期目标以及在压力下的变化趋势。2. 响应时间Response Time平均值Average参考价值有限容易受极端值影响。中位数Median50%用户的响应时间更能代表典型体验。百分位数90th, 95th, 99th Percentile这是黄金指标。例如P99500ms表示99%的请求响应时间在500毫秒以内。它反映了系统的尾部延迟对用户体验影响巨大。需要重点关注P90、P95、P99在压测过程中的变化。3. 错误率Error %任何非2xx/3xx的HTTP状态码或未通过的断言都会被记为错误。生产级压测要求错误率接近于0%。即使有少量错误也必须逐一分析原因超时、业务逻辑错误、资源不足等。4. 压测机资源监控数据关联分析将6.3中收集的监控数据与JMeter结果的时间轴对齐。例如当TPS下降或响应时间飙升时去看对应时间点的Slave CPU、内存、网络或JVM GC是否出现异常。如果Slave的CPU持续在95%以上那瓶颈很可能在压测机本身而非被测系统。6.2 使用HTML报告与第三方工具深入分析1. JMeter的Dashboard Report使用-e -o生成的HTML报告非常强大。重点关注Over Time图表响应时间、吞吐量随时间的变化趋势。寻找拐点或毛刺。Throughput vs Threads图表吞吐量随并发用户数增长的变化。理想情况是线性增长后趋于平稳。如果过早平稳或下降说明系统存在瓶颈。Response Time Percentiles Over Time图表各百分位响应时间趋势。观察P99是否与其他曲线分离分离越大说明延迟分布越不均匀。2. 使用JTL文件进行自定义分析JTL是CSV格式可以导入到数据库如InfluxDB或数据分析工具如Grafana, Python Pandas中进行更灵活的分析。# 示例用awk快速计算某个API的平均响应时间和P99 awk -F, {if ($4HTTP Request $9200) {sum$2; count; arr[count]$2}} END {asort(arr); print Avg:, sum/count, ms; print P99:, arr[int(count*0.99)], ms} result.jtl3. 关联系统监控如Grafana如果被测系统SUT有完善监控如通过Prometheus收集的服务器指标、应用指标、数据库指标将JMeter的测试结果时间轴与这些监控仪表盘对齐是定位瓶颈最有效的方法。例如发现TPS下降时数据库的CPU使用率或慢查询数量是否同步飙升。6.3 常见性能瓶颈模式与排查思路根据响应时间、TPS和错误率的变化可以初步判断瓶颈类型模式一TPS随着并发增加而线性增长响应时间平稳判断系统尚未达到瓶颈可以继续增加压力。排查关注压测机资源是否先于被测系统耗尽。模式二TPS达到一个峰值后不再增长甚至下降响应时间急剧上升判断系统达到瓶颈。排查步骤检查被测系统资源CPU、内存、磁盘I/O、网络带宽是否饱和。检查中间件/服务应用服务器Tomcat等线程池是否打满连接池数据库、Redis是否耗尽消息队列是否堆积检查数据库是否存在慢查询锁竞争CPU或IO瓶颈检查代码/配置是否存在同步锁、低效算法、不当的缓存策略JVM Full GC是否频繁模式三错误率突然升高TPS骤降判断系统可能出现了故障或保护机制触发。排查步骤查看错误类型是连接超时、读取超时还是5xx服务器错误检查日志查看被测系统的应用日志寻找异常堆栈信息。检查限流熔断是否触发了网关或微服务的限流、熔断机制检查依赖服务下游的数据库、缓存、第三方接口是否不可用模式四TPS和响应时间周期性波动判断可能与定时任务、缓存失效、垃圾回收GC有关。排查对比波动周期与系统后台任务周期、JVM GC日志中的Full GC周期是否吻合。定位瓶颈是一个“由外到内、由表及里”的过程。从最外层的负载均衡器、到应用服务器、再到数据库和底层存储结合监控数据和日志层层深入才能找到真正的根因。每一次压测不仅是对系统的考验也是对测试人员分析判断能力的锻炼。把每次压测中发现的问题、排查的思路、解决的方法记录下来会成为团队最宝贵的知识库。
返回列表