Latest Post

🚀 Hướng dẫn chi tiết về Worker Threads trong Node.js Cách giải phóng bộ nhớ sau mỗi lần trim ảnh với thư viện trim-image trong Node.js

Các bài cùng serial: Tối ưu xử lý ảnh dung lượng lớn trong Node.js

Bạn đang sử dụng thư viện trim-image trong Node.js và gặp lỗi full bộ nhớ khi xử lý ảnh dung lượng lớn hoặc số lượng nhiều. Dưới đây là các cách giúp bạn giải phóng bộ nhớ sau mỗi lần trim ảnh.

🔹 1. Giải phóng bộ nhớ bằng global.gc()

Nếu Node.js được chạy với cờ --expose-gc, bạn có thể gọi global.gc() sau mỗi lần xử lý ảnh.

Cách làm

Chạy lệnh với --expose-gc:

node --expose-gc script.js

Và trong code:

const trimImage = require('trim-image');
const fs = require('fs');

async function processImage(input, output) {
    return new Promise((resolve, reject) => {
        trimImage(input, output, (err) => {
            if (err) return reject(err);

            console.log(`✅ Trimmed: ${output}`);

            // Giải phóng bộ nhớ
            if (global.gc) global.gc();

            resolve();
        });
    });
}

async function processImages(images) {
    for (const image of images) {
        await processImage(image.input, image.output);
    }
}

const images = [
    { input: 'input1.jpg', output: 'output1.jpg' },
    { input: 'input2.jpg', output: 'output2.jpg' }
];

processImages(images);

🚀 Lợi ích: Giúp thu hồi bộ nhớ không cần thiết.

🔹 2. Hạn chế xử lý đồng thời bằng for...of

Nếu bạn xử lý nhiều ảnh cùng lúc, bộ nhớ có thể bị tràn. Hãy xử lý ảnh tuần tự bằng for...of thay vì Promise.all().

❌ Cách KHÔNG NÊN DÙNG (có thể gây full bộ nhớ):

await Promise.all(images.map(image => processImage(image.input, image.output)));

Thay vào đó, hãy dùng for...of:

for (const image of images) {
    await processImage(image.input, image.output);
}

🚀 Lợi ích: Hạn chế số lượng ảnh được xử lý cùng lúc.

🔹 3. Giới hạn số ảnh xử lý đồng thời bằng p-limit

Nếu bạn muốn xử lý nhiều ảnh nhưng không quá tải bộ nhớ, hãy sử dụng thư viện p-limit để giới hạn số lượng ảnh chạy cùng lúc.

Cách làm

Cài đặt p-limit:

npm install p-limit

Sửa code:

const pLimit = require('p-limit');
const trimImage = require('trim-image');

const limit = pLimit(2); // Chỉ xử lý tối đa 2 ảnh cùng lúc

async function processImage(input, output) {
    return new Promise((resolve, reject) => {
        trimImage(input, output, (err) => {
            if (err) return reject(err);
            console.log(`✅ Trimmed: ${output}`);
            resolve();
        });
    });
}

async function processImages(images) {
    const tasks = images.map(image => limit(() => processImage(image.input, image.output)));
    await Promise.all(tasks);
}

const images = [
    { input: 'input1.jpg', output: 'output1.jpg' },
    { input: 'input2.jpg', output: 'output2.jpg' },
    { input: 'input3.jpg', output: 'output3.jpg' },
    { input: 'input4.jpg', output: 'output4.jpg' }
];

processImages(images);

🚀 Lợi ích: Chỉ chạy tối đa 2 ảnh cùng lúc, tránh full bộ nhớ.

🔹 4. Dùng setImmediate() để giải phóng event loop

Thêm setImmediate() vào vòng lặp để giải phóng tài nguyên giữa mỗi lần xử lý ảnh.

Cách làm

async function processImages(images) {
    for (const image of images) {
        await processImage(image.input, image.output);
        await new Promise(resolve => setImmediate(resolve)); // Giải phóng tài nguyên
    }
}

🚀 Lợi ích: Tránh nghẽn bộ nhớ khi xử lý quá nhiều ảnh.

🔹 5. Sử dụng Worker Threads để xử lý song song mà không quá tải

Khi cần xử lý ảnh song song, bạn có thể dùng Worker Threads để tránh làm đầy bộ nhớ chính.

Cách làm

Tạo file worker.js:

const { parentPort, workerData } = require('worker_threads');
const trimImage = require('trim-image');

trimImage(workerData.input, workerData.output, (err) => {
    parentPort.postMessage(err ? { error: err } : { success: workerData.output });
});

Tạo file script.js:

const { Worker } = require('worker_threads');

const images = [
    { input: 'input1.jpg', output: 'output1.jpg' },
    { input: 'input2.jpg', output: 'output2.jpg' },
    { input: 'input3.jpg', output: 'output3.jpg' },
    { input: 'input4.jpg', output: 'output4.jpg' }
];

const MAX_WORKERS = 2; // Chỉ xử lý tối đa 2 ảnh cùng lúc
let activeWorkers = 0;
let index = 0;

function runWorker() {
    if (index >= images.length) return;

    const image = images[index++];
    activeWorkers++;

    const worker = new Worker('./worker.js', { workerData: image });

    worker.on('message', msg => {
        if (msg.success) console.log(`✅ Trimmed: ${msg.success}`);
        else console.error(`❌ Error: ${msg.error}`);

        activeWorkers--;
        runWorker(); // Tiếp tục với ảnh tiếp theo
    });

    worker.on('error', err => {
        console.error(`❌ Worker error: ${err}`);
        activeWorkers--;
        runWorker();
    });
}

// Khởi động số worker ban đầu
for (let i = 0; i < MAX_WORKERS; i++) {
    runWorker();
}

🚀 Lợi ích: Xử lý ảnh song song nhưng không quá tải bộ nhớ.

Tóm lại, bạn nên làm gì?

Nếu gặp lỗi full bộ nhớ khi trim ảnh với trim-image, hãy thử các cách sau:

  1. Giải phóng bộ nhớ thủ công bằng global.gc() (Chạy với --expose-gc).
  2. Xử lý ảnh tuần tự bằng for...of thay vì Promise.all().
  3. Giới hạn số lượng ảnh xử lý đồng thời bằng p-limit.
  4. Dùng setImmediate() để giải phóng tài nguyên giữa mỗi lần xử lý.
  5. Sử dụng Worker Threads để xử lý song song nhưng tránh quá tải.

📌 Cách tối ưu nhất: Nếu bạn xử lý ảnh số lượng lớn, hãy kết hợp Worker Threads với p-limit để đạt hiệu suất cao mà không tốn quá nhiều bộ nhớ.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *