下面是否有比赛条件?

时间:2017-11-12 01:27:15

标签: javascript node.js express race-condition

我的快递服务器中有以下代码(为简洁起见,已将其删除)。我有一个共同的对象,我在三个不同的宁静终点添加/修改/阅读。由于nodejs中的所有http请求都是异步的,因此我可以同时获取put和get请求 。所以尽管PUT发生了但是我们说状态是而不是更新,我的GET可能会得到一个稍微陈旧的响应?

根据我的理解,我的测试显示这里没有竞争条件。因为更新results对象是同步操作,所有异步操作都应该等待它。有人可以帮助更好地解释这是否正确吗?

    var obj = {};
    const exec = require('child_process').exec;
    app.post('/foo', (req, res) => {
         var result = {};
         result.id = generateSomeRandomId();
         result.failed = 0;
         result.status = 'running'
         //execute some command and update result
         const child = exec('some command');
         child.stdout.on('data',  (data) => {
             //some logic
         });
         child.stderr.on('data',  (data) => {
             result.failed = result.failed + 1;
          });
         child.on('close',  (code, signal) => {
              if (signal !== null && signal !== undefined) {
                    result.status = 'cancelled';
              } else {
                    result.status = 'completed';
                    result.runtime = calculateRunTime();
                }
         });
         result.pid = child.pid;
         obj[result.id] = result; 
         res.send(result); 
    }   

    app.put('/foo/:id', (req, res) => {
         var result =  obj[req.params.id];
         if (result.status === 'running' && result.pid !== undefined) {
              kill(result.pid, 'SIGKILL');
              result.status = 'cancelled';
              result.runtime = calculateRunTime();
         }
         res.send(result);
    }   
    app.get('/foo/:id', (req, res) => {
         var result =  obj[req.params.id];
         res.send(result);
    }

2 个答案:

答案 0 :(得分:1)

这只是一个想法,但也许承诺在这里可能会有所帮助:

var obj = {};
const exec = require('child_process').exec;
app.post('/foo', (req, res) => {
     var result = {};
     result.id = generateSomeRandomId();
     result.status = 'running';
     const child = exec('some command');
     child.stdout.on('data',  (data) => {
         //some logic
     });

     result.promise = new Promise(resolve => {
       child.stderr.on('data',  (data) => {
           result.failed = result.failed + 1;
           resolve(false);
        });
       child.on('close',  (code, signal) => {
            // ...
           resolve(true);
       });
     });

     result.pid = child.pid;
     obj[result.id] = result;
     res.send(result); 
}   

app.get('/foo/:id', (req, res) => {
     var result =  obj[req.params.id];
     if(result.status === 'running') {
       result.promise.then(() => res.send(result));
     }
     else {
       res.send(result);
     }
}

在这种情况下,GET仅在child错误或“关闭”时才会响应。事件

答案 1 :(得分:1)

你没有任何我称之为“竞争条件”的东西;这里有一个不确定因素,但在实践中它并不重要。

看起来您的post启动了一个流程并返回了ID,您的put取消了流程,而您的get会返回流程的当前状态。由此我得知,在get完成并提供ID之前,您将永远无法post

如果您在get异步侦听器完成之前收到并返回了exec调用,您将获得最后一个正在进行的状态 - 我认为这是设计的。因此,唯一可能的冲突是,如果您已拨打put来暂停您的流程。

在与结果对象进行交互时,putget都是同步的,因此首先接收的是先完成的对象。您可以为我们的目的忽略进程取消请求,因为它不会修改结果对象。我们无法保证按照客户发送的订单顺序收到这些订单,这可能是也可能不是您方案中的实际问题。

相信(虽然我的记忆可能有问题),如果您使用cluster处理不同进程中的请求,则无法通过via传递数据无论如何,共享对象,因此已经排除了由这种可能性增加的任何复杂性。

因此,网络性能和可靠性的差异是您唯一真正的通配符。服务器将按照它们进入的顺序处理请求并为您提供预期的结果。如果你只有一个客户端,你可以等到你从之前的请求中得到响应,发送下一个请求,这可能会使你的性能无法接受,但要使其或多或少具有防弹性。否则,只需发送您的请求,不要担心,只需让您的应用程序足够健壮,以便识别&即使您已取消该流程,也会处理第二个取消请求。