Rails 求助:ActionCable 的 websocket 怎么样处理网络断开重联?

qichunren · 2023年04月11日 · 最后由 glorysnoopy 回复于 2023年04月11日 · 493 次阅读

我最近在做一个 设备实时控制的手机 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 里找,没有发现相关的问题。

我在项目中看到大家用https://github.com/discourse/message_bus 多过 action cable

需要 登录 后方可回复, 如果你还没有账号请 注册新账号