Học lập trình web online bằng Node.js xin việc làm

JavaScript là ngôn ngữ dễ học. Node.js cũng dễ cài đặt. Module dùng sẵn trong Node.js thì sẵn. Node.js luôn được giới thiệu là một framework non-blocking I/O tốc độ xử lý rất nhanh. Tuy nhiên đã có lần TJ Holowaychuk , một lập trình viên trẻ, tác giả và đồng tác giả của rất nhiều Node.js module nổi tiếng như Express, KOA, vô cùng thất vọng mà viết bài Farewell Node.js gây xôn xao, chấn động giới lập trình Node.js. Mọi người trong đó có tôi e ngại, liệu Node.js có thể là một nền tảng ổn định để phát triển những ứng dụng nghiêm túc cho doanh nghiệp hay dịch vụ đám mây có tải lớn?

  • Có quá nhiều module Node.js viết cẩu thả.
  • Callback viết không cẩn thận là call back hell (lồng nhau nhiều cấp), Zalgo, gọi lại hai lần....
  • Đã có một thời kỳ Node.js được cập nhật một cách rất ỳ ạch (2013- 2014), tưởng như người ta đã rời bỏ Node.js
  • ...

Tuy nhiên ra phân tách hai tổ chức nodejs.orgiojs.org đã kích thích sự hồi phục vũ bão của Node.js. Trong bài viết này, tôi tập trung chia sẻ vài kinh nghiệm bản thân để làm sao lập trình ra những ứng dụng Node.js có tốc độ cao, tính ổn định tốt. Bài sẽ được cập nhật lại thường xuyên.

Nguyên tắc 1: Thinking in Non-Blocking I/O, Asynchronous Call

Cần thay đổi tư duy lập trình tuần tự, kiểu PHP sang cách gọi hàm asynchronous. 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, Q...)

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 trước mắt cứ xài ưu tiên xài Blue Bird trước. Hàm call back cổ điển có thể promisify để sử dụng Promise. Tham khảo thêm bài viết này nhé. Đ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);
});

Nguyên tắc 2: Luôn bechmark đo đếm thời gian

Có rất nhiều giải pháp cho một yêu cầu trong Node.js, 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ớ. Xem bài Đo thời gian chạy tác vụ khi lập trình Node.js
Hoặc sử dụng hàm dưới để in thời gian hiện tại.

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, psion, 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.

Nguyên tắc 3: Chọn lựa cẩn thận Node.js module

Node.js module có quá nhiều và miễn phí. Cần là kiếm, rồi tải về, cắm vào dự án. Một thời gian sau, ứng dụng của bạn cắm đầy những Node.js module. Mấy tips khi dùng Node.js 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 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 ở một điểm nào đó.

Nguyên tắc 4: Dùng đúng và đủ Node.js module cần thiết, tránh phụ thuộc trồng chéo

  • Sự phụ thuộc chồng chéo càng lớn, ứng dụng Node.js của bạn sẽ càng nặng nề, nên nhớ 2 điều: khi load node.js module, thread hiện tại sẽ blocking cho đến khi load xong. Node.js 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 ! Tình cho không, biếu không đôi khi nguy hiểm phết.
  • Hãy để lệnh require lên đầu file javascript
  • Khi gõ install module nhớ bổ xung 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 node.js module nào.

Nguyên tắc 5: Fail Fast and 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á. Công ty chúng tôi có 3 nguyên tắc:

  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 Node.js vào tháng sau. Tại sao không phải là ngay ngày mai? Những lập trình chu toàn thường không muốn phát 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, chúng tôi đã từng phân vân chọn giữa MySQL, Postgresql và MongoDB hay Rails, Phalcon, Play Framework, Node.js. Sau nhưng lần thử nghiệm, chúng tôi chọn Postgresql và Node.js vì nó phù hợp với công ty nhỏ, uyển chuyển như Techmaster. Một ví dụ nữa: Express là web framework được viết đã lâu. Còn KOA là một web framework mới được quảng cáo là không bị call back hell, chạy nhanh hơn. Chúng tôi viết thử luôn 2 ứng dụng web một trên Express và một trên KOA. Kết quả web dùng KOA chạy vừa chậm và tốn bộ nhớ gần gấp 1.5. Vậy là chúng tôi vẫn xài Express.

Nguyên tắc 6: Cố gắng loại bỏ tất cả lệnh console.log khi chạy Node.js app 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 để gỡ rồi ứng dụng Node.js. 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 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ả chứ Node.js mà hệ thống đa nhiệm, xử lý song song nào cũng vậy). 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ì thì chạy chế độ debug và ngược lại thì bỏ qua

Còn nữa...