使用rxjs通过nodejs request.get发出批处理请求

时间:2018-01-11 22:17:39

标签: node.js request rxjs

我目前正在使用以下函数从调用Promise的结果中创建request.get

function dlPromiseForMeta(meta) {
    return new Promise(function (resolve, reject) {

        meta.error = false;

        var fileStream = fs.createWriteStream(meta.filePath);

        fileStream.on('error', function (error) {
            meta.error = true;
            console.log('filesystem ' + meta.localFileName + ' ERROR: ' + error);
            console.log('record: ' + JSON.stringify(meta));
            reject(meta);
        });

        fileStream.on('close', function () {
            resolve(meta);
        });

        request.get({
            uri: meta.url,
            rejectUnauthorized: false,
            followAllRedirects: true,
            pool: {
                maxSockets: 1000
            },
            timeout: 10000,
            agent: false
        })
            .on('socket', function () {
                console.log('request ' + meta.localFileName + ' made');
            })
            .on('error', function (error) {
                meta.error = true;
                console.log('request ' + meta.localFileName + ' ERROR: ' + error);
                console.log('record: ' + JSON.stringify(meta));
                reject(meta);
            })
            .on('end', function () {
                console.log('request ' + meta.localFileName + ' finished');
                fileStream.close();
            })
            .pipe(fileStream);
    });
}

这项工作正常,除非我尝试多次调用它,如下例所示,其中imagesForKeywords返回rxjs Observable

imagesForKeywords(keywords, numberOfResults)
    .mergeMap(function (meta) {
        meta.fileName = path.basename(url.parse(meta.url).pathname);
        meta.localFileName = timestamp + '_' + count++ + '_' + meta.keyword + '_' + meta.source + path.extname(meta.fileName);
        meta.filePath = path.join(imagesFolder, meta.localFileName);

        return rxjs.Observable.fromPromise(dlPromiseForMeta(meta))(meta);
    });

当源可观察量变得足够大时,我开始收到ESOCKETTIMEDOUT错误。

所以我想要做的是以某种方式批量mergeMap为每个100条目发生的事情...所以我并行执行100个,并且每个批次顺序执行,然后最后将它们合并。

如何使用rxjs完成此操作?

1 个答案:

答案 0 :(得分:2)

我认为最简单的用法是:,它在一定数量的ms之后触发,但最后还有一个参数用于计数。

如果在合理的时间内没有达到批量限制的流模式,则使用超时似乎很有用。

如果这不符合您的使用案例,请给我发一些详细信息,我会相应调整。

您的代码将如下所示,

  • bufferTime ,如上所述
  • forkjoin - 并行运行缓冲区内容,并在全部返回时发出
  • mergeMap - 合并结果
bufferTime()

这是一个可运行的模型,以显示它的工作原理。已注释掉最后一个imagesForKeywords(keywords, numberOfResults) .mergeMap(function (meta) { meta.fileName = path.basename(url.parse(meta.url).pathname); meta.localFileName = timestamp + '_' + count++ + '_' + meta.keyword + '_' + meta.source + path.extname(meta.fileName); meta.filePath = path.join(imagesFolder, meta.localFileName); return meta; }) .bufferTime(maxTimeout, null, maxBatch) .mergeMap(items => rxjs.Observable.forkJoin(items.map(dlPromiseForMeta))) .mergeMap(arr => rxjs.Observable.from(arr)) 以显示缓冲。

我假设了几件事,

  • imagesForKeywords将关键字分解为可观察的关键字流
  • 每个dlPromiseForMeta调用有一个关键字

mergeMap
// Some mocking
const imagesForKeywords = (keywords, numberOfResults) => {
  return Rx.Observable.from(keywords.map(keyword => { return {keyword} }))
}
const dlPromiseForMeta = (meta) => {
  return Promise.resolve(meta.keyword + '_image')
}

// Compose meta - looks like it can run at scale, since is just string manipulations.
const composeMeta = meta => {
  // meta.fileName = path.basename(url.parse(meta.url).pathname);
  // meta.localFileName = timestamp + '_' + count++ + '_' + meta.keyword + '_' + meta.source + path.extname(meta.fileName);
  // meta.filePath = path.join(imagesFolder, meta.localFileName);
  return meta;
}

const maxBatch = 3
const maxTimeout = 50 //ms
const bufferedPromises = (keywords, numberOfResults) =>
  imagesForKeywords(keywords, numberOfResults)
    .map(composeMeta)
    .bufferTime(maxTimeout, null, maxBatch)
    .mergeMap(items => Rx.Observable.forkJoin(items.map(dlPromiseForMeta)))
    //.mergeMap(arr => Rx.Observable.from(arr))

const keywords = ['keyw1', 'keyw2', 'keyw3', 'keyw4', 'keyw5', 'keyw6', 'keyw7'];
const numberOfResults = 1;
bufferedPromises(keywords, numberOfResults)
  .subscribe(console.log)