我有一个聊天应用程序,用户可以在其中互相发送文件。每个文件必须在 2 天后删除。
为此,我每 4 小时递归检查一次所有文件,并删除创建时间超过 2 天的文件。
但是这项工作消耗了我服务器的大量CPU,从而扰乱了应用程序的工作。因为要检查的文件数量非常多,全部大小都在10GB左右。
现在,如果每个文件一上传,就激活一个计时器,在 2 天后删除该文件,是不是更好?考虑到几乎每秒都会上传一个文件,并且计时器的数量可能会非常多。
我的递归解决方案:
const fs = require('fs')
const path = require('path')
const rootDir = require('../config').rootDir
const log = require('../da/log');
const DIRECTORY_MESSAGES = rootDir + '/file/messages';
const DIRECTORY_AVATARS = rootDir + '/file/avatars';
const INTERVAL_MINUTES_MESSAGES = 240;
const INTERVAL_MINUTES_AVATARS = 265;
const ACCESS_DAYS_MESSAGES = 2;
const ACCESS_DAYS_AVATARS = 10;
/*
* Remove files that access times is older than {fileAccessHours}.
* Runs every {removeEveryMinutes}
* */
module.exports = {
start: function () {
setInterval(deleteFiles.bind(this, DIRECTORY_MESSAGES, ACCESS_DAYS_MESSAGES), INTERVAL_MINUTES_MESSAGES * 60_000)
setInterval(deleteFiles.bind(this, DIRECTORY_AVATARS, ACCESS_DAYS_AVATARS), INTERVAL_MINUTES_AVATARS * 60_000)
}
}
function deleteFiles(dir, accessDays) {
walkDir(dir, function (path, isDirectory) {
try {
if (isDirectory) {
if (path === dir) {
return;
}
return fs.rmdir(path, function (err) {
if (err) {
return log.error(__filename + ' :deleteFiles, fs.rmdir, ' + err);
}
});
} else {
fs.stat(path, function (err, stat) {
try {
const now = new Date().getTime();
const endTime = new Date(stat.atime).getTime() + accessDays * 86_400_000;
if (err) {
return log.error(__filename + ' :deleteFiles, fs.stat, ' + err);
}
if (now > endTime) {
return fs.unlink(path, function (err) {
if (err) {
return log.error(__filename + ' :deleteFiles, fs.unlink, ' + err);
}
});
}
} catch (err) {
console.log('File Deleter: walkDir: callback: fs.stat: ' + err);
}
});
}
} catch (err) {
console.log('File Deleter: walkDir: callback' + err);
}
});
}
function walkDir(dir, callback) {
try {
let files = fs.readdirSync(dir);
if (files.length === 0) {
callback(dir, /* isDirectory */ true);
} else {
files.forEach(f => {
let dirPath = path.join(dir, f);
let isDirectory = fs.statSync(dirPath).isDirectory();
isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f), /* isDirectory */ false);
});
}
} catch (err) {
console.log('File Deleter: walkDir: ' + err);
}
}
现在,如果每个文件一上传,就激活一个计时器,在 2 天后删除该文件,是不是更好?
“更好”是一个难以捉摸的概念,但请考虑一下如果您的服务器崩溃并重新启动,或者您必须关闭它以进行升级等会发生什么:这些计时器丢失了,您必须再次执行目录遍历操作。
这看起来像是一个简单数据库的应用程序。它可以是由文件支持的进程内数据库(例如
sqlite
数据库),或者现在让正确的数据库进程运行也非常简单。您在上传时记录文件的日期/时间和路径,然后定期查询数据库以查看确切的文件已过期并需要删除。无需目录遍历。如果使用单独的数据库(而不是进程内),清理甚至可以是一个单独的过程,如 A R K 建议,而不是占用服务器的主 Node.js 线程。
执行目录walkm,但不要重新发明轮子。例如使用 gnu find:
find /path/to/directory -type f -ctime +2 -delete
从 cron 运行它。