Appearance
WebSocket 接入(网关长连接)
bot.mode: websocket(默认)时,Bngeuit 主动连接 QQ 开放平台网关,由网关推送事件(DISPATCH)。连接、鉴权、心跳、断线重连全部由框架自动处理,插件无需感知连接细节。
工作方式
- 连接:启动时从
GET /gateway/bot获取网关地址与分片配置,建立长连接并完成鉴权 - 心跳:按网关下发的间隔自动发送心跳包维持连接
- 事件:网关推送的事件由框架统一分发,插件用
@SubscribeEvent(Java)或bngeuit.on(...)(脚本)监听 - 就绪:鉴权成功收到网关 READY 事件 → 触发
ReadyEvent(每分片各一次);全部分片就绪后触发插件的onActive()生命周期
心跳
框架按网关下发的间隔自动发送心跳包并处理心跳确认,无需干预。框架同时监测应答:连续 2 个心跳间隔未收到确认即判定连接半开(NAT 超时等场景下 TCP 层无感知),主动断开并立即重连,避免长时间「假在线」导致的隐性丢事件。每次发出与收到确认都会触发一次 HeartbeatEvent(无载荷、不可取消),可用于连接活跃度指示或看门狗:
java
@SubscribeEvent
public void onHeartbeat(HeartbeatEvent e) {
lastActiveAt = System.currentTimeMillis();
}HeartbeatEvent 只在 WebSocket 模式触发,Webhook 模式没有连接心跳。
断线重连(自动,无需干预)
- 连接断开后框架自动重新入队重连,插件无感知
- 可恢复时会话续传(RESUME):携带上次消息序列号补拉断线期间的事件,消息不丢失
- 鉴权失败(如 Token 失效)时自动重置 Token 后重连
- 正常掉线零延迟重连:会话已建立后的断开立即重连,配合 RESUME 补推,掉线多久事件就在网关缓冲多久
- 连接建立失败指数退避:仅当网关/网络不可达(连接根本建立不起来,此时本就无事件可收)时按 5s → 10s → 20s → 40s → 60s 退避重试,避免高频重连风暴;上限 60s,保证网络恢复后大概率仍在网关会话有效期内、RESUME 可成功补推
- 连接半开(心跳连续 2 个间隔无应答)时主动断连并立即重连
多分片
网关可能要求一个机器人维护多条连接分担事件流量(分片)。框架按网关返回的 shards 自动创建全部分片连接并统一调度,单个分片断线只影响该分片、自动恢复——插件层面无任何区别,事件照常收到。
与 Webhook 模式的差异
连接方向、分片、重连机制、就绪事件、接入鉴权均不同,但事件分发到插件的方式完全相同。对比表见 Webhook 模式。
就绪信号(ReadyEvent / onActive)
每个分片连接鉴权成功后,网关推送 READY 事件,框架分发 ReadyEvent(WebSocket 网关专属,每分片各触发一次,多分片时用 getShardId() 区分来源)。
- 事件形式:
ReadyEvent(event/receive/),事件监听器 / 脚本可用@SubscribeEvent/bngeuit.on('ReadyEvent', ...)监听 - 插件推荐方式:实现
Plugin.onActive()—— 该生命周期在两种接入模式统一触发一次(WS 全部分片就绪 / Webhook 回调服务就绪),做「机器人已上线」初始化无需关心接入方式和分片
java
// 方式一:监听事件(每分片各触发一次,多分片时注意防重)
@SubscribeEvent
public void onReady(ReadyEvent e) {
log.info("分片 {} 已就绪", e.getShardId());
}
// 方式二(推荐):插件生命周期(两模式统一,全局只触发一次)
@Override
public void onActive() {
startScheduledTasks(); // 启动定时推送等
}ReadyEvent vs onActive
ReadyEvent 是 WebSocket 网关每分片触发的事件,Webhook 模式不触发(对应的是 WebhookReadyEvent)。Plugin.onActive() 在两种模式下统一触发一次,做上线初始化优先用 onActive()。
