强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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-*.txtJVM 崩溃报告
GC 日志logs/gc.logGC 活动日志
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
记录故障处理过程积累经验,建立知识库

扩展阅读