0

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

尽管一切正常,但我一直在 js 控制台中遇到一些错误。

基本上,有一个日期选择器,当访问者选择一个日期时,我将一个全局变量设置day为日期(类似于2018-05-25)和 call 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);
      }
    }
  }
};
4

1 回答 1

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(). 根据channel#detach() 的 api 文档,这是一个异步操作——它请求从 Ably 分离,同时将通道置于detaching状态。如果有必要释放一个通道(客户端几乎从来没有),它应该在回调detach()(或once('detached')监听器)中完成。

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

是的,但这很好,在那个物体中并不意味着它们已经依附。您可以通过过滤那些为stateis的条目来仅显示附加的条目'attached'

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

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

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

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

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

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

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

于 2018-05-25T18:56:13.020 回答