我最近在做一个 设备实时控制的手机 APP,我在使用 ActionCable 的自带的重联机制时,发现它内部的 websocket 在处理连接超时这一块应该是有问题的。
问题重现:
按照官方文档的做法,创建 Consumer,然后创建好 Channel,在连接正常时,可以工作。现在将 Rails 服务进程停止,此时页面里 ActionCable 检测到 websocket 连接断开,会自己尝试不断的连接 ActionCable server。然后我再将 Rails 服务器启动好,此时 页面里的 Channel 订阅不能按照预期的很快的恢复到正常的可用状态,要过几分钟 (大概) 才能自行重联到 ActionCable 服务器。
ActionCable 的 Javascript 代码:https://github.com/rails/rails/blob/main/actioncable/app/assets/javascripts/actioncable.esm.js
类 ConnectionMonitor 中的重联方法:
reconnectIfStale() {
if (this.connectionIsStale()) {
logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
this.reconnectAttempts++;
if (this.disconnectedRecently()) {
logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
} else {
logger.log("ConnectionMonitor reopening");
this.connection.reopen();
}
}
}
然后看一下 Connection 类的相关方法:
open() {
if (this.isActive()) {
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
return false; // 处于连接中的状态时,代码就不向下执行了。
} else {
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`);
if (this.webSocket) {
this.uninstallEventHandlers();
}
this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
this.installEventHandlers();
this.monitor.start();
return true;
}
}
close({allowReconnect: allowReconnect} = {
allowReconnect: true
}) {
if (!allowReconnect) {
this.monitor.stop();
}
if (this.isOpen()) {
return this.webSocket.close();
}
}
reopen() {
logger.log(`Reopening WebSocket, current state is ${this.getState()}`);
if (this.isActive()) {
try {
return this.close();
} catch (error) {
logger.log("Failed to reopen WebSocket", error);
} finally {
logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
setTimeout(this.open, this.constructor.reopenDelay);
}
} else {
return this.open();
}
}
问题出在这个 isActive 方法上,
isActive() {
return this.isState("open", "connecting");
}
这个“connecting” “正在连接”的状态没有一个可以配置的超时的机制,导致 ActionCable 连接恢复好了后,前端页面还不能及时地恢复到可用的状态,显示“Attempted to open WebSocket, but existing socket is connecting”
你们在使用 ActionCable 时有没有遇到这方面类似的问题,是不是要自己写补丁代码来解决这个问题?我在 Issue 里找,没有发现相关的问题。