我有一个代理管理池,负责存储,检查和检索代理,以便它们可以用于Web请求。
async getNextAvailableProxy() {
while(true) {
var sleepTime = global.Settings.ProxyPool.ProxySleepTimeMS;
var availableProxies = await this.data.find(PROXY_COLLECTION, {
$query: {
Enabled: true,
InUse: false,
LastUsed: { $lte: new Date(new Date() - sleepTime) }
},
$orderby: { ResponseTime: 1 }
});
if (availableProxies.length <= 0) {
var nextAvailable = await this.data.findOne(PROXY_COLLECTION, {
$query: { Enabled: true, InUse: false },
$orderby: { LastUsed: -1 }
});
if (!nextAvailable) {
await Utils.sleep(100);
console.log('No proxies available, sleeping');
continue;
}
sleepTime = sleepTime - (new Date() - nextAvailable.LastUsed)
if (sleepTime > 0)
await Utils.sleep(sleepTime);
continue;
}
var selectedProxy = availableProxies[0];
selectedProxy.InUse = true;
await this.data.save(PROXY_COLLECTION, selectedProxy);
return selectedProxy;
}
}
值得注意的是,我的find
和save
版本是针对NodeJS的MongoDB驱动程序的包装。
值得注意的是,Utils.sleep()
是使用setTimeout
执行异步睡眠的承诺。
现在,我知道由于NodeJS是单线程的,因此不会出现竞争条件。但是,当使用多个快速查询数据库的隔离对象时,情况根本不是这样。
如果我有五个对象ProxyPool
的实例,并且它们都在短时间内调用getNextAvailableProxy()
,它们将从数据库中获取相同的代理,因为一个实例具有在另一个实例保存了InUse
标记之前已经启动了查询,只留下了n
- ProxyPool
个实例都检索了保存完全代理。
如何以异步方式解决这个问题?
答案 0 :(得分:1)
老实说,根据你的帖子很难说出为什么会出现问题。虽然碰撞可能会发生,但在我看来应该是不够重要的,除非使用代理是一个非常长时间运行的操作(因此给定的代理很多)。
也就是说,我也不会在每个请求上查找代理。相反,我可能让每个工作人员在启动时或间隔(可能每小时一次)获取一个代理池,然后在内部管理(内存中)它可用的代理。
用于确定给定给定工作人员的代理的算法可以非常灵活,并且不太可能发生冲突,因为每个节点实例都是单线程的,它不会两次分配相同的代理。
风险在于您可能会遇到某个特定工人用完代理的地方。这也是你需要处理的事情,但是因为你(理论上)会让你的工人以某种方式负载平衡,如果你到达那个地方,你可能会用尽代理并且必须尽快发出Too Busy
回复。
最后,当您点击数据库获取可用代理列表时,您应该使用findAndModify()或类似内容一次性获取和更新文档,这样当您从数据库中取出一个时告诉数据库它不可用,而不是先等待Web服务器上的处理。