javascript 从AWS S3获取多个对象在第50个文件时停止

ktca8awb  于 2023-03-06  发布在  Java
关注(0)|答案(1)|浏览(112)

我有一个公共AWS S3存储桶,其中包含150个文本文件,每个30kB。我创建了一个简单的test.js文件,用于下载这150个文件,并显示第n个文件已成功下载的信息:

const { GetObjectCommand } = require('@aws-sdk/client-s3');
const { S3Client } = require('@aws-sdk/client-s3');
const { range } = require('lodash');

const s3 = new S3Client({
  region: 'eu-central-1',
  credentials: {
    accessKeyId: 'XXXXX',
    secretAccessKey: 'XXXXX',
  },
});

const test = async () => {
  let counter = 0;

  await Promise.all(
    range(0, 150).map((i) => {
      const getCommand = new GetObjectCommand({
        Key: `test/testfile${i}.txt`,
        Bucket: 'bucketname',
      });

      return s3.send(getCommand).then(() => {
        counter += 1;
        console.log(counter, 'downloaded');
      });
    })
  );

  console.log('Finish!');
};

test();

问题是下载了第一个50文件,然后程序停止(没有记录“Finish!”)。100kB2MB4MB。在每种情况下,50文件都有固定的某种限制。
我测试过,当文件大小为15 kB时,这个问题不存在-我在50文件的每个块之间都有一些中断,但所有文件都下载了。

您知道如何让它下载所有文件吗?

我试图找到与AWS S3有关的一些限制,但我什么也没找到。
我试着玩--max-old-space-size=XXX,但是没有用。而且看起来也不像是内存问题,因为不同的文件大小有相同的50文件下载限制。
我试着在第一个0-40文件和下一个41-80文件之间添加一些sleep,但它也卡在了50th文件上。

ruarlubt

ruarlubt1#

您正在为150个操作重用同一个S3 Client。虽然在较小的范围内共享一个S3 Client是一个好做法,但一个S3 Client无法同时处理这么多http连接。请注意,默认情况下,aws-sdk-js配置为最多只能处理50个套接字
我认为你可以通过传递一个自定义的代理来配置S3 Client,但是我不认为这是一个好主意。我从来没有尝试过,但是它可能看起来像这样:

const https = require('https');
const AWS = require('aws-sdk');

const agent = new https.Agent({
  maxSockets: 150
});

const s3 = new AWS.S3({
  httpOptions: {
    timeout: 2000,
    agent: agent
  }
});

一个更好的解决方案是创建一个promise池,每个promise池一次可以下载一个文件,一旦下载完成就开始下一个。This great solution似乎正是您所需要的:

Promise.pool = function pool(funcs, inParallel, progressCallback) {
  const promises = [];
  const results = [];

  function getNext() {
    if (funcs.length) {
      return funcs.pop()()
        .catch(() => {})
        .then((res) => {
          results.push(res);
          if (progressCallback) {
            progressCallback(results);
          }
          return getNext();
        });
    }
  }
  for (let i = 0; i < Math.min(inParallel, funcs.length); i++) {
    promises.push(getNext());
  }
  return Promise.all(promises)
    .then(() => results);
};

const inParallel = 25; // or other value less or equal than 50
Promise.pool(funcs, inParallel, someCallback)
  .then(() => console.log("all done!"));

相关问题