2014-06-10 52 views
1

我想知道什么是设计这样一个Web服务的方式:避免竞争条件

说我有一台服务器监听请求,它会收到一些重点和检查,如果它的缓存(例如使用一些数据库),如果不是它做了一些处理,则生成答案,将其存储在高速缓存数据库中并将答案返回给客户端。

这似乎工作正常,但如果两个客户端请求相同的不存在的密钥会发生什么?在这种情况下,竞争条件会发生,所以它看起来像

client 1 -> check cache DB -> generate answer -> store in cache -> reply to client 
client 2 -> check cache DB -> generate answer -> store in cache -> reply to client 

的一种方式,以避免此问题将在DB采用了独特的功能,因此每当产生并写入到数据库的第二个答案,发生一些错误。这很好,但看起来更像是一个补丁,而不是一个真正的解决方案。特别是,想象一个产生答案需要大量处理的情况,那么其他事情会更好。

我能想到的一个选择是使用作业队列,所以无论何时收到一个密钥,密钥要么附加到现有作业,要么将新作业添加到队列中。

我一直在玩node.js几个星期,我很惊讶我没有找到显示这种用例的例子。所以我想知道这是否是一种可接受的解决方案,或者更好的方案?

回答

2

这里是你如何能做到在一个单进程设置:

var Emitter = require('events').EventEmitter; 

var requests = Object.create(null); 

function getSomething (key, callback) { 

    var request = requests[key]; 

    if (!request) { 
    request = requests[key] = new Emitter; 

    getSomethingActually(key, function (err, result) { 
     delete requests[key]; 
     if (err) return request.emit('error', err); 
     request.emit('result', result); 
    }); 
    } 

    request.once('result', function (result) { 
    callback(null, result); 
    }); 

    request.once('error', function (err) { 
    callback(err); 
    }); 

} 

,如果你想扩展这一点,你需要使用一些外部存储+事件总线,像Redis的。

0

您应该使用作业队列(或其他类型的卸载作业)。处理密集型任务应始终从主节点应用程序中取出(通过队列,将其产生为单独的进程等),否则将阻塞事件循环,从而阻止所有其他请求。

这就是说,如果您选择使用某种可以具有唯一约束的队列(例如postgres支持的队列),并为该键设置唯一约束,那么重复将永远不会插入到工作队列中,所以永远不会被处理两次。在这种情况下,您可以简单地忽略唯一的约束错误。

注意,它仍然是可能的可能,但可能性非常小,能有像事件的顺序:

  1. 要求检查重点X上的“缓存”,获取一个小姐
  2. 工人完成回答密钥x,将其插入“缓存”,从队列中删除X
  3. 请求接收到的未命中密钥x,将其添加到队列
  4. 工人从队列拉出密钥x,开始计算

在此事件(可能不太可能)发生后,第二个工作人员会在插入密钥时出错。在我看来,这可能是一个不太可能发生的事情,添加一个唯一的关键约束,而忽略第二个工作者的唯一约束违规错误可能是一个足够可行的选择。