Lập trình hướng sự kiện có thể làm cho ae mới bắt đầu cảm thấy khó khăn khi bắt đầu với Node.js. Nhưng đừng để điều đó ngăn cản. Trong bài viết này, tôi sẽ hướng dẫn cho ae về những điều căn bản về Node.js và giải thích vì sao nó trở nên phổ biến.

Giới thiệu

Để bắt đầu làm việc với Node.js, bạn trước tiên cần hiểu sự khác nhau giữa Node.js và các ngôn ngữ kịch bản truyền thống phía Server (như: PHP, Python, Ruby, …)

Lập trình bất đồng bộ

Rất có thể ae đã quen với lập trình không đồng bộ. Đó là A trong Ajax 😀 Mọi hàm trong Node.js là không đồng bộ Asynchronous. Do đó, các tác vụ đều được xử lý và thực thi ở chế độ nền (background processing). Ví dụ, nếu bạn đang đọc 1 file trên hệ thống, bạn cần phải định nghĩa 1 hàm callback mà nó sẽ được gọi lại để xử lý file khi quá trình đọc file hoàn tất.

Bạn làm tất tần tật!

Node.js chỉ là một môi trường – điều này có nghĩa bạn tự phải làm mọi thứ. Sẽ chẳng có bất kỳ máy chủ mặc định nào cả !!!! Một đoạn script xử lý tất cả các kết nối với clients. Điều này làm giảm đáng kể số lượng tài nguyên được sử dụng trong ứng dụng. Ví dụ, đoạn mã sau thực hiện trên ứng dụng Node.js:

var i, a, b, c, max;

max = 1000000000;

var d = Date.now();

for (i = 0; i < max; i++) {
    a = 1234 + 5678 + i;
    b = 1234 * 5678 + i;
    c = 1234 / 2 + i;
}

console.log(Date.now() - d);

Và đoạn sau tương đương viết lại bằng mã PHP:

$a = null;
$b = null;
$c = null;
$i = null;
$max = 1000000000;

$start = microtime(true);

for ($i = 0; $i < $max; $i++) {
    $a = 1234 + 5678 + $i;
    $b = 1234 * 5678 + $i;
    $c = 1234 / 2 + $i;
}

var_dump(microtime(true) - $start);

Hãy xem bảng so sánh sau đây: Bảng mô tả thời gian đáp ứng trên 2 ứng dụng Node.js và PHP (đo bằng mili giây).

Number of iterations Node.js PHP
100 2.00 0.14
10,000 3.00 10.53
1,000,000 15.00 1119.24
10,000,000 143.00 10621.46
1,000,000,000 11118.00 1036272.19

Tôi đã thử nghiệm trên giao diện dòng lệnh, và lấy kết quả trung bình cho 10 lần thử nghiệm. PHP đáng chú ý là chạy nhanh hơn với số lượng đầu vào lặp lại là nhỏ, nhưng ưu thế này bị mất đi nhanh chóng nếu ta tăng số lượng xử lý đầu vào. Gần như là PHP chậm hơn 93% so với Node.js!

Node.js sử dụng kiến trúc mô-đun để đơn giản hóa việc tạo ra các ứng dụng phức tạp.

Node.js là NHANH, nhưng bạn cần vọc 1 vài điều cơ bản để sử dụng nó đúng cách.

Modules

Node.js sử dụng kiến trúc mô-đun để đơn giản hóa việc tạo ra các ứng dụng phức tạp. Mô-đun là giống như các thư viện trong C, C#, Java, … Mỗi 1 module chứa 1 tập các hàm chức năng có liên quan đến một “đối tượng” của mô-đun. Ví dụ,  http  là mô-đun chứa các hàm cụ thể liên quan đến thiết lập HTTP. Node.js cung cấp 1 vài các mô-đun core kèm theo để hỗ trợ chúng ta truy cập files trên hệ thống, tạo các máy chủ HTTP, TCP/UDP, và các hàm tiện ích nhỏ hữu dụng khác.

Trước khi sử dụng mô-đun, ae đơn giản khai báo với hàm require(), như sau:

var http = require('http');

Node.js chỉ là môi trường, bạn phải tự làm tất tần tật

require() là hàm trả về tham chiếu tới 1 mô-đun cụ thể. Trong trường hợp của đoạn mã trên, chúng ta đang khai báo 1 tham chiếu tới mô-đun  http  và lưu nó vào biến http.

Trong đoạn mã trên, chúng truyền 1 tham số là tên của mô-đun. Điều này báo cho Node sẽ tìm 1 mô-đun tên là http trong thư mục node_modules của ứng dụng. Nếu nó không thấy, Node sẽ tiếp tục tìm mô-đun đó ở thư mục global cài đặt node.

Lệnh kiểm tra thư mục global cài đặt node_modules, bạn mở giao diện dòng lệnh CMD và gõ lệnh sau:

npm root -g

Quay trở lại vấn đề, bạn có thể chỉ rõ file bằng việc truyền vào tham số là đường dẫn tương đối  ./path/to/my/module.js  hoặc tuyệt đối  /path/to/my/module.js 

var myModule = require('./myModule.js');

Túm lại, mô-đun là các đoạn mã được đóng gói lại với nhau. Mã trong một mô-đun thường là private – nghĩa là các hàm, biến được định nghĩa và truy cập bởi bên trong của mô-đun. Nhưng, bạn có thể chìa ra các api là các hàm và/hoặc biến để sử dụng bên ngoài mô-đun. Bằng cách sử dụng 1 đối tượng exports, xem ví dụ sau đây:

var PI = Math.PI;
 
exports.dientich = function (r) {
  return PI * r * r;
};
 
exports.chuvi = function (r) {
  return 2 * PI * r;
};

Đoạn mã trên tạo ra 1 biến PI và nó chỉ có thể truy cập trong mô-đun ta đang định nghĩa. Bằng việc sử dụng exports để chìa ra 2 hàm sử dụng bên ngoài mô-đun là  dientich()  chuvi() . Như vậy, giả sử ta đang viết mã trên file ./myModule.js thì biến khai báo tham chiếu  myModule  có thể gọi hàm dientich()chuvi()

Global Scope

Node.js là môi trường cho phép lập trình sử dụng JavaScript ở phía server và chạy trên Google’s V8 JavaScript engine. Như vậy, chúng ta nên thực hiện các đoạn mã như khi sử dụng lập trình ở phía client. Ví dụ, chúng ta nên hạn chế sử dụng biến global. Tuy nhiên, nếu muốn sử dụng, bạn có thể dễ dàng tạo 1 biến global bằng việc định nghĩa tên biến không có từ khóa  var , như sau:

globalVariable = 1;
globalFunction = function () { ... };

Một lần nữa, biến global nên được hạn chế đến mức tối đa. Vì thế, hãy cần thận và nhớ sử dụng từ khóa var để khai báo biến.

Cài đặt Node.js

Đương nhiên, trước khi ae có thể viết được 1 ứng dụng node.js và thực thi nó. Thì điều đầu tiên ta cần phải làm là cài đặt. Hãy xem bài viết hướng dẫn cài đặt Node.js ở trang sau:

http://how.vndemy.com/nodejs/49-huong-dan-cai-dat-node-js/

Cài đặt Mô-đun

Node.js rất tuyệt vời và nó có 1 trình quản lý các gói mô-đun trên repositories của npmjs.org. Được gọi là Node Package Manager (NPM). Chương trình này tự động được cài đặt cùng với Node.js, bạn sẽ sử dụng NPM để cài đặt mới 1 mô-đun khi phát triển ứng dụng. Việc cài đặt này là cần thiết, và bạn ko thể tránh được việc sử dụng npm 😀 khi viết 1 ứng dụng tầm cỡ :))). Để cài đặt 1 mô-đun mới, hãy mở terminal trên linux hoặc CMD trên windows gõ lệnh sau:

npm install <module_name>

Ví dụ:

npm install express

Sau khi cài đặt xong, bạn sử dụng lệnh  dir node_modules  hoặc  ls -l node_modules  để xem gói mô-đun mà ta vừa cài đặt.

Viết chương trình Hello World

Hãy xem lại bài hướng dẫn cài đặt và viết chương trình Hello World theo link sau: http://how.vndemy.com/nodejs/49-huong-dan-cai-dat-node-js/

Thiết lập HTTP Server

Okay, chuyển sang phần nâng cao hơn 1 chút. Nó không phức tạp như bạn tưởng. Hãy cùng phân tích đoạn mã sau.

// Include http mô-đun.
var http = require("http");
 
// createServer: Hàm tạo http server với tham số vào là hàm sẽ được gọi xử lý khi có mỗi yêu cầu từ client đến.
// request: là biến chứa các tham số yêu cầu
// response: là biến cho phép bạn gửi những thứ hợp lệ tới client.
http.createServer(function (request, response) {
    console.log(" from " + request.url);
    // Ghi thông tin headers trả về
    // 200 là mã trạng thái HTTP (mã 200 có nghĩa là thành công)
    // Tham số thứ 2 là thông tin loại dữ liệu trả về dạng văn bản thuần: text/plain
    response.writeHead(200, {
        'Content-Type': 'text/plain'
    });
    // Gửi dữ liệu là thông điệp: Hello HTTP! cho client.
    response.end('Hello HTTP!');
// Server lắng nghe client ở cổng 8090.
}).listen(8090);

Đoạn mã trên khá đơn giản. Ae có thể gửi nhiều dữ liệu hơn bằng việc sử dụng hàm response.write(), nhưng hãy gọi nó trước hàm response.end(). Lưu lại đoạn mã trên với tên file là http.js và gõ lệnh sau trên terminal/cmd.

node http.js

Mở trình duyệt và gõ vào địa chỉ  http://localhost:8090 . Ae sẽ nhìn thấy 1 đoạn thông điệp là “Hello HTTP!” trên trang web.

Xử lý các tham số yêu cầu URL

Như tôi đã nói ở trên, chúng ta sẽ phải xử lý mọi thứ trong Node, bao gồm cả việc nhập thông tin tham số request. Nó cũng đơn giản thôi. Hãy xem đoạn xử lý sau đây:

// Include http mô-đun, 
var http = require("http")
    // Và mô-đun url, nó sẽ hỗ trợ nhập các tham số request.
    , url = require("url"); 
 
// Tạo http server. 
http.createServer(function (request, response) { 
    console.log(" from " + request.url);
    // Nhập các tham số request và lưu trữ vào biến _get.
    var _get = url.parse(request.url, true).query; 
    // Ghi thông tin headers. 
    response.writeHead(200, { 
        'Content-Type': 'text/plain'
    }); 
    // Gửi dữ liệu cho client.
    response.end('Here is your data: ' + _get['data']); 
// Lắng nghe client ở cổng 8090 
}).listen(8090);

Ở trên, ta sử dụng hàm parse() của mô-đun url, mô-đun core của Node.js, sử dụng để nhập và chuyển đổi các tham số yêu cầu URL sang 1 object. Đối tượng trả về có 1 property là  query  chứa các tham số URL. Lưu lại đoạn mã trên vào file get.js và run nó với lệnh sau:

node get.js

Sau khi run đoạn mã đó, ta đã tạo 1 http server, hãy mở trình duyệt và nhập địa chỉ  http://localhost:8090/?data=Xin_Chao . Enter và xem thông tin trả về, hãy thay đổi giá trị truyền vào của tham số data ?

Đọc và ghi File

Quản lý files trong Node, chúng ta sử dụng mô-đun core là  fs . Đọc và ghi file sẽ sử dụng tương ứng các hàm fs.readFile()fs.writeFile(). Tiếp tục với ví dụ ở trên, chúng ta sẽ tạo ra 1 http server, khi có yêu cầu đến từ client thì hãy trả cho họ nội dung của 1 file test.txt trên hệ thống:

// Khai báo mô-đun http,
var http = require("http")
    // Và sử dụng mô-đun quản lý file hệ thống fs
    , fs = require("fs");
 
// Tạo http server.
http.createServer(function (request, response) {
  console.log(" from " + request.url);
  fs.readFile("test.txt", 'utf-8', function (error, data) {
      // Tăng 1 đơn vị nội dung hiện tại có trong file
      console.log(data);
      data = parseInt(data || 0) + 1;
      // Ghi dữ liệu vào file sử dụng hàm writeFile.
      fs.writeFile('test.txt', data);
      // Ok, trả về 1 thông điệp cho client
      response.end('This page was refreshed ' + data + ' times!');
  });
// Server lắng nghe yêu cầu client ở cổng 8090.
}).listen(8090);

Lưu lại đoạn mã trên vào file testfiles.js. Mỗi khi server nhận 1 request, đoạn script sẽ đọc 1 số từ 1 file tên là test.txt và tăng nó lên 1 đơn vị (nếu dữ liệu null thì được gán mặc định là 0). Hàm đọc file fs.readFile() chứa 3 tham số: tên file, bảng mã, và hàm callback xử lý file khi đọc xong.

Hàm ghi file fs.writeFile() thì nó đơn giản hơn là có 2 tham số: tên file để lưu và dữ liệu để ghi vào file. Ở đây chúng ta sẽ ghi giá trị mới vào file sau khi đã tăng nó lên 1 từ lần đọc trước.

Okay, hãy thử chạy đoạn mã trên và tạo server http đọc file:

node testfiles.js

Hãy mở trình duyệt và gõ vào địa chỉ  http://localhost:8090  và f5 refresh trình duyệt vài lần. Bạn sẽ thấy con số trên trình duyệt thay đổi và mỗi lần tăng 2 đơn vị (thay vì 1). Ok? Đó có phải là lỗi? Hãy để ý đến log trên giao diện console, bạn sẽ thấy đã có 2 request gửi đến server từ trình duyệt, bao gồm request lấy file http://localhost:8090/favicon.ico, và đương nhiên, request thứ 2 là http://localhost:8090/

Ae đã hiểu vì sao ta chỉ f5 1 lần trên trình duyệt mà server nhận được 2 request??? Hãy thay đổi lại hàm xử lý để 1 lần f5 chỉ có 1 lần tăng trên file? Bạn làm được !!!!

Truy cập cơ sở dữ liệu

Hầu hết tất cả các công nghệ phía server đều tích hợp sẵn các kết nối truy xuất cơ sở dữ liệu. Với Node.js bạn cần phải cài đặt thêm thư viện, tùy xem chúng ta sử dụng csdl nào ? Giả sử là MySQL thì mô-đun tương ứng cần cài đặt là mysql@2.0.0-alpha2 (đây là tên đầy đủ của 1 mô-đun và chứa số hiệu phiên bản kèm theo sau dấu @). Mở giao diện console và gõ lệnh sau:

npm install mysql@2.0.0-alpha2

Gói mysql sẽ được tải xuống và được cài đặt vào thư mục node_modules ở thư mục hiện tại. Hãy xem đoạn mã sau đây, chúng ta sẽ tương tác với cơ sở dữ liệu MySQL:

// Sử dụng mô-đun mysql
var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  database : 'db_name',
  user     : 'root',
  password : ''
});

connection.connect();

connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;

  console.log('The solution is: ', rows[0].solution);
});

connection.end();

Trên đây là 1 đoạn mã (giả lập), và ae thấy được cách thiết lập kết nối tới DB như sau:

  • Mọi truy vấn trên 1 kết nối được đưa vào hàng đợi và thực thi theo thứ tự
  • Đóng kết nối khi hoàn tất 1 thao tác đọc dữ liệu để đảm bảo tất cả truy vấn đã được thực thi trước khi gửi ngắt kết nối tới mysql server.

Tham khảo thêm tại: https://www.npmjs.org/package/mysql

Kết luận

Node.js yêu cầu rất nhiều công việc phụ (au tô mát tay), nhưng đây chính là lợi điểm giúp cho ứng dụng node.js chạy nhanh và mạnh mẽ. Nếu bạn không muốn làm tất cả công việc này ở level thấp nhất, 1 lựa chọn đáng giá là sử dụng 1 vài framework, ví dụ như Express để làm cho quá trình phát triển ứng dụng của bạn nhanh hơn và dễ dàng hơn. 😀

Node.js là một công nghệ đầy hứa hẹn và là 1 sự lựa chọn tuyệt vời cho 1 ứng dụng tải cao. Nó đã được chứng mình bởi các tập đoàn lớn, như Microsoft, eBay, và Yahoo. Nếu bạn không đảm bảo được 1 nơi lưu trữ cho ứng dụng/website, bạn có thể sử dụng giải pháp VPS hoặc các dịch vụ cloud-based miễn phí, giá rẻ, như Microsoft Azure, Amazon EC2, Heroku. Các dịch vụ này đều dễ dàng mở rộng tương ứng với các mức phí dịch vụ của nó.

Mọi hàm trong Node.js là bất đồng bộ.

Hãy comment và tranh luận những vấn đề bạn quan tâm hay có những thắc mắc nhé !

About The Author