ConnectionManager.onChannelMessage() 收到的消息具有不同的 connectionSerial,但与之前的消息 ID 相同;丢弃

ConnectionManager.onChannelMessage() received message with different connectionSerial, but same message id as a previous; discarding

将 Ably Realtime 用于基于 Web 的预订系统。

我在 js 控制台中不断收到一些错误,尽管一切正常。

基本上,有一个日期选择器,当访问者选择一个日期时,我将全局变量 day 设置为日期(类似于 2018-05-25)并调用 VisitorMessages.start(),为他们订阅消息并让他们出现在频道 visitor:2018-05-25 上,并从所有其他频道取消订阅他们。

我还有一个 visitor:all 频道,每个人都可以在该频道上收到消息,不需要在场。

这是我正在做的(请原谅 CoffeeScript):

VisitorMessages =
  realtime: null
  connected_first_time: false
  start: ->
    unless @realtime
      @realtime = new Ably.Realtime
        authUrl: '/auth'
        recover: (lastConnectionDetails, cb) ->
          cb(true)
          return

    @unsubscribe()

    dayChannel = @realtime.channels.get("visitor:#{day}")
    allChannel = @realtime.channels.get("visitor:all")

    allChannel.subscribe (m) ->
      switch m.name
        when " . . . "
          # . . .
    dayChannel.subscribe (m) ->
      switch m.name
        when " . . . "
          # . . .

    dayChannel.presence.subscribe 'enter', (member) -> VisitorMessages.setNumOnline(dayChannel)
    dayChannel.presence.subscribe 'leave', (member) -> VisitorMessages.setNumOnline(dayChannel)
    dayChannel.presence.enter()
    VisitorMessages.setNumOnline(dayChannel)

    @realtime.connection.on 'connected', ->
      VisitorMessages.refreshData() # not showing this function here
      VisitorMessages.connected_first_time = true # the refreshData() function returns if this is false
      dayChannel = VisitorMessages.realtime.channels.get("visitor:#{day}")
      dayChannel.attach()
      dayChannel.presence.enter()
      VisitorMessages.setNumOnline(dayChannel)
      allChannel = VisitorMessages.realtime.channels.get("visitor:all")
      allChannel.attach()

  setNumOnline: (channel) ->
    channel.presence.get (err, members) ->
      # I use ractive.js to manipulate the DOM
      ractive.set('number_online', members.length)

  unsubscribe: ->
    for channelName of VisitorMessages.realtime.channels.all
      unless channelName == 'visitor:all'
        channel = VisitorMessages.realtime.channels.get(channelName)
        channel.presence.leave()
        channel.presence.unsubscribe()
        channel.unsubscribe()
        channel.detach()
        VisitorMessages.realtime.channels.release(channelName)

在 js 控制台中,访问者得到了一堆这些:

Ably: ConnectionManager.onChannelMessage() received message with different connectionSerial, but same message id as a previous; discarding

有时这样:

Ably: RealtimePresence._ensureMyMembersPresent(): Presence auto-re-enter failed: [c: Unable to enter presence channel (incompatible state); code=90001]

并且,当切换到不同的日期(设置日期并调用 VisitorMessages.start() 时,他们会得到:

Channels.onChannelMessage(): received event for non-existent channel: visitor:2018-05-26

我知道这可能是因为我在切换日期时明确释放频道,但当我不这样做时,VisitorMessages.realtime.channels.all 将包含我曾经加入的所有频道,而且我仍在接收未订阅频道的消息。

所以,这里发生了很多不同的事情,但是有人可以看出我的方法中的一些大局缺陷,或者帮助我理解为什么会发生这些错误吗?同样,一切正常,但有些地方不对!

谢谢!


以上代码编译成javascript:

var VisitorMessages;

VisitorMessages = {
  realtime: null,
  connected_first_time: false,
  start: function() {
    var allChannel, dayChannel;
    if (!this.realtime) {
      this.realtime = new Ably.Realtime({
        authUrl: '/auth',
        recover: function(lastConnectionDetails, cb) {
          cb(true);
        }
      });
    }
    this.unsubscribe();
    dayChannel = this.realtime.channels.get("visitor:" + day);
    allChannel = this.realtime.channels.get("visitor:all");
    allChannel.subscribe(function(m) {
      switch (m.name) {
        case " . . . ":
          // . . .
      }
    });
    dayChannel.subscribe(function(m) {
      switch (m.name) {
        case " . . . ":
          // . . .
      }
    });
    dayChannel.presence.subscribe('enter', function(member) {
      VisitorMessages.setNumOnline(dayChannel);
    });
    dayChannel.presence.subscribe('leave', function(member) {
      VisitorMessages.setNumOnline(dayChannel);
    });
    dayChannel.presence.enter();
    VisitorMessages.setNumOnline(dayChannel);
    return this.realtime.connection.on('connected', function() {
      VisitorMessages.refreshData();
      VisitorMessages.connected_first_time = true;
      dayChannel = VisitorMessages.realtime.channels.get("visitor:" + day);
      dayChannel.attach();
      dayChannel.presence.enter();
      VisitorMessages.setNumOnline(dayChannel);
      allChannel = VisitorMessages.realtime.channels.get("visitor:all");
      allChannel.attach();
    });
  },
  setNumOnline: function(channel) {
    channel.presence.get(function(err, members) {
      ractive.set('number_online', members.length);
    });
  },
  unsubscribe: function() {
    var channel, channelName, results;
    for (channelName in VisitorMessages.realtime.channels.all) {
      if (channelName !== 'visitor:all') {
        channel = VisitorMessages.realtime.channels.get(channelName);
        channel.presence.leave();
        channel.presence.unsubscribe();
        channel.unsubscribe();
        channel.detach();
        VisitorMessages.realtime.channels.release(channelName);
      }
    }
  }
};

我是 Ably 的一名工程师。

没有特别的大图,只是这里发生了一堆不同的事情。

Ably: ConnectionManager.onChannelMessage() received message with different connectionSerial, but same message id as a previous; discarding

这意味着客户端库已收到消息的多个副本,并自动对它们进行重复数据删除。众所周知,这偶尔会在客户端库执行实时 comet->websocket 升级后发生。如果它一直发生,则可能是其他原因 - 联系我们,我们将尝试实时调试它。

Channels.onChannelMessage(): received event for non-existent channel: visitor:2018-05-26

正如您正确指出的那样,那是因为您要发布频道。 channels.release() 是一个很少使用的功能,它仅在客户端连接和断开如此多的通道以致 channels.all 共同开始占用大量内存时真正有用,因此 release() 删除它们以便它们可以收集的垃圾。这不是 99% 的人需要做或知道的事情——我认为它甚至没有包含在我们的 api 文档中。它绝对不应该在尚未分离的通道上使用,这将导致未定义的行为。

当您在代码中调用它时,通道确实尚未分离,即使您已调用 detach()。根据 the api docs for channel#detach(),这是一个异步操作——它请求与 Ably 分离,同时将通道置于 detaching 状态。如果有必要释放一个通道(客户端几乎从来没有),应该在 detach()(或 once('detached') 监听器)的回调中完成。

when I wasn't doing that, VisitorMessages.realtime.channels.all would contain all of the channels that I had ever joined

是的,但没关系,在那个对象中并不意味着它们已附加。您可以通过过滤 state'attached'.

的条目来仅显示附加的条目

and I was still receiving messages for unsubscribed channels.

取消订阅是一个本地操作(它只是同步删除你添加的监听器);如果你想阻止 lib 从服务器接收消息,你需要从通道中分离——参见 channels/messages docs

(如果你的意思是你的消息监听器在你调用 unsubscribe() 之后仍然被调用,或者 lib 仍然在处于 detached 状态的通道上接收消息,那么这两种情况都不可能 - - 要么你的取消订阅代码坏了(恐怕我不太了解 coffeescript),要么被调用的监听器不是你认为的频道,或者 ably-js 中有一个错误。设置 log: {level: 4} 在 lib 构造函数中将启用调试日志记录,这可以帮助您查看 lib 正在做什么;如果您需要帮助分析日志,请告诉我们)。

Ably: RealtimePresence._ensureMyMembersPresent(): Presence auto-re-enter failed: [c: Unable to enter presence channel (incompatible state); code=90001]

通常,这意味着频道在暂停后尝试自动重新连接和重新进入(例如,因为您与互联网断开连接超过 2 分钟),但无法,例如因为客户端现在使用的令牌无权访问该频道。

但在你的情况下,我猜这是来自你手动释放的频道的噪音()d,然后被留在了边缘,因为服务器永远不会告诉他们他们已经 detached .

(我实际上刚刚提出了一项功能建议,要求在 channels.release() 上设置一个警卫,以阻止您对处于活动状态的频道执行此操作 -- https://github.com/ably/docs/issues/437)。