gpt4 book ai didi

node.js - Meteor.setTimeout 和 Meteor.methods 之间的并发

转载 作者:可可西里 更新时间:2023-11-01 09:13:34 32 4
gpt4 key购买 nike

在我的 Meteor 应用程序中实现基于回合的多人游戏服务器,客户端通过发布/订阅接收游戏状态,并可以调用 Meteor 方法 sendTurn 将回合数据发送到服务器(他们不能直接更新游戏状态集合) .

var endRound = function(gameRound) {
// check if gameRound has already ended /
// if round results have already been determined
// --> yes:
do nothing
// --> no:
// determine round results
// update collection
// create next gameRound
};

Meteor.methods({
sendTurn: function(turnParams) {
// find gameRound data
// validate turnParams against gameRound
// store turn (update "gameRound" collection object)
// have all clients sent in turns for this round?
// yes --> call "endRound"
// no --> wait for other clients to send turns
}
});

为了实现时间限制,我想等待一定时间(让客户有时间调用 sendTurn ),然后确定回合结果 - 但前提是回合结果尚未在 sendTurn 中确定。

我应该如何在服务器上实现这个时间限制?

我天真的实现方法是调用 Meteor.setTimeout(endRound, <roundTimeLimit>)

问题:

  • 并发性如何?我假设我应该在 sendTurnendRound (?) 中同步更新集合(没有回调),但这是否足以消除竞争条件? (阅读关于同步数据库操作也产生的 this SO question 已接受答案的第 4 条评论,我对此表示怀疑)

  • 在这方面,“每个请求”在我的上下文中的 Meteor docs 中是什么意思(由客户端方法调用和/或在服务器 endRound 中调用的函数 setTimeout)?

    In Meteor, your server code runs in a single thread per request, not in the asynchronous callback style typical of Node.

  • 在多服务器/集群环境中,这(如何)工作?

最佳答案

好问题,它比看起来更棘手。首先,我想指出,我已经在以下存储库中针对这个确切问题实现了解决方案:

https://github.com/ldworkin/meteor-prisoners-dilemma https://github.com/HarvardEconCS/turkserver-meteor

总而言之,问题基本上具有以下性质:

  • 每个客户端在每一轮都发送一些 Action (你称之为sendTurn)
  • 当所有客户端都已发送他们的操作时,运行 endRound
  • 每一轮都有一个计时器,如果超时,无论如何都会自动运行 endRound
  • endRound 无论客户端做什么,每轮都必须恰好执行一次

现在,考虑我们必须处理的 Meteor 的属性:

  • 每个客户端一次只能向服务器发送一个未完成的方法(除非在方法内部调用了 this.unblock())。以下方法等待第一个。
  • 服务器上的所有超时和数据库操作都可以让渡给其他纤程

这意味着每当一个方法调用通过一个生成操作时,Node 或数据库中的值就会改变。这可能会导致以下潜在的竞争条件(这些只是我已经修复的,但可能还有其他的):

  • 例如,在 2 人游戏中,两个客户端同时调用 sendTurn。两者都调用一个 yielding 操作来存储 turn 数据。然后这两种方法检查是否有 2 个玩家轮到他们发送,找到肯定的,然后 endRound 运行两次。
  • 玩家在回合超时时调用 sendTurn。在这种情况下,endRound 被超时和播放器方法调用,导致再次运行两次。
  • 对上述问题的不正确修复会导致饥饿,其中 endRound 永远不会被调用。

您可以通过多种方式解决这个问题,在 Node 中同步或在数据库中同步。

  • 由于实际上一次只有一个 Fiber 可以更改 Node 中的值,如果您不调用让步操作,则可以保证避免可能的竞争条件。因此,您可以在内存中而不是在数据库中缓存诸如转弯状态之类的东西。但是,这要求缓存正确完成并且不会转移到集群环境。
  • endRound 代码移到方法调用本身之外,使用其他东西来触发它。这是我采用的方法,可确保只有计时器或最终玩家触发回合结束,而不是两者都触发(有关使用 observeChanges 的实现,请参阅 here)。
  • 在集群环境中,您将不得不仅使用数据库进行同步,可能使用条件更新操作和原子运算符。类似于以下内容:

    var currentVal;

    while(true) {
    currentVal = Foo.findOne(id).val; // yields

    if( Foo.update({_id: id, val: currentVal}, {$inc: {val: 1}}) > 0 ) {
    // Operation went as expected
    // (your code here, e.g. endRound)
    break;
    }
    else {
    // Race condition detected, try again
    }
    }

上述方法很原始,可能会导致高负载下数据库性能不佳;它也不处理计时器,但我相信通过一些思考您可以弄清楚如何扩展它以更好地工作。

您可能还想看到这个 timers code对于其他一些想法。一旦我有时间,我将把它扩展到你描述的完整设置。

关于node.js - Meteor.setTimeout 和 Meteor.methods 之间的并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32987510/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com