Javascript Node.js:可控并发while循环
我收集了170万张mongodb唱片。每个记录都是一个ID号。我需要读取每个ID号,对另一个服务执行一些请求,转换数据,将其写入不同的集合,如果所有操作都成功,则删除原始ID记录 我想要一个脚本,该脚本可以无限期地执行这些操作,直到集合为空,并具有可指定的并发性(即任何时候最多3个请求) 通常我会使用Bluebird的Javascript Node.js:可控并发while循环,javascript,node.js,concurrency,Javascript,Node.js,Concurrency,我收集了170万张mongodb唱片。每个记录都是一个ID号。我需要读取每个ID号,对另一个服务执行一些请求,转换数据,将其写入不同的集合,如果所有操作都成功,则删除原始ID记录 我想要一个脚本,该脚本可以无限期地执行这些操作,直到集合为空,并具有可指定的并发性(即任何时候最多3个请求) 通常我会使用Bluebird的映射,它可以指定并发承诺的数量,但没有输入数组(除非我将所有输入记录读取到内存中,我不会这样做) 我想要的本质上是一个并发while循环,即:(伪javascript) 您可以使用
映射
,它可以指定并发承诺的数量,但没有输入数组(除非我将所有输入记录读取到内存中,我不会这样做)
我想要的本质上是一个并发while循环,即:(伪javascript)
您可以使用mongodb的游标异步迭代所有记录。要让三名工作人员处理该任务,请将该任务包装成一个异步函数,并多次调用该函数:
const cursor = db.collection("records").find({});
async function process() {
while(await cursor.hasNext()) {
const record = await cursor.next();
//...
}
}
await Promise.all([ process(), process(), process() ]);
(我不确定mongodb驱动程序是否支持对.next()
的并发调用,但是,您应该对此进行测试)
否则,此信号量实现可能会有所帮助:
function Semaphore(count = 1) {
const resolvers = [];
let startCount = count;
return {
aquire() {
return new Promise(resolve => {
if(startCount) { resolve(); startCount -= 1; }
else resolvers.push(resolve);
});
},
free() {
if(resolvers.length) resolvers.pop()();
else startCount += 1;
},
async use(cb) {
await this.aquire();
await cb();
this.free()
},
async done() {
await Promise.all(Array.from({ length: count }, () => this.aquire()));
startCount = count;
},
};
}
在您的情况下,它可用作:
const connectionSemaphore = Semaphore(3);
(async fuction() {
while(await cursor.hasNext()) {
const record = await cursor.next();
/*await*/ connectionSemaphore.use(async () => {
// Do connection stuff concurrently
});
}
await connectionSemaphore.done();
})();
您可以使用mongodb的游标异步迭代所有记录。要让三名工作人员处理该任务,请将该任务包装成一个异步函数,并多次调用该函数:
const cursor = db.collection("records").find({});
async function process() {
while(await cursor.hasNext()) {
const record = await cursor.next();
//...
}
}
await Promise.all([ process(), process(), process() ]);
(我不确定mongodb驱动程序是否支持对.next()
的并发调用,但是,您应该对此进行测试)
否则,此信号量实现可能会有所帮助:
function Semaphore(count = 1) {
const resolvers = [];
let startCount = count;
return {
aquire() {
return new Promise(resolve => {
if(startCount) { resolve(); startCount -= 1; }
else resolvers.push(resolve);
});
},
free() {
if(resolvers.length) resolvers.pop()();
else startCount += 1;
},
async use(cb) {
await this.aquire();
await cb();
this.free()
},
async done() {
await Promise.all(Array.from({ length: count }, () => this.aquire()));
startCount = count;
},
};
}
在您的情况下,它可用作:
const connectionSemaphore = Semaphore(3);
(async fuction() {
while(await cursor.hasNext()) {
const record = await cursor.next();
/*await*/ connectionSemaphore.use(async () => {
// Do connection stuff concurrently
});
}
await connectionSemaphore.done();
})();
最近我不得不做两次这样的事情。我的问题不涉及那么多的记录,我需要将结果合并到一个数据结构中,但我认为这可能是您所寻找的一个开始 概括这些解决方案会给我带来如下结果:
//processQueue::((数字->承诺[a]),数字,(a->b),(c[b])->c)->承诺c
const processQueue=(队列、计数、进程、组合、初始化)=>
队列(计数)
.then(items=>items.map(流程))
.然后(承诺=>Promise.all(承诺))
.然后(当前=>当前长度
?处理队列(队列、计数、处理、合并、合并(初始、当前))
:联合收割机(初始、当前)
)
这需要五个参数:
是一个函数,它接受一个数字并返回一个值列表的承诺queue
是一个数字count
是一个将这些值之一转换为另一种类型的函数process
是一个将目标类型和第二种类型的列表组合到目标类型中的函数combine
是减速器的起始值init
queue
函数,该函数返回最多为n
项的组的承诺,直到没有更多项为止,然后返回一个空列表的承诺。下面是一个愚蠢的版本:
const queue=((v)=>(count)=>Promise.resolve(
from({length:Math.min(count,10-v+1)},()=>({id:v++}))
)) (1)
队列(3)。然后(console.log)/~>[{id:1},{id:2},{id:3}]
队列(3).then(console.log)/~>[{id:4},{id:5},{id:6}]
队列(3).then(console.log)/~>[{id:7},{id:8},{id:9}]
队列(3)。然后(console.log)/~>[{id:10}]
队列(3)。然后(console.log)/~>[]//(并将永远返回空列表)
最近我不得不做两次这样的事情。我的问题不涉及那么多的记录,我需要将结果合并到一个数据结构中,但我认为这可能是您所寻找的一个开始
概括这些解决方案会给我带来如下结果:
//processQueue::((数字->承诺[a]),数字,(a->b),(c[b])->c)->承诺c
const processQueue=(队列、计数、进程、组合、初始化)=>
队列(计数)
.then(items=>items.map(流程))
.然后(承诺=>Promise.all(承诺))
.然后(当前=>当前长度
?处理队列(队列、计数、处理、合并、合并(初始、当前))
:联合收割机(初始、当前)
)
这需要五个参数:
是一个函数,它接受一个数字并返回一个值列表的承诺queue
是一个数字count
是一个将这些值之一转换为另一种类型的函数process
是一个将目标类型和第二种类型的列表组合到目标类型中的函数combine
是减速器的起始值init
queue
函数,该函数返回最多为n
项的组的承诺,直到没有更多项为止,然后返回一个空列表的承诺。下面是一个愚蠢的版本:
const queue=((v)=>(count)=>Promise.resolve(
from({length:Math.min(count,10-v+1)},()=>({id:v++}))
)) (1)
队列(3)。然后(console.log)/~>[{id:1},{id:2},{id:3}]
队列(3).then(console.log)/~>[{id:4},{id:5},{id:6}]
队列(3).then(console.log)/~>[{id:7},{id:8},{id:9}]
队列(3)。然后(console.log)/~>[{id:10}]
队列(3)。然后(console.log)/~>[]//(并将永远返回空列表)
您应该能够使用var stream=collection.find().batchSize(3).stream()函数查看所有记录。然后,您可以在数据事件stream.on('data'.fun…)
中读取一批3条记录,您可以在