【问题标题】:Rails 5: Send reload to set of clients on data updateRails 5:在数据更新时向一组客户端发送重载
【发布时间】:2017-12-28 06:15:02
【问题描述】:

我正在使用 Rails 5 为面对面的社交游戏(通过手机/平板电脑/等)制作一个简单的回合制游戏跟踪器

我想让游戏中的所有“玩家”(会话/用户列表/...)在玩家采取行动后自动重新加载他们的浏览器。

我知道有实时更新功能,例如 AJAX 和 websockets,但对于看似简单的问题来说,它们似乎都太重要了。此外,我想更新其他个客户端页面,而不仅仅是启动操作的客户端。

是否有发送重载的简单解决方案?还是我需要在更复杂的 API 中编写一些代码?

【问题讨论】:

  • 在这种情况下,使用 AJAX 的方式是最简单的方式。不过 Rails 5 中的ActionCable(WebSocket 方式)也很容易实现,所以如果可以的话,你应该尝试一下。
  • 有没有一个简单的例子可以证明这一点?我看过的所有 AJAX 教程都使用 AJAX,以便客户端的操作可以更新它自己的客户端页面,而无需加载整个页面。我想向 other 客户端发送重载。我会更新问题以使这一点更清楚。

标签: ruby-on-rails ajax websocket


【解决方案1】:

对于简单的麻烦,您仍然可以使用 AJAX 重新加载用户客户端,每 XX 秒发出一次间隔请求。服务器可以返回最后一个动作时间,客户端可以使用它来确定它是否应该重新加载自己。

例如在控制器上

# SomeController
def get_last_action_time
  # Return the timestamp of the last action
  render json: {last_action_time: "2017-12-29 10:00:42 UTC"}
end

在客户端

function getLocalLastAction () {
  /* return timestamp of the last action on local */
}

function setLocalLastAction (time) {
  /* Store the `time` to somewhere, ex: localStorage */
}

function checkLastAction () {
  $.getJSON("/get_last_action_time", function (data) {
    if (getLocalLastAction() < data.last_action_time) {
      /* destroy the interval */
      setLocalLastAction(data.last_action_time)
      /* do the reload page */
    } else {
      /* do nothing */
    }
  })
}

// Check every 1 second, shouldn't be too short due to performance
var checking = setInterval(checkLastAction, 1000)

那么当用户A做一个动作时,服务器last_action_time会发生变化,因此其他用户的客户端最多会在1秒后重新加载。

这种方式比较老了,但是在一些简单的情况下很容易做到,而且当你和动作缓存一起实现时,应用程序的性能还是可以接受的。在更复杂的情况下,我建议使用 WebSocket 解决方案

  • 完全控制
  • 低延迟
  • 应用性能更佳

【讨论】:

  • 与常规重新加载相比,这里唯一的优势似乎是应用程序不必重新加载整个页面?我想这也意味着用户页面不会继续在它们下面重新加载......
  • @DavidLjungMadison 不,这并不意味着是否重新加载整个页面。解决的问题是当用户 A 停留在 LA 做一个动作时,CA 的用户 B 会在不到 1 秒后自动重新加载页面。 AJAX 仅帮助您检查是否有人刚刚做了某事,然后在必要时重新加载页面。
  • 对 - 我的意思是它可以让您免于重新加载页面,但客户端仍然需要每隔一段时间从服务器轮询/加载数据。在这种情况下,它是动作时间而不是整个页面,这是一种改进。最好能找到一种简单的方法来保持与每个客户端的连接并直接从服务器调用重新加载。
  • @DavidLjungMadison 是的,你是对的,避免轮询的唯一方法是服务器推送或 Websocket,它们都基于更复杂的实现。不过,如前所述,Rails 5 的ActionCable 非常少,所以我认为您应该尝试一下。
  • 实现了类似的东西,它基本上可以工作。请注意,您的代码需要进行更改,以便在第一次检查时不会重新加载,可能是通过测试最后一次活动重新加载不为 0。
【解决方案2】:

感谢@yeuem1vannam 的回答,这是我使用的最终代码,它有助于避免页面在时间更新时加载旧信息的竞争条件,然后 javascript 更新时间并获取新时间,因此丢失重新加载。

javascript代码:

var actionChecker;

function doneChecking () {
  clearInterval(actionChecker);
}

function checkLastAction () {
  // Get the game ID from the html access span
  var dataId = document.getElementById('javascript_data_access');
  if (!dataId) return doneChecking();
  var initActionTime = dataId.getAttribute('init_last_action_time');
  if (!initActionTime) return doneChecking();

  dataId = dataId.getAttribute('game_number');
  if (!dataId) return doneChecking();

  // Get the last action time
  var ret = $.getJSON("/get_last_action_time/"+dataId, function (data) {
    var lastActionTime = data.last_action_time;
    if (!lastActionTime) return doneChecking();
    if (lastActionTime>initActionTime) {
      location.reload();
    }
  })
}

window.onload = function() {
  // Check every 1 second, shouldn't be too short due to performance
  actionChecker = setInterval(checkLastAction, 1000);
}

控制器的动作:

  def get_last_action_time
    last_time = nil
    begin
      @game = Game.find_by_id(params[:id])
      # Return the timestamp of the last action
      last_time = (@game && !@game.endTime) ? @game.reloadTime.to_i : 0
    rescue ActiveRecord::RecordNotFound
      last_time = 0
    end
    # Stop bugging us after 30m, we should have moved on from this page
    last_time==0 if (last_time!=0 && (milliseconds - last_time)>30*60*1000)
    render json: {last_action_time: last_time}
  end

然后在html.erb中:

<span id='javascript_data_access' game_number=<%= params[:id] %> init_last_action_time=<%= @game.reloadTime %>></span>

显然,您需要将 reloadTime 添加到您的模型中,如果您不想再检查重新加载,还需要添加 endTime。

到目前为止似乎工作正常,你必须确保你小心谁负责设置 reloadTime。如果两个页面在每次重新加载时都设置了 reloadTime,那么您将陷入两个页面之间的重新加载循环之战。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-01
    • 2017-05-07
    • 1970-01-01
    • 2016-01-09
    • 2015-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多