Как я могу ограничить количество дочерних процессов в imagemin-mozjpeg?
Я использую imagemin-mozjpeg, который использует двоичный файл mozjpeg для сжатия изображений.
Проблема в том, что я использую его в веб-сервере nodejs.
Вот как это работает сейчас:
Я загружаю изображение в формате JPEG с помощью модуля "request" (fs.createReadStream).
Multer обрабатывает поток и сохраняет его в буфере (памяти).
Затем передайте буфер imagemin для сжатия.
Затем сжатый буфер записывается в файл. (Example.jpg)
Все работает.
Проблема здесь в том, что для каждого запроса создается новый дочерний процесс бинарного файла mozjpeg, cjpeg.
1 дочерний процесс использует 12,5 МБ памяти (для файла.5 МБ).
Если у меня одновременно 50 запросов, то это составит ~700 МБ, потому что для 50 изображений есть 50 дочерних процессов.
Есть ли способ, которым я могу ограничить количество дочерних процессов? (библиотека использует модуль "execa") или просто порождает 4-5 дочерних процессов, и они выполняют сжатие для всех запросов.
Спасибо
if (req.files.myimage[0].buffer) {
let fileNumber = filesUploaded++;
imagemin.buffer(req.files.myimage[0].buffer, {
plugins: [
imageminMozjpeg({ quality: 60, progressive: true })
]
})
.then(file => {
fs.writeFile(__dirname + '/uploads/' + uuidv4() + '.jpg', file, 'hex' , function () {
res.end("SUCCESS" + fileNumber.toString());
});
})
.catch(err => {
console.log(err);
res.end("FAILED");
});
}
1 ответ
Основная концепция для решения этой проблемы заключается в ограничении количества звонков на imagemin()
(кто порождает процесс обработки изображений).
Может быть, вы можете внедрить систему планирования задач, используя очередь задач для сбора запросов, а некоторые работники для обработки запроса: imagemin()
,
var multer = require('multer')
var upload = multer({ dest: 'your/uploads/' })
// TaskScheduler, a wrapper of a task array
class TaskScheduler extends Array {
constructor (MAX_SLOTS, worker) {
super()
this._MAX_SLOTS= MAX_SLOTS
this._busy_slots= 0
this._worker= worker
}
/**
* Create new tasks
*/
push (...tasks) {
const ret = super.push(...tasks)
this.run()
return ret
}
/**
* Run tasks in available slots
*/
run () {
// if there are any tasks and available slots
while (this.length > 0 && this._busy_slots < this._MAX_SLOTS) {
const firstTask = this.shift()
this._worker(firstTask).then(() => {
// release the slot
this._busy_slots--
// since a task slot is released
// call run() again to process another task from the queue
this.run()
})
this._busy_slots++
}
}
}
// worker is supposed to return a Promise
const scheduler = new TaskScheduler(5, function (task) {
return imagemin.buffer(task.buffer, { /* imagemin options */ })
.then(() => { /* write image files */ })
.catch(() => { /* error handling */ })
})
// schedule the task when there is an incoming request
// the actual code depends on your web server
// here what to do in the callback is the point ;)
// Take express for example, `app` is the express.Application
app.post('your/end/point', upload.fields([{ name: 'myimage' }]), function (req) {
if (req.files.myimage[0]) {
scheduler.push(req.files.myimage[0])
}
})
Обратите внимание, что, поскольку ваш планировщик расширен от Array
Вы можете использовать любой Array
метод для управления вашими задачами, например pop()
отменить последнее задание, shift()
отказаться от первого задания и unshift(newTask)
вставить новую задачу в начале очереди планировщика.