Tối ưu tốc độ ứng dụng NodeJS khi lập trình

Node.js luôn được giới thiệu là một framework non-blocking I/O tốc độ xử lý rất nhanh. Trong bài viết này, tôi tập trung chia sẻ vài kinh nghiệm bản thân để tối ưu hóa tốc độ.


1058

Non-Blocking I/O, Asynchronous

Cần thay đổi tư duy lập trình tuần tự, kiểu PHP sang cách gọi hàm asynchronous trong NodeJS. Khi lập trình async thì lại chia làm 2 trường phái:

  • Call back cổ điển
  • Promise A+ (Blue Bird,)

Lời khuyên ECMAScript 6 sẽ đưa vào chuẩn Promise  A+ do đó hãy ưu tiên sử dụng module hỗ trợ Promise. BlueBird được benchmark tốc độ tốt hơn Q, xem kết quả ở đây. Nên ưu tiên sử dụng BlueBird. Hàm call back cổ điển có thể promisify để sử dụng Promise. Đoạn code dưới sử dụng request để tải về 2 ảnh từ trang unsplash.com.

var fs = require('fs');
var promise = require("bluebird");
var request = require('request');
//Mục tiêu ví dụ này là tải về 2 photo từ trang Unsplash
console.time('download');
var photoLinks = [{link: 'https://unsplash.imgix.net/photo-1425235024244-b0e56d3cf907?fit=crop&fm=jpg&h=700&q=75&w=1050',
    name: 'dog.jpg'},
    {link: 'https://unsplash.imgix.net/reserve/NxxLccbqQdGtNc7xJ43e_ancestral-home.jpg?fit=crop&fm=jpg&h=600&q=75&w=1050',
name: 'house.jpg'}];
//Promisify đoạn lện request.get...on ...pipe
function getPhoto(photoLink){
    return new Promise(function(fulfill, reject) {
        request.get(photoLink.link)
            .on('error', function (err) {
                reject(err);
            })
            .pipe(fs.createWriteStream(photoLink.name)
                .on('finish', function () {
                    fulfill(photoLink.name);
                }).on('error', function (err) {
                    reject(err);
                })
        );
    });
}
promise.all(photoLinks.map(function(photoLink) {
    return getPhoto(photoLink).then(function(result) {
        console.log(result);  //In ra từng kết quả của mỗi tác vụ
        return result;
    });
})).then(function(results) {  //Sau khi tất cả các tác vụ thanh cong
    console.log(results);
});

Luôn bechmark đo thời gian

Có rất nhiều giải pháp cho một yêu cầu trong NodeJS, hãy tìm – chọn con đường tối ưu: tốc độ thực thi, sử dụng tối thiểu tài nguyên CPU và bộ nhớ.

function now(txt) {
    console.log(new Date().toLocaleTimeString().replace(/T/, ' ').replace(/\..+/, '')+' '+txt);
}
now("Task A");

Trình cao hơn nữa thì đọc code của Gorgi Kosev, benchmark tốc độ các thư viện Promise A+ khác nhau. Chắc chắn bạn sẽ học hỏi được nhiều điều để viết đoạn mã benchmark đúng đắn.

Chọn lựa cẩn thận NodeJS module

NodeJS module có quá nhiều và miễn phí. Cần là kiếm, rồi tải về, sử dụng trong dự án. Một thời gian sau, ứng dụng của bạn quá nhiều NodeJS module. Tips khi dùng NodeJS module:

  • Hãy xem số lượt tải, ngày gần nhất được cập nhật trên github, số lượng contributor trên github. Có thể dùng thêm npmsearch.com để kiểm tra ranking của module
  • Hãy xem module bạn dùng có phụ thuộc vào modules nào khác (dependencies), có các modules nào phụ thuộc vào module này (dependents). Ví dụ tôi đã chọn dùng BlueBird mà không phải Q thì tôi sẽ ưu tiên dùng các module phụ thuộc BlueBird. Nếu chọn module phụ thuộc vào Q, thì dự án của tôi cũng sẽ dùng Q vào thời gian không xa.

Dùng đúng và đủ NodeJS module

  • Sự phụ thuộc chồng chéo càng lớn, ứng dụng của bạn sẽ càng nặng nề, nên nhớ 2 điều: khi load NodeJS module, thread hiện tại sẽ blocking cho đến khi load xong. NodeJS module load xong sẽ được cache bản chất là sẽ lưu lại trong bộ nhớ. Càng nhiều Node.js module, thì càng tốn bộ nhớ. Bạn sẽ phải móc nhiều tiền ra để trả gói VPS mắc tiền hơn !
  • Hãy để lệnh require lên đầu file javascript
  • Khi gõ install module nhớ bổ sung lựa chọn –save. Tên module được lưu lại trong package.json, khi cần bạn có thể duyệt lại xem dự án đã dùng những NodeJS module nào.

Fail Fast và Benchmark Your Self

Đừng quá tin vào những bài blog viết kiểu như MySQL nhanh Postgresql, hay như MongoDB nhanh cực. Google xem các bài viết so sánh tốc độ là một thói quen tốt. Nhưng đừng dễ dãi tin người quá. Vì vậy có 3 nguyên tắc sau:

  1. Fail Fast: công nghệ mới phải thử thách thực tế càng sớm càng tốt. Giả lập một số lượng dữ liệu đủ lớn để thử xem khả năng chịu tải của nó ra sao. 500,000 — 10,000,000 bản ghi là con số hợp lý, dùng script mà tạo dữ liệu. Thay vì deploy ứng dụng NodeJS vào tháng sau. Tại sao không phải là ngay ngày mai? Những lập trình cầu toàn thường không muốn release sớm sản phẩm còn đầy lỗi. Nhưng thực tế sản phẩm đầy lỗi sẽ kích thích toàn đội làm việc như điên để hoàn thiện. Vậy hãy phát hành ngay ngày mai !
  2. Eat Your Own Dog Food: dùng luôn sản phẩm (một phần hoặc tất cả) bạn định viết cho khách hàng, vào hoạt động của công ty. Đưa vào sử dụng càng sớm, càng phát hiện ra nhiều vấn đề tiềm tàng.
  3. Benchmark Your Self: đừng cảm tính khi sử dụng bất kỳ công nghệ nào. Hãy tạo một bảng có nhiều tiêu chí đánh giá và đánh trọng số cùng điểm của mỗi lựa chọn. Ví dụ 2 năm trước đây, phân vân giữa MySQL, Postgresql và MongoDB hay Rails, Phalcon, Play Framework, NodeJS. Sau những lần thử nghiệm, tôi chọn Postgresql và NodeJS vì nó phù hợp với project nhỏ.

Loại bỏ tất cả lệnh console.log khi chạy môi trường production

console.log, console.time, console.timeEnd, console.trace, console.error là những hàm rất hữu dụng để degub ứng dụng NodeJS. Tuy nhiên bản chất việc hiển thị màn hình console (terminal) là ghi vào string buffer. Nếu nhiều tác vụ (hay thread) cùng ghi vào một string buffer thì sẽ phải synchronize. Synchronize là biến chứa các hành động đồng thời cập nhật một biến thành chuỗi các hành động tuần tự để tránh việc dẫm chân lên nhau. Synchronize sẽ giảm tốc độ chung của hệ thống. Chốt lại trong môi trường production, hãy loại bỏ console.log, console.time, console.timeEnd, console.trace, console.error

if (process.env.ENV_VARIABLE == 'production'){
  console.log= function() {};
}

Cách khác nữa là thay vì sử dụng console.log thì sử dụng module debug để khi cần thì chạy chế độ debug và ngược lại thì bỏ qua

Hoặc có thể Debug bằng cách khác qua bài viết : Từng bước debug NodeJs trong Docker không dùng console.log

Kết luận

Qua bài viết, chúng ta đã hiểu được những điều để cải thiện tối ưu hóa app NodeJS. Hãy luôn áp dụng những điều trên vào bất cứ app và bất cứ project nào. Để được sản phẩm chạy nhanh, và khả năng ổn định tốt.


Like it? Share with your friends!

1058

0 Comments