ConnectionManager.onChannelMessage() 接收到具有不同 connectionSerial 的消息,但消息 id 与上一个相同;丢弃

Posted

技术标签:

【中文标题】ConnectionManager.onChannelMessage() 接收到具有不同 connectionSerial 的消息,但消息 id 与上一个相同;丢弃【英文标题】:ConnectionManager.onChannelMessage() received message with different connectionSerial, but same message id as a previous; discarding 【发布时间】:2018-11-05 03:10:25 【问题描述】:

将 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);
      
    
  
;

【问题讨论】:

【参考方案1】:

我是 Ably 的工程师。

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

Ably:ConnectionManager.onChannelMessage() 接收到的消息具有不同的 connectionSerial,但消息 id 与上一个相同;丢弃

这意味着客户端库已收到消息的多个副本,并会自动对它们进行重复数据删除。众所周知,在客户端 lib 进行 live comet->websocket 升级之后偶尔会发生这种情况。如果它一直发生,可能还有其他问题 - 请与我们联系,我们会尝试实时调试。

Channels.onChannelMessage():接收到不存在频道的事件:visitor:2018-05-26

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

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

当我不这样做时,VisitorMessages.realtime.channels.all 将包含我曾经加入的所有频道

是的,不过没关系,在那个物体中并不意味着它们是依附的。您可以通过过滤那些state'attached' 的条目来仅显示附加的条目。

我仍然收到未订阅频道的消息。

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

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

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

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

但在您的情况下,我猜这是您手动 release()d 的频道发出的噪音,然后由于服务器永远不会告诉他们他们一直是detached,所以它们被搁置了。

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

【讨论】:

谢谢!我更新了我的问题以包含代码的 javascript 版本(无论如何都不应该发布 CoffeeScript)。 好的,好的。我删除了释放通道的行,它处理了所有内容,除了第一个错误(帖子的标题)关于具有不同连接序列的消息的消息。每次第一次连接时仍然会发生这种情况。如果你能帮我调试它,那就太好了。 对于它的价值,我得到发布频道的想法在这里:ably.io/documentation/realtime/channels-messages#subscribe(在订阅方法的“注意事项”末尾) 好的,所以问题标题中的错误不是我的代码问题,而是一个不会导致任何问题的 Ably 问题。我将在生产环境中将 log 设置为 0 以抑制客户端上的该消息。

以上是关于ConnectionManager.onChannelMessage() 接收到具有不同 connectionSerial 的消息,但消息 id 与上一个相同;丢弃的主要内容,如果未能解决你的问题,请参考以下文章