在加入凤凰频道(或其他公共频道)时同步应用程序状态



我正在构建一个协作绘图板(例如 r/place):有一个像素网格,用户可以随时更改,像素更新会在线传播给所有其他用户。我想使用凤凰频道广播像素变化。

我的问题是关于当用户连接到服务时如何正确发送当前应用程序状态。

目前,我有一个包含绘图板状态的 ETS 表。我可以在广播任何像素写入之前MyChannel.handle_in/3更新此表。

我担心的是,读取MyChannel.join中的当前状态和Phoenix订阅频道的用户之间,不同的过程会更新状态。

用户将获得应用程序状态的过时版本,并且尚未订阅,因此他们也不会通过通道获取更新。

为了解决这个问题,我认为我需要一种方法来原子地读取当前状态,然后订阅 pubsub,确保在该时间段内没有消息写入 ETS 表或频道。我猜是锁?那是长生不老,还是有别的办法?

在写这个问题的时候,我看了一下Chris McCoord的ElixirConf 2015培训材料。我以为该示例中存在相同的竞争条件,但事实证明没有!该渠道拥有解决方案。

在该示例中,在Channel.join函数进程中,进程会向自身发送一条 :after_join 消息,稍后(订阅后)将 触发器 handle_info({:after_join...}) 读取应用程序状态并将其发送给用户。

关键是查询订阅后的应用程序状态。

并且始终在发布之前更改状态。

我总是这么说,因为我经历了以下 24 种可能的排序中的每一个:

  • 阅读状态,
  • 变异状态,
  • 订阅
  • 频道
  • 发布到频道

并确认订阅后阅读状态,结合 在发布之前更新状态,保证不会丢失数据。 这是我的工作要点。

它确实会导致 4 种可能的情况,其中状态更改两次,但这比数据丢失更容易处理。