Files
layue13andClaude Opus 5.5 b7d6497ca8 perf: WebSocket 经 CDN:wss 改用 BoringSSL、保活 Ping、整合包形态基准
- ProxySessionBenchmark 新增 --profile modded(仿大型整合包的双向包组与 2 MiB 登录突发,逐字节校验)、
  --websocket-tls 1(keytool 一次性证书的 wss)和 --client-coalesce 1。
- wss 监听改用随包附带的 BoringSSL(netty-tcnative-boringssl-static 2.0.84):TLS 占 wss 代理 I/O CPU 的 55%,
  换用后每帧 CPU 在每一对配对测量中都更低(窗口 1 -22%,窗口 8 -36%),帧率 +31%/+34%;其他平台回落 JDK,
  启动日志标明所用实现。jar 由 10.4 MB 增至 16.5 MB。
- 新增 websocket.pingIntervalSeconds(默认 30,0 关闭):升级后代理静默不超过该时长,期间无写出就发空 Ping,
  避免 CDN/负载均衡在安静的登录、选服阶段按空闲超时断开。IdleStateHandler 版本在窗口 8 多耗 7.6% CPU,
  未达规则;改为写时置位标志、每半个间隔检查的实现,JFR 中几乎不可见。
- 文档:WebSocket 接入新增“经 CDN 部署”,更正消息大小描述;记录见
  testing/benchmarks/results/2026-10-23-websocket-cdn.md。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:40:48 +08:00

100 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 运维与验证
本文概括配置入口和启动约束。架构边界见[架构说明](architecture.md);简要项目说明见 [README](../README.md)。实际部署需按自身网络与并发规模验证,验证记录的适用范围见[测试说明](testing.md)。
## 配置和启动
默认配置模板为 [`moonbridge.yml`](../proxy/core/src/main/resources/config/moonbridge.yml)。发行包将示例配置放在 `config/`;自有插件 JAR 可放在 `plugins.directory` 指向的目录。相对 `plugins.directory` 从所选配置文件所在目录解析;`plugins.enabled` 以插件实现类全名为键,空映射不启用外部插件。插件可用设置由各插件在启动时校验。
本地安装发行包并启动:
```powershell
.\gradlew.bat :proxy-core:installDist
cd .\proxy\core\build\install\moonbridge
java -jar moonbridge.jar --validate-config config\moonbridge.yml
java -jar moonbridge.jar --config config\moonbridge.yml
```
`--validate-config` 校验核心 YAML、静态后端注册和服务器列表图标;它不会加载插件,所以不会验证已启用插件的设置、后端连通性或登录流程。未知核心配置键会报错。
采用统一事件模型后,已有配置中的 `plugins.accessTimeoutSeconds` 需要改为 `plugins.eventTimeoutSeconds`;旧键已删除,启动时会被当作未知配置拒绝。
## 控制台命令
在交互式终端中启动时,控制台使用 JLine:提示符固定在底部,日志在上方滚动(警告黄色、错误红色),支持历史(↑↓)和 Tab 补全命令、玩家名、服务器名和日志区域。没有终端时(Docker 日志、systemd、面板控制台)按行读取标准输入,输出为纯文本。控制台输入先交给内置命令,再交给插件注册的控制台命令;插件命令与内置命令同名时,启动日志会警告,该名字在控制台上执行内置命令,玩家仍可使用插件的 `/名字`。
| 命令 | 作用 |
| --- | --- |
| `list` | 按服务器列出在线玩家 |
| `servers` | 每个服务器的地址、在线人数和来源(静态配置、插件或控制通道实例) |
| `player <名字>` | 玩家的 UUID、地址、TCP/WebSocket、所在服务器和在线时长 |
| `status` | 版本、运行时长、网络传输层、监听地址、人数、堆内存,以及最近 10 分钟的加入、离开、拒绝、转服和过载次数 |
| `send <玩家> <服务器>` | 通过正常转服流程把玩家移到另一个服务器 |
| `kick <玩家> [原因]` | 断开玩家,原因省略时为 `Disconnected by an operator.` |
| `loglevel [<区域> <级别>]` | 不带参数时列出区域和当前级别;`loglevel transfer debug` 等运行时修改,重启后恢复 `logback.xml` 的设置 |
| `stop` | 正常关闭,与 Ctrl+C 相同 |
| `help` | 列出以上命令 |
`player` 和 `list` 中的地址不受 `logging.playerAddresses` 影响:该开关只决定日志文件里是否写 IP。`send`、`kick`、`loglevel` 和 `stop` 会以 INFO 写入 `moonbridge.admin`。Ctrl+D 只关闭控制台输入,代理继续运行。
## 从 StrataProxy 迁移
MoonBridge 是破坏性改名,旧包名/API、启动程序和配置文件没有兼容别名。升级时将 `strataproxy` 启动程序改为 `moonbridge`、将 `strataproxy.yml` 迁移为 `moonbridge.yml`,并检查 `dev.strataproxy` Java 导入改为 `dev.moonbridge`。旧配置中的 `plugins.initialPlacementTimeoutSeconds` 改为 `initialRouting.timeoutSeconds`;首次登录入口应显式写入 `initialRouting.servers`,不要依赖注册顺序。
后端宿主插件从 `StrataProxyBackend` 改为 `MoonBridgeBackend`。更新业务插件 `plugin.yml` 的 `depend`、安装新的宿主 JAR,并将旧插件配置迁移到新宿主插件的数据目录。消费者需重新编译;已删除的 `backendChannels()` API 应迁移到 `messaging()`,Bukkit 业务插件改用 `backend` 构件提供的 `BukkitMessagingService`。迁移后的依赖和宿主接法见[后端 Channel 接入](backend-channel-design.md#后端插件接入)。
## 服务器列表
```yaml
status:
motd: "MoonBridge"
maxPlayers: 100
# icon: "server-icon.png"
```
`motd` 是允许换行的纯文本,最多 1024 个 Unicode 码点。`maxPlayers` 是客户端列表展示人数,范围为 1–1,000,000,不影响实际连接上限 `maxConnections`;在线人数来自代理已发布的玩家会话。省略 `status` 时使用上面的默认文本与人数。
可选 `icon` 必须是 64×64 PNG,文件不超过 64 KiB,使用相对配置文件所在目录的路径。完整 STATUS 响应还须满足协议字符串 32,767 UTF-8 字节上限,文本与图标组合过大时配置校验失败。启动和 `--validate-config` 读取并校验图标,错误时停止;运行中的状态查询复用预构造数据,不读磁盘,也不派发插件事件。更改列表配置需要重启。
## 认证、监听和容量边界
- 必须显式设定 `authentication: OFFLINE` 或 `ONLINE_BUNGEE`。随附示例使用 `OFFLINE` 并绑定字面量 `127.0.0.1`。OFFLINE 信任客户端提交的名字;没有显式设置 `allowOfflinePublicAccess: true` 时只允许字面量回环 IP,避免主机名解析改变监听范围。
- 对外服务使用 `ONLINE_BUNGEE`,并将后端限制为代理可达,禁止客户端绕过代理直连;后端需启用兼容的旧版 Bungee 身份转发。会话验证与加密路径有自动化测试,当前正版账号的真实 Mojang 会话验证、UUID 和皮肤签名转发也已完成端到端实测。
- 可选的 `websocket` 段在 TCP `listen` 之外再开一个 WebSocket 玩家监听,供经 CDN 接入的客户端使用;两种入口同时可用,见 [WebSocket 接入](websocket.md)。OFFLINE 模式对 `websocket.listen` 同样要求字面量回环 IP,除非设置 `allowOfflinePublicAccess: true`。
- `maxConnections` 默认 4096,范围为 1–1,000,000;计数包括登录中的客户端,以及尚未完成 WebSocket 升级的连接。达到上限时新连接直接关闭。按部署资源和连接规模配置,不将其解释为后端容量。
- `transport` 选择玩家连接、后端连接和 DNS 使用的网络传输层,默认 `AUTO`:Linux 上用 epoll,macOS 上用 kqueue,其他平台(如 Windows)或原生库无法加载时(如 ARM/x86 以外的架构、musl 发行版)回退到 NIO,启动日志会输出实际选用的传输层。发行包附带 Linux x86_64/aarch64 的 epoll 与 macOS x86_64/aarch64 的 kqueue 原生库,以及供 `wss://` 监听使用的 BoringSSL(上述平台加 Windows x86_64,见 [WebSocket 接入](websocket.md)),启动脚本已加 `--enable-native-access=ALL-UNNAMED`。显式写 `NIO`、`EPOLL` 或 `KQUEUE` 会强制使用对应实现;指定的原生传输层不可用时启动失败,不会静默回退。后端控制通道使用独立的阻塞套接字,不受此项影响。
- 发行包默认关闭 Netty 的内存泄漏检测(`-Dio.netty.leakDetection.level=disabled`):它在每个手写转发/编码路径上都有逐帧开销,合成基准中 burst 场景每帧 CPU 中位数约低 26%(范围有重叠,见[记录](../testing/benchmarks/results/2026-10-05-event-loop-queue-and-netty.md))。代替它的是测试期的 `LeakGate`:`proxy-core` 的每个测试类都以 PARANOID 级别运行,发现未释放的缓冲区即让该类失败,且有自测证明它能抓到故意的泄漏。引用计数的手写位置已集中到 `ByteBufs`、`FrameTransformHandler` 与 Netty 自带的编解码基类里;已评估的手写 release 位置都有测试守护,范围与方法见[验证说明](testing.md)。这个默认值由 `ProxyMain` 在加载 Netty 前设置;需要在某个部署上重新启用时,启动时传入 `java -Dio.netty.leakDetection.level=simple -jar moonbridge.jar`,显式给出的值优先。
- 日志同时写到控制台和 `logs/latest.log`(按天滚动,保留 14 天);`logging.playerAddresses: false` 让玩家事件不带 IP。各级别写什么、怎样只打开某一块的 DEBUG,见[日志策略](logging.md)。
- `plugins.eventTimeoutSeconds` 为统一事件链期限,默认 5 秒,可设为 1–30 秒。玩家准入事件按订阅注册顺序共享一个期限;无订阅者时跳过派发。准入队列容量为 128,最多 1024 个未决请求。队列饱和、超时、异常或空结果都会拒绝当前访问:连接准入阶段关闭 TCP 连接,玩家准入阶段返回通用错误文本;插件可通过显式拒绝结果提供登录断开原因。生命周期通知有独立的单线程 FIFO 队列,容量 128,最多一个事件正在派发;单事件链受同一事件期限约束。过载时尽力丢弃并聚合记录,不影响连接处理。
- 未认证帧上限为 4 KiB;后端 Login Success 后切换到协议 5 PLAY 帧上限。握手/身份验证与后端连接/登录各自有 15 秒期限;`initialRouting.timeoutSeconds` 默认 15 秒,可设为 1–120 秒,覆盖插件异步选服和所有候选服务器的 DNS/TCP 建连。`initialRouting.servers` 是无插件策略时显式配置的有序候选服务器名,最多 16 个;默认空列表会拒绝首次登录,不会从动态注册目录猜一个服务器。有初始选服插件时,该插件的选择完全接管路由,失败不会回退到配置列表。只有发送任何 Minecraft 握手/登录字节前的 DNS/TCP 失败可继续尝试候选;开始后端登录后失败会结束本次登录,不重放协议流量。成功登录后,客户端须在两分钟内完成首次 PLAY/Forge 握手。
- 登录拒绝、插件主动断开以及后端关闭/读失败的已提交前端写入最多排空等待 5 秒。转服切换阶段有 15 秒期限;Forge 客户端切换后目标握手最多等待 30 秒。各期限届满可能使当前登录失败或关闭当前会话。
## 可选后端发现
后端控制通道示例见 [`moonbridge-channel.example.yml`](../proxy/core/src/main/resources/config/moonbridge-channel.example.yml),使用独立监听端口接收实例注册、心跳和双向消息;使用方法与限制见[后端控制通道](backend-channel-design.md)。
Docker 容器间的监听、服务名和地址配置见[配置与部署](backend-channel-design.md#配置与部署);`sh testing/smoke/container-channel.sh` 可运行跨容器烟测。
控制通道默认关闭。启用后,后端实例通过独立 TCP 连接认证和注册;代理不提供 DNS 发现或 HTTP 注册。代理核心没有内置云实例发现或健康探测。
玩家准入事件 API、检查顺序与 Ban 插件边界见[插件接入说明](plugins.md)。连接准入拒绝发生在 Minecraft 协议开始前,因此没有 Login Disconnect 可供显示;玩家准入发生在身份解析后。`ONLINE_BUNGEE` 仍由核心完成会话验证后才发出玩家事件,离线模式的身份由客户端提供且 `authenticated` 为 false。会话通知不保证 Forge 或玩法世界就绪,也不能用于可靠审计或计费。
## 验证命令和现有证据
项目定义的常规验证入口为:
```powershell
.\gradlew.bat check
```
该任务包含插件 API、消息 API/协议、Java 8 后端 SDK、Bukkit 宿主和核心测试,并检查宿主 JAR 的 Java 8 字节码和依赖边界。核心 `check` 还依赖安装发行包后的帮助、版本及配置校验烟测。详细烟测、性能测量方法与历史记录见[验证方法](testing.md)。
现有测试可用于定位行为范围:
- 登录、认证和协议:`proxy/core/src/test/java/dev/moonbridge/core/auth/` 与 `proxy/core/src/test/java/dev/moonbridge/core/protocol/`
- 会话、断开、转服与缓冲:`proxy/core/src/test/java/dev/moonbridge/core/session/`,重点包括 `SessionTransferTest`、`SessionTransferDispatchTest`、`transfer/TransferFrameBufferTest`
- 插件生命周期、访问检查和 API:`proxy/core/src/test/java/dev/moonbridge/core/plugin/PluginHostTest.java`、`proxy/core/src/test/java/dev/moonbridge/core/session/ProxySessionListenerTest.java`、`proxy/core/src/test/java/dev/moonbridge/app/ProxyConfigurationTest.java` 与 `proxy/plugin-api/src/test/`
- 控制通道:`proxy/core/src/test/java/dev/moonbridge/core/control/` 与 `backend/src/test/`
仓库另有本地 Uranium 协议探针和 Prism Forge 客户端记录,见 [`testing/smoke/results`](../testing/smoke/results/2026-09-25-local-uranium.md)、[转服记录](../testing/smoke/results/2026-09-25-local-uranium-transfer.md) 和 [真实客户端记录](../testing/smoke/results/2026-09-25-prism-forge-client.md)。这些记录支持其明确写出的单机环境、软件版本和探针路径。已知也出现过真实 Forge 客户端转服候选后端登录停滞,见[调查记录](../testing/smoke/results/2026-09-26-uranium-login-stalls.md);已有成功样例不构成稳定性或生产验收。