Как я могу ограничить количество дочерних процессов в imagemin-mozjpeg?

Я использую imagemin-mozjpeg, который использует двоичный файл mozjpeg для сжатия изображений.

Проблема в том, что я использую его на веб-сервере nodejs.

Вот как это работает сейчас:

  1. Я загружаю изображение в формате JPEG с помощью модуля «запрос» (fs.createReadStream).

  2. Мультер обрабатывает поток потоком и сохраняет его в буфере (хранилище памяти).

  3. Затем передайте буфер imagemin для сжатия.

  4. Затем сжатый буфер записывается в файл. (пример.jpg)

Все работает.

Проблема здесь в том, что для каждого запроса создается новый дочерний процесс бинарного файла mozjpeg, cjpeg.

1 дочерний процесс потребляет 12,5 МБ памяти (для файла размером 0,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");
                });

        }

person Faheem    schedule 20.04.2018    source источник


Ответы (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) для вставки новой задачи в начало очереди планировщика.

person momocow    schedule 20.04.2018
comment
Спасибо за концепцию. Теперь буду пробовать и экспериментировать. :) - person Faheem; 01.05.2018
comment
req.files — пустой объект. Есть идеи? Я думаю, что это потому, что он еще не был пропущен через мультер. - person Faheem; 01.05.2018
comment
Затем зарегистрируйте промежуточное ПО планировщика задач после промежуточного ПО multer. Если вы используете экспресс-программы промежуточного слоя, они выполняются в том порядке, в котором вы вызываете express.use(). - person momocow; 02.05.2018
comment
Я обновил свой код, чтобы включить express и multer. Попробуйте, есть ли проблемы ;) - person momocow; 03.05.2018