我有 NodeJS Express Web 服务器,可以提供来自 AWS S3 的文件。大多数时候,这个确切的代码可以正常工作,并为生产中具有大量请求的各种应用程序提供文件。 NodeJS Web 服务器在 docker swarm 服务器上的多个节点上运行。
大约 2-3 周后,此功能将停止工作。 S3Client 没有响应
GetObjectCommand
,没有返回错误或任何东西。仅在重新启动 NodeJS Docker 容器后,此操作才会再次开始工作。
我阅读了 S3 SDK 文档,其中指出 SDK 将自动重试。
每个 AWS 开发工具包都实现自动重试逻辑。
问题:
NodeJS 版本:node:lts-alpine 模块:@aws-sdk/client-s3
控制器
AWS 控制器
const consoleLogger = require('../logger/logger.js').console;
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const config = {
"credentials": {
"accessKeyId": "example",
"secretAccessKey": "example"
},
"endpoint": "example",
"sslEnabled": true,
"forcePathStyle": true
}
const s3client = new S3Client(config);
const awsCtrl = {};
awsCtrl.getObject = async (key) => {
// Get object from Amazon S3 bucket
let data;
try {
// Data is returned as a ReadableStream
data = await s3client.send(new GetObjectCommand({ Bucket: "example", Key: key }));
console.log("Success", data);
} catch (e) {
consoleLogger.error("AWS S3 error: ", e);
const awsS3Error = {
name: e.name || null,
status: e.$metadata.httpStatusCode || 500
};
throw awsS3Error;
}
return data;
}
module.exports = awsCtrl;
文件控制器
const queryString = require('query-string');
const consoleLogger = require('../logger/logger.js').console;
const httpCtrl = require('./http.ctrl');
const jwtCtrl = require('./jwt.ctrl');
const awsCtrl = require('./aws.ctrl');
filesCtrl.deliverFile = async (req, res) => {
/* Get object from AWS S3 */
let fileObjectStream;
try {
fileObjectStream = await awsCtrl.getObject(filePath);
} catch (e) {
consoleLogger.error(`Unable to get object from AWS S3`, e);
if (e.status && e.status === 404) {
result.error = `Not found`;
result.status = 404;
return res.status(result.status).json(result);
}
return res.status(e.status || 500).json(result);
}
const filename = lookupResponse.data.filename;
// Set response header: Content-Disposition
res.attachment(filename);
// API response object stream download to client
return fileObjectStream.Body.pipe(res);
}
API
const express = require('express');
const router = express.Router();
const filesCtrl = require('../../controllers/files.ctrl');
const filesValidation = require('../validation/files');
router.get('/:fileId', [filesValidation.getFile], (req, res, next) => {
return filesCtrl.deliverFile(req, res);
});
我们使用 AWS Lambda 遇到了一个非常类似的问题,如果我们运行超过 5 或 6 个并发 Lambda,从 S3 读取 Lambda 就会死掉,没有错误,没有跟踪,也没有回退(= SQS 上的死信,这会触发拉姆达)。 非常令人沮丧和“危险”,因为我们没有任何迹象表明该事件没有得到正确处理。
希望您已经解决了这个问题,但由于 AWS 尚未修复此问题,因此可能对其他人有帮助...
从 AWS 支持中我们得到了在每次调用时重新实例化客户端的建议,因此您的代码应该类似于:
const consoleLogger = require('../logger/logger.js').console;
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const config = {
"credentials": {
"accessKeyId": "example",
"secretAccessKey": "example"
},
"endpoint": "example",
"sslEnabled": true,
"forcePathStyle": true
}
const awsCtrl = {};
awsCtrl.getObject = async (key) => {
const s3client = new S3Client(config); // <== new instance of the client
// Get object from Amazon S3 bucket
let data;
try {
// Data is returned as a ReadableStream
data = await s3client.send(new GetObjectCommand({ Bucket: "example", Key: key }));
data.Body.on('finished', () => s3Client.destroy()); // <== destroy it once used
console.log("Success", data);
} catch (e) {
consoleLogger.error("AWS S3 error: ", e);
const awsS3Error = {
name: e.name || null,
status: e.$metadata.httpStatusCode || 500
};
throw awsS3Error;
}
return data;
}
module.exports = awsCtrl;
这当然会对性能造成影响,但是您可能会发现调整http代理的其他建议解决方法并不能保证有效,因为无论如何您迟早都会达到上限...