14 - 故障排查
14 - 故障排查
14.1 故障排查流程
14.1.1 标准排查步骤
1. 收集信息
├── 错误日志(latest.log, crash reports)
├── 玩家描述(何时开始?做了什么?)
├── 最近变更(新插件?配置修改?)
└── 系统状态(CPU、内存、磁盘)
2. 定位问题
├── 分析日志中的错误/警告
├── 使用 Spark Profiler 定位性能瓶颈
├── 二分法排查插件冲突
└── 检查配置文件
3. 解决问题
├── 修改配置/代码
├── 更新/移除问题插件
├── 优化 JVM 参数
└── 扩容硬件资源
4. 验证解决
├── 重启服务器
├── 测试问题是否重现
├── 监控性能指标
└── 记录解决方案
14.1.2 日志位置
| 文件 | 路径 | 说明 |
|---|---|---|
| 最新日志 | logs/latest.log | 当前运行日志 |
| 历史日志 | logs/2026-05-10-1.log.gz | 按日期压缩的日志 |
| 崩溃报告 | crash-reports/crash-*.txt | JVM 崩溃报告 |
| GC 日志 | logs/gc.log | GC 活动日志 |
| Spark 报告 | Spark Web URL | 性能分析报告 |
| 插件日志 | plugins/<插件名>/logs/ | 插件独立日志 |
14.2 常见启动错误
14.2.1 Java 版本错误
Error: A JNI error has occurred, please check your installation
Exception in thread "main" java.lang.UnsupportedClassVersionError
原因:Java 版本过低。 解决:
# 检查当前版本
java -version
# 安装正确版本
sudo apt install openjdk-21-jdk
# 设置默认版本
sudo update-alternatives --config java
14.2.2 内存不足
java.lang.OutOfMemoryError: Java heap space
解决:
# 增大 -Xmx 值
java -Xms4G -Xmx4G -jar paper.jar --nogui
# 检查系统可用内存
free -h
# 检查是否设置了合理的内存(不超过物理内存的 80%)
14.2.3 端口被占用
java.net.BindException: Address already in use: bind
解决:
# 查找占用端口的进程
sudo lsof -i :25565
sudo ss -tlnp | grep 25565
# 终止占用进程
kill <PID>
# 或修改 server.properties 使用其他端口
server-port=25566
14.2.4 EULA 未同意
You need to agree to the EULA in order to run the server.
Go to eula.txt for more info.
解决:
sed -i 's/eula=false/eula=true/' eula.txt
14.2.5 磁盘空间不足
java.io.IOException: No space left on device
解决:
# 检查磁盘使用
df -h
# 查找大文件
du -h /opt/minecraft/paper/ | sort -rh | head -20
# 清理日志
find /opt/minecraft/paper/logs -name "*.log.gz" -mtime +30 -delete
# 清理旧的世界备份
find /opt/minecraft/backups -name "*.tar.gz" -mtime +30 -delete
# 清理区块缓存
rm -rf /opt/minecraft/paper/world/region/*.mca.tmp
14.3 运行时错误
14.3.1 插件加载失败
[Server thread/ERROR]: Could not load 'plugins/OldPlugin.jar' in folder 'plugins'
org.bukkit.plugin.InvalidPluginException: java.lang.NoSuchMethodException
排查步骤:
# 1. 检查插件是否兼容当前 PaperMC 版本
grep "api-version" /opt/minecraft/paper/plugins/OldPlugin.jar
# 2. 检查依赖
unzip -p plugins/OldPlugin.jar plugin.yml | grep -i "depend\|softdepend"
# 3. 查看完整错误栈
grep -A 50 "Could not load" logs/latest.log
# 4. 更新插件到最新版本
14.3.2 插件运行时异常
[Server thread/ERROR]: Could not pass event PlayerJoinEvent to MyPlugin v1.0
java.lang.NullPointerException: Cannot invoke method on null object
排查步骤:
# 1. 定位错误的插件和行号
grep "Could not pass event" logs/latest.log
# 2. 检查插件配置文件是否正确
cat plugins/MyPlugin/config.yml
# 3. 检查依赖插件是否已安装
/plugins
# 4. 向插件作者报告错误(附完整栈信息)
14.3.3 内存泄漏
# 表现:服务器运行时间越长,内存占用越高,最终 OOM
# 使用 Spark 诊断
# 1. 查看内存使用趋势
/spark healthmemory
# 2. 启用堆分析
/spark heapdump
# 3. 检查 GC 日志
grep -c "Full GC" logs/gc.log
# Full GC 频繁说明可能有内存泄漏
# 4. 使用 VisualVM 分析堆转储
14.3.4 TPS 下降
# 使用 Spark 快速诊断
/spark tps
/spark health
# 启动 Profiler 找出耗时操作
/spark profiler --timeout 120
# 常见原因:
# 1. 实体数量过多
/paper entity list
# 2. 红石机器
# 大型红石机器是 TPS 杀手
# 3. 区块加载
/spark healthchunks
# 4. 插件问题
/spark healthplugins
14.4 崩溃分析
14.4.1 崩溃报告分析
# 崩溃报告位置
ls -la /opt/minecraft/paper/crash-reports/
# 查看最新崩溃报告
less /opt/minecraft/paper/crash-reports/crash-*-server.txt
崩溃报告关键信息:
---- Minecraft Crash Report ----
// Description: Watching Server thread
Time: 2026-05-10 14:30:00
Description: Watching Server thread
java.lang.Error: ServerWatchdog
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:XXX)
...
Thread: Server Watchdog
...
-- System Details --
Details:
Minecraft Version: 1.21.4
Operating System: Linux (amd64) version 6.1.0
Java Version: 21.0.4
Memory: 3.2 GB / 4.0 GB
Loaded Plugins: 25
14.4.2 常见崩溃原因
| 错误 | 原因 | 解决方案 |
|---|---|---|
OutOfMemoryError | 内存不足 | 增大 -Xmx,检查内存泄漏 |
StackOverflowError | 递归过深 | 检查插件逻辑 |
ConcurrentModificationException | 并发修改 | 插件线程安全问题 |
Watchdog 超时 | 主线程卡死 | Spark Profiler 分析 |
ClassCastException | 类型转换错误 | 插件 API 版本不兼容 |
NoClassDefFoundError | 缺少依赖类 | 检查插件依赖 |
14.4.3 Watchdog 超时
Description: Watching Server thread
java.lang.Error: ServerWatchdog
说明:Server Thread 超过 60 秒无响应,Watchdog 强制关闭服务器。
排查:
# 1. 查看崩溃时的线程栈
# 在崩溃报告中找到 "Thread State" 部分
# 2. 使用 Spark 检查主线程阻塞原因
/spark profiler --timeout 60
# 3. 检查是否有长时间运行的 tick
# 在日志中搜索 "Running" "tick" "took"
grep -i "running\|tick.*took\|overloaded" logs/latest.log
14.4.4 自动重启配置
# 使用启动脚本实现自动重启
#!/bin/bash
# start-with-restart.sh
while true; do
echo "[$(date)] 启动服务器..."
java -Xms4G -Xmx4G ... -jar paper.jar --nogui
EXIT_CODE=$?
echo "[$(date)] 服务器退出,状态码: ${EXIT_CODE}"
if [ "$EXIT_CODE" -eq 0 ]; then
echo "正常关闭,不再重启"
break
fi
echo "[$(date)] 10 秒后重启..."
sleep 10
done
Systemd 也支持自动重启:
[Service]
Restart=on-failure
RestartSec=10
14.5 网络问题
14.5.1 连接超时
# 1. 检查服务器是否在运行
systemctl status minecraft
# 2. 检查端口监听
ss -tlnp | grep 25565
# 3. 检查防火墙
sudo ufw status
sudo iptables -L -n
# 4. 检查路由器端口转发
# 在外部使用 nc 测试
nc -zv your_public_ip 25565
14.5.2 玩家频繁断开
# 检查日志中的断开原因
grep "lost connection\|disconnect\|timed out" logs/latest.log
# 常见原因:
# - 网络不稳定
# - 服务器过载
# - 插件踢出
# - 客户端版本不匹配
14.5.3 “Connection refused” 错误
# 原因:服务器未运行或端口未监听
# 检查服务器状态
systemctl status minecraft
# 检查端口
ss -tlnp | grep 25565
# 检查日志
tail -100 logs/latest.log
14.6 世界数据问题
14.6.1 区块损坏
Chunk file at r.X.X.mca is missing root-level compound tag
解决:
# 1. 使用 MCA Selector 定位损坏区块
java -jar mcaselector.jar --mode analyze --world world
# 2. 删除损坏的区块文件
# 找到对应区域文件并删除(区块会重置为原始状态)
rm world/region/r.X.X.mca
# 3. 从备份恢复
tar -xzf backup.tar.gz world/region/r.X.X.mca
14.6.2 level.dat 损坏
# 使用备份的 level.dat
cp world/level.dat_old world/level.dat
# 或从完整备份恢复
tar -xzf backup.tar.gz world/level.dat
14.6.3 玩家数据丢失
# 检查玩家数据文件
ls -la world/playerdata/
# 从 CoreProtect 恢复(如果有记录)
/co restore u:<UUID> t:24h
# 从备份恢复
tar -xzf backup.tar.gz world/playerdata/<UUID>.dat
14.7 插件问题
14.7.1 插件冲突排查(二分法)
# 1. 移走一半插件
mkdir plugins-disabled
mv plugins/[A-M]*.jar plugins-disabled/
# 2. 重启服务器
# 3. 检查问题是否消失
# 4. 如果问题仍在,继续移走剩余的一半
# 5. 如果问题消失,冲突在移走的那半中
# 6. 重复直到定位到具体插件
14.7.2 插件版本不兼容
java.lang.NoSuchMethodError: 'void org.bukkit.Server.someMethod()'
解决:
# 更新插件到支持当前 PaperMC 的版本
# 或降级 PaperMC 到插件支持的版本(不推荐)
14.7.3 数据库连接问题
[MyPlugin] Failed to connect to database: Connection refused
解决:
# 1. 检查数据库服务
systemctl status mysql
# 2. 检查连接配置
cat plugins/MyPlugin/config.yml
# 3. 测试连接
mysql -u minecraft -p -h localhost luckperms
# 4. 检查防火墙
sudo ufw allow 3306/tcp
14.8 性能问题诊断
14.8.1 服务器卡顿诊断流程
服务器卡顿
├── 1. 检查 TPS
│ └── /spark tps
├── 2. 如果 TPS < 20
│ ├── 检查实体数量 /paper entity list
│ ├── 检查 Spark Profiler
│ ├── 检查红石机器
│ └── 检查区块加载
├── 3. 如果 TPS = 20 但玩家感觉卡
│ ├── 检查网络延迟 /ping
│ ├── 检查客户端 FPS
│ └── 检查视距设置
└── 4. 检查系统资源
├── CPU: htop
├── 内存: free -h
└── 磁盘: iotop
14.8.2 常见性能问题
| 表现 | 可能原因 | 解决方案 |
|---|---|---|
| TPS 低 | 实体过多 | 限制怪物生成,清理掉落物 |
| TPS 低 | 红石机器 | 限制红石时钟频率 |
| TPS 低 | 区块加载 | 降低视距 |
| 内存高 | 泄漏 | Spark heapdump 分析 |
| 启动慢 | 插件过多 | 禁用不必要插件 |
| 卡顿 | GC 暂停 | 优化 JVM 参数 |
| 卡顿 | 磁盘慢 | 升级 SSD |
| 断开 | 网络问题 | 检查防火墙和带宽 |
14.8.3 使用 Spark 生成诊断报告
# 生成完整的诊断报告
/spark health
# 输出包含:
# - TPS 历史
# - MSPT 历史
# - 内存使用
# - 实体数量
# - 区块数量
# - GC 活动
# 导出到文件
/spark health --output-file diagnostics.txt
14.9 日志分析技巧
14.9.1 常用 grep 命令
# 查看所有错误
grep -i "error" logs/latest.log
# 查看插件相关错误
grep "plugin" logs/latest.log | grep -i "error\|warn"
# 查看踢出记录
grep "kicked\|disconnect" logs/latest.log
# 查看内存相关警告
grep -i "memory\|heap\|oom\|gc" logs/latest.log
# 查看性能警告
grep -i "overloaded\|tick.*took\|running behind" logs/latest.log
# 查看玩家活动
grep "joined\|left\|logged" logs/latest.log
# 查看特定时间段的日志
grep "14:3[0-9]" logs/latest.log
14.9.2 使用 awk 进行复杂分析
# 统计每小时的错误数量
grep "ERROR" logs/latest.log | \
awk -F'[][]' '{print $2}' | \
cut -d: -f1 | sort | uniq -c
# 统计最活跃的插件
grep "plugin" logs/latest.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn | head -10
# 查看 TPS 变化趋势
grep "TPS" logs/latest.log | \
awk '{print $1, $2, $NF}'
14.9.3 日志轮转
# Paper 日志配置
# config/paper-global.yml
logging:
# 使用 Log4j 配置自定义日志轮转
# log4j2.xml 可以配置日志大小和保留数量
<!-- log4j2.xml 配置示例 -->
<Configuration>
<Appenders>
<RollingRandomAccessFile name="File"
fileName="logs/latest.log"
filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="100MB"/>
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
14.10 故障排查清单
启动失败检查清单
- Java 版本是否正确?
java -version - 内存是否足够?
free -h - EULA 是否同意?
cat eula.txt - 端口是否被占用?
ss -tlnp | grep 25565 - 磁盘是否有空间?
df -h - 文件权限是否正确?
ls -la - 插件是否兼容?检查日志
运行时问题检查清单
- TPS 是否正常?
/spark tps - 内存是否充足?
/spark healthmemory - 实体是否过多?
/paper entity list - 最近是否修改了配置?检查 git log
- 是否更新了插件?检查 plugins 目录
- 系统资源是否正常?
htop
性能问题检查清单
- JVM 参数是否使用 Aikar’s Flags?
- 视距是否合理?view-distance
- 实体激活范围是否合理?
- 是否有大型红石机器?
- 插件是否有性能问题?
/spark healthplugins - 磁盘 IO 是否正常?
iotop
14.11 本章小结
| 要点 | 说明 |
|---|---|
| 日志是排查问题的金钥匙 | latest.log 和 crash-reports |
| Spark 是最强诊断工具 | Profiler + Heap Dump + Health |
| 二分法排查插件冲突 | 最有效的冲突定位方法 |
| Watchdog 超时需要 Profiler 分析 | 找出主线程阻塞原因 |
| 自动重启减少停服时间 | 启动脚本或 systemd |
| 记录故障处理过程 | 积累经验,建立知识库 |