【问题标题】:Make Meteor.methods Synchronous and Asynchronous使 Meteor.methods 同步和异步
【发布时间】:2014-02-09 05:15:14
【问题描述】:

有时我需要对 writeMeLater 的 Meteor.call 进行排队并同步执行(阻止来自同一客户端的其他对 writeMeLater 的调用)。

其他时候,对writeMeLater 的调用应尽快执行,而不会排在当前排队的所有调用后面。

如果async 参数为真,以下是我使用this.unblock() 的尝试。案例 1 和 2 工作正常。但在案例 3 中,async=true 的呼叫被排在async=false 的呼叫后面!我们如何使用async=true 拨打电话跳过队列?这将类似于来自第二个客户端的呼叫在来自第一个客户端的呼叫之后不排队的方式,

所有Meteor.call() 均来自客户端

案例 1(正确同步):

Meteor.call('writeMeLater', 's', false)
Meteor.call('writeMeLater', 's', false)
Meteor.call('writeMeLater', 's', false)

案例 2(正确异步):

Meteor.call('writeMeLater', 'a', true)
Meteor.call('writeMeLater', 'a', true)
Meteor.call('writeMeLater', 'a', true)

案例 3(不是期望的行为)

Meteor.call('writeMeLater', 's', false)
Meteor.call('writeMeLater', 's', false)
Meteor.call('writeMeLater', 's', false)

Meteor.call('writeMeLater', 'a', true)
Meteor.call('writeMeLater', 'a', true)
Meteor.call('writeMeLater', 'a', true)

server/main.js

writeMeLater = function(data, callback) {
    console.log('writeMeLater: ', data)

    // simulate taking 3 second to complete
    Meteor.setTimeout(function() {
        Logs.insert({data: data, timestamp: new Date().getTime()})
        console.log('Log.insert: ', data)
        callback(null, 'done')
    }, 3 * 1000)
}



writeMeLaterSync = Meteor._wrapAsync(writeMeLater)



Meteor.methods({

    writeMeLater: function(data, async) {
        if(async)
            this.unblock()

        writeMeLaterSync(data)
    }

})

【问题讨论】:

  • @imslavko 是否可以让一些方法调用立即执行而不是排队,类似于来自不同客户端的方法调用不会在第一个客户端的调用之后排队
  • 这正是this.unblock() 调用的作用——它将允许另一个方法调用当前连接到 start 之前完成前一个。这就是我看到的输出:pastebin.com/gtdgLTGf,它看起来对我来说是正确的。对于 3 次同步调用,在调用完成之前会有 3 秒的延迟。对于第二批 - 它们都同时开始并在 3 秒后结束。
  • 由于您只运行一个 node.js 服务器,所有 6 个调用将共享相同的事件循环。请记住,node.js 是单线程的。没有真正的多线程,只有协作并发
  • 对于真正的多线程,您需要运行多个服务器实例并对它们之间的连接负载进行负载平衡,但即便如此,一个 DDP 连接将只与一个单线程服务器通信(如 DDP是有状态的)
  • 仅当客户端建立两个不同的 DDP 连接或者您的服务器上有消息队列和 node.js 服务器的多个进程时。这只是单线程 JavaScript 环境的局限。

标签: javascript multithreading node.js asynchronous meteor


【解决方案1】:

我的第一个想法是,为什么不真正创建一个队列? 而不是依赖 JavaScript 事件循环作为你的队列。然后将文档插入该队列,例如:

WritesQueue = new Meteor.Collection("WritesQueue");
WritesQueue.insert({data: 'a', prioritize: true, inserted: new Date()});

也许每次您插入高优先级写入时,都会触发处理队列的Meteor.call("write"),优先级(非异步)优先处理:

Meteor.methods({
  write: function () {
    WritesQueue.find({prioritize: true}, {sort: {inserted: 1}})
    .forEach(function (doc) {
      console.log(doc.data);
      WritesQueue.remove(doc._id);
    });
    WritesQueue.find({prioritize: false}, {sort: {inserted: 1}})
    .forEach(function (doc) {
      console.log(doc.data);
      WritesQueue.remove(doc._id);
    });
  }
});

或者,如果您希望每次插入高优先级或低优先级写入时都处理队列,请随时调用write 方法或将insert 放入write 方法本身。这解决了跳转到行首问题,尽管写入仍然是每个客户端同步处理的。

至于尝试为单个客户端实现并行处理,@imslavko(在上面问题的 cmets 中)是正确的,因为实现此目的的一种方法是让客户端建立多个 DDP 连接。有一种相对简单的方法,虽然很老套且非 Meteoric,但可以做到这一点:

安装Iron Router 并在您的服务器代码中,定义一个服务器端路由:

Router.map(function () {
  this.route('writeMeLater', {
    where: 'server',
    action: function () {
      Meteor.call('writeMeLater',
        this.request.query.data, this.request.query.async);
    }
  });
});

上面的this.request.query 是您随请求提交的具有键值对的对象。例如:

HTTP.post("http://yoursite.com/writeMeLater",
  {params: {data: 'a', async: true}});

据服务器所知,这个请求来自一个新的客户端,所以它将在一个新的 Fiber(即线程)中处理。如果writeMeLater 方法知道不等待,它的许多实例可以开始同时运行。现在问题变成了保持请求的顺序,如果服务器上的执行顺序与客户端上的执行顺序对您很重要,因为 HTTP POST 请求可能不一定以与它们相同的顺序到达服务器发送。但也有多种处理方式(分批发送,或者包含一个计数器,如果服务器检测到请求乱序,则让服务器等待几秒钟,等等)。

【讨论】:

    猜你喜欢
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 2014-03-29
    • 2011-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-01
    相关资源
    最近更新 更多