“Nếu nginx không đặt trước ứng dụng node server của bạn, có lẽ bạn đang thực hiện sai” theo như Bryan Hugh trên Twitter.

Node.js là công cụ hàng đầu cho việc tạo ra các ứng dụng server bằng JavaScript, ngôn ngữ lập trình phổ biến nhất thế giới. Cung cấp chức năng của cả một web server và một ứng dụng server, Node.js hiện nay được xem là một công cụ quan trọng cho tất cả các nền tảng microservices và phân tán. (Bạn có thể download một bản báo cáo nghiên cứu Forrester miễn phí về Node.js và NGINX tại đây).

Node.js có thể thay thế hoặc bổ sung cho Java hoặc .NET trong phát triển các ứng dụng backend.

Node.js là single-threaded và sử dụng non-blocking I/O, cho phép nó mở rộng và hỗ trợ hàng chục nghìn các thao tác đồng thời. Nó có chung những đặc điểm kiến trúc với NGINX và giải quyết 10,000 vấn đề đồng thời- hỗ trợ hơn 10,000 kết nối đồng thời - Điều mà NGINX cũng được phát minh để giải quyết. Node.js nổi tiếng với tốc độ cao và giúp các nhà phát triển làm việc năng suất hơn.

Vậy thì, có vấn đề gì ở đây?

Node.js có một vài điểm yếu và lỗ hổng làm cho hệ thống dựa trên Node trở nên kém hiệu quả hoặc thậm chí bị treo. Vấn đề phát sinh thường xuyên hơn khi một ứng dụng web nền tảng Node.js có lượng traffic tăng trưởng nhanh chóng.

Ngoài ra, Node.js là một công cụ tuyệt vời cho việc tạo và chạy ứng dụng logic, cái tạo ra core, nội dung động cho trang web của bạn. Nhưng nó không tuyệt vời như vậy khi phục vụ nội dung tĩnh - Ví dụ như, các hình ảnh và các file JavaScript hoặc cân bằng tải trên nhiều máy chủ.

Để loại bỏ hầu hết điều đó ra khỏi Node.js, bạn cần cache nội dung tĩnh, để proxy và cân bằng tải giữa nhiều máy chủ ứng dụng, và quản lý cổng kết nối giữa các client, Node.js và các thành phần trợ giúp khác, chẳng hạn như các server chạy Socket.IO. NGINX có thể được sử dụng cho tất cả các mục đích này, tạo cho nó trở thành một công cụ tuyệt vời cho việc điều chỉnh hiệu suất Node.js.

Sử dụng các bí quyết sau đây để cải thiện hiệu suất của ứng dụng Node.js:

  1. Triển khai một reverse proxy server.
  2. Cache các file tĩnh.
  3. Cân bằng tải qua nhiều máy chủ.
  4. Các kết nối WebSocket proxy.
  5. Thực hiện SSL/TLS và HTTP/2.

Chú ý: Một cách cải thiện tốc độ nhanh là chỉnh lại cấu hình Node.js của bạn để nhận được lợi thế của các server nhiều nhân (multi-core). Đọc bài viết này để học cách chia Node.js thành các tiến trình con (child processes) - bằng với số CPU trên máy chủ web. Mỗi tiến trình sau đó sẽ ở trên một nhân của CPU, cho bạn có một sự cải thiện lớn trong hiệu năng.

Để xây dựng web site chuyên nghiệp trên Node.js tham khảo khoá học Node.js xây dựng web site tốc độ cao và Arrowjs.io Core - CMS

1. Triển khai một Reverse Proxy Server

Tại NGINX, Inc chúng tôi luôn luôn sợ hãi khi thấy các máy chủ ứng dụng web tiếp xúc trực tiếp với traffic đến từ Internet. Ví dụ như nó gồm nhiều trang web dựa trên WordPress cũng như các trang Node.js.

Node.js được thiết kế cho quy mô, để mở rộng tốt hơn hầu hết các ứng dụng server và phía web server có thể xử lý tốt một lượng lớn traffic. Nhưng phục vụ web không thực sự là cái được xây dựng để thực hiện.
  
Nếu bạn có một site traffic cao, bước đầu tiên trong việc tăng hiệu suất ứng dụng là đặt một reverse proxy server trước máy chủ Node.js của bạn. Điều này bảo vệ máy chủ Node.js tiếp xúc trực tiếp với traffic từ Internet và cho phép bạn rất linh hoạt trong sử dụng nhiều máy chủ ứng dụng, trong cân bằng tải trên nhiều  máy chủ, và caching nội dung.

Học lập trình web trực tuyến cơ bản đến nâng cao

Đặt NGINX trước một máy chủ thiết lập như một reverse proxy server, là một trường hợp sử dụng NGINX, triển khai bởi hàng triệu website trên khắp thế giới.

Các lợi ích cụ thể khi sử dụng NGINX như một reverse proxy server Node.js gồm có:

  • Đơn giản hóa xử lý đặc quyền (privilege) và gán cổng.
  • Phục vụ các file tĩnh hiệu quả hơn (giải thích ở phần tiếp theo).
  • Quản lý treo Node.js một cách thành công.
  • Giảm đi các tấn công DoS

Chú ý: Những bài hướng dẫn cách sử dụng NGINX như một reverse proxy server trong môi trường Ubuntu 14.04 hoặc CentOS, cung cấp cái nhìn tổng quan cho những ai muốn đặt NGINX trước Node.js.

2. Cache các file tĩnh

Khi việc sử dụng một site dựa trên nền tảng Node.js tăng lên, các máy chủ bắt đầu có biểu hiện quá tải. Có 2 thứ bạn muốn làm ở điểm này:

  1. Loại bỏ hầu hết ra khỏi server Node.js
  2. Làm cho nó dễ dàng thêm các server ứng dụng và cân bằng tải giữa chúng.

Điều này thực sự dễ dàng. Bắt đầu bằng việc triển khai NGINX như một reverse proxy server, như đã mô tả ở phần trước. Điều này làm cho nó dễ dàng thực hiện caching, cân bằng tải (khi bạn có nhiều máy chủ Node.js), và còn nhiều nữa.

Website cho Modulus, một nền tảng chứa ứng dụng, có một bài viết hữu ích tăng hiệu suất ứng dụng Node.js với NGINX. Với Node.js làm tất cả công việc của nó, trang chủ có thể phục vụ trung bình gần 900 request/giây. Với NGINX như một reverse proxy server, phục vụ nội dung tĩnh, trang web đó có thể phục vụ hơn 1600 request/giây - hiệu năng được cải thiện gần gấp đôi.

Gấp đôi hiệu suất khiến cho bạn có thêm thời gian để thực hiện các bước đạt được tăng trưởng hơn nữa, chẳng hạn rà soát (và có thể cải thiện) thiết kế site của bạn, tối ưu code, và triển khai thêm các máy chủ ứng dụng.

Sau đây là cấu hình cho các website đang chạy trên Modulus:

server {
  listen 80;
  server_name static-test-47242.onmodulus.net;

  root /mnt/app;
  index index.html index.htm;

  location /static/ {
       try_files $uri $uri/ =404;
  }

  location /api/ {
       proxy_pass http://node-test-45750.onmodulus.net;
  }
}

Đây là chi tiết bài viết của Patrick Nommensen về NGINX, Inc giải thích cách anh ấy cache nội dung tĩnh từ blog cá nhân của mình, chạy trên nền tảng blog mã nguồn mở Ghost, cũng là một ứng dụng Node.js. Mặc dù một vài chi tiết là của Ghost, bạn vẫn có thể tái sử dụng code cho các ứng dụng Node.js khác.

Ví dụ, trong khối NGINX location, bạn sẽ thường muốn bỏ qua một vài nội dung được cached. Bạn không muốn cache giao diện phần quản trị của một nền tảng blog chẳng hạn. Đây là code cấu hình để vô hiệu cahing giao diện phần quản trị của Ghost:

location ~ ^/(?:ghost|signout) { 
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ghost_upstream;
        add_header Cache-Control "no-cache, private, no-store,
        must-revalidate, max-stale=0, post-check=0, pre-check=0";
    }

Các thông tin chung về các nội dung tĩnh, đọc NGINX Plus Admin Guide. Admin Guide gồm các hướng dẫn cấu hình, khi cố gắng tìm kiếm một file, sẽ có nhiều tùy chọn trả về thành công hoặc lỗi và tối ưu hóa cách tiếp cận để đạt được hiệu suất nhanh hơn.

Caching các file tĩnh trên máy chủ NGINX giảm tải đáng kể cho máy chủ ứng dụng Node.js, cho phép nó đạt được hiệu suất cao hơn.

3. Thực hiện cân bằng tải

Đây là chìa khóa thực sự cho hiệu suất cao - gần như là không giới hạn cho các ứng dụng Node.js, để chạy nhiều máy chủ ứng dụng và cân bằng tải giữa chúng.

Cân bằng tải Node.js có thể khó khăn bởi vì Node.js cho phép tương tác mức độ cao giữa code JavaScript đang chạy trong trình duyệt và code JavaScript đang chạy trên máy chủ ứng dụng Node.js, với các đối tượng JSON như là phương tiện trao đổi dữ liệu. Điều này ngụ ý rằng một session client được đưa ra tiếp tục chạy trên một server ứng dụng cụ thể và sự ổn định session khó mà thừa kế để đạt cùng với các server ứng dụng. 

Một trong những lợi ích chính của Internet và web là statelessness, bao gồm khả năng hoàn thành các request của client bởi bất kỳ máy chủ nào với file đã được yêu cầu. Node.js phá vỡ statelessness và làm việc tốt nhất trong môi trường stateful, nơi cùng một máy chủ trả về cho các request tới từ bất kỳ client nào.

Yêu cầu này có thể được đáp ứng tốt nhất bởi NGINX Plus, hơn cả phần mềm mã nguồn mở. Dù cả hai phiên bản NGINX khá giống nhau, nhưng điểm khác nhau chính là chúng hỗ trợ các thuật toán cân bằng tải khác nhau.

NGINX hỗ trợ các phương thức cân bằng tải stateless:

  • Round Robin. Một request mới tới máy chủ tiếp theo trong một danh sách
  • Least Connections. Một request mới tới máy chủ có số connection hoạt động ít nhất.
  • IP Hash. Một request mới tới máy chủ đã được gán một hash địa chỉ IP của client

Duy nhất một trong các phương thức, IP Hash gửi các request của client tới cùng một máy chủ, là lợi ích của các ứng dụng Node.js. Tuy nhiên, IP Hash có thể dễ dàng dẫn tới một máy chủ nhận được số lượng các request không cân đối với các máy chủ khác, như mô tả trong bài viết này về kỹ thuật cân bằng tải. Phương thức này hỗ trợ statefulness khả năng phân bổ tối ưu các request tài nguyên.

Không giống như NGINX, NGINX Plus hỗ trợ sự ổn định của session. Với việc sử dụng sự ổn định của session, một máy chủ tin cậy nhận tất cả các request từ một client. Cùng lợi thế của Node.js với sự giao tiếp stateful giữa client và server, và của NGINX Plus với lợi thế cân bằng tải cao, đều được tối đa.

Vì thế bạn có thể sử dụng NGINX hoặc NGINX Plus để hỗ trợ cân bằng tải trên nhiều máy chủ Node.js. Tuy nhiên chỉ với NGINX Plus bạn mới có thể đạt được tối đa cả hiệu suất cân bằng tải và statefulness thân thiện với Node.js. 

NGINX Plus cũng hỗ trợ từng session, cho phép một máy chủ ứng dụng hoàn thành session vừa làm việc sau khi một request tự đưa nó ra khỏi phục vụ.

4. Các kết nối Proxy WebSocket

HTTP, trong tất cả các phiên bản, được thiết kế cho các giao tiếp "kéo (pull)", nơi các file request client từ máy chủ. WebSocket là một công cụ có thể có các giao tiếp "đẩy (push)" và "đẩy/kéo (push/pull)", nơi  máy chủ có thể chủ động gửi các file mà client không yêu cầu.

Giao thức WebSocket làm cho nó dễ dàng hơn để hỗ trợ tương tác mạnh mẽ hơn giữa client và server trong khi giảm lượng dữ liệu trao đổi và giảm thiểu độ trễ. Khi cần thiết, một kết nối ghép đôi (full-duplex), với client và server cùng khởi tạo và nhận các request khi cần thiết.

Giao thức WebSocket có một giao diện JavaScript mạnh mẽ và phù hợp với Node.js như là máy chủ ứng dụng và cho các ứng dụng web với khối lượng giao dịch vừa phải, cũng như với máy chủ web. Khi khối lượng giao dịch tăng lên, nó cần chèn NGINX giữa client và máy chủ web Node.js, sử dụng NGINX hoặc NGINX Plus để cache các file tĩnh và cân bằng tải giữa nhiều máy chủ ứng dụng.

Node.js thường sử dụng kết hợp với Socket.IO - một WebSocket API trở nên khá phổ biến để sử dụng cùng các ứng dụng Node.js. Điều này dẫn đến cổng 80 (HTTP) hoặc cổng 443 (HTTPS) bị đầy, và giải pháp là trung gian (proxy) cho máy chủ Socket.IO. Bạn có thể sử dụng NGINX cho máy chủ trung gian, như đã mô tả ở trên, và cũng có những chức năng bổ sung chẳng hạn như caching file, cân bằng tải, và nhiều hơn nữa.

Học lập trình web online cơ bản đến nâng cao

Theo như đoạn code sau cho một file ứng dụng server.js chạy ở cổng 5000. Nó hoạt động như một máy chủ trung gian (proxy) (không phải một web server) và điều hướng các request đến đúng cổng:

var io = require('socket.io').listen(5000);

io.sockets.on('connection', function (socket) {
  socket.on('set nickname', function (name) {
    socket.set('nickname', name, function () {
      socket.emit('ready');
    });
  });

  socket.on('msg', function () {
    socket.get('nickname', function (err, name) {
      console.log('Chat message by ', name);
    });
  });
});

Trong file index.html, thêm đoạn code sau để kết nối tới ứng dụng máy chủ của bạn và nhanh chóng thêm một WebSocket giữa ứng dụng và trình duyệt của người dùng.

<script src="/socket.io/socket.io.js"></script>
<script>
     var socket = io(); // your initialization code here.
</script>

Để được hướng dẫn đầy đủ, gồm cấu hình NGINX, xem bài viết của chúng tôi hướng dẫn sử dụng NGINX và NGINX Plus với Node.js và Socket.IO. Để biết sâu hơn các vấn đề về kiến trúc tiềm năng và cơ sở hạ tầng cho các ứng dụng web, xem cả bài viết của chúng tôi về các ứng dụng web thời gian thực và WebSocket.

5. Triển khai SSL/TLS và HTTP/2

Ngày càng có nhiều web site sử dụng SSL/TLS để bảo mật cho tất cả các giao diện người dùng trên trang. Đó là quyết định của bạn, sử dụng nó hay không, nhưng nếu bạn sử dụng nó, NGINX hỗ trợ chuyển đổi theo hai cách:

  1. Bạn có thể chấm dứt một kết nối SSL/TLS tới client trong NGINX, khi bạn thiết lập NGINX như một reverse proxy. Máy chủ Node.js gửi và nhận các request không mã hóa và trả lại nội dung cho NGINX reverse proxy server.
  2. Đầu tiên cho thấy việc sử dụng HTTP/2, một phiên bản mới của giao thức HTTP, có một lượng lớn hoặc toàn bộ sử dụng SSL/TLS NGINX hỗ trợ HTTP/2 và bạn có thể chấm dứt HTTP/2 cùng với SSL, một lần nữa loại bỏ bất kì thay đổi nào trong máy chủ ứng dụng Node.js.

Trong các bước cần thực hiện bạn cần cập nhật URL trong file cấu hình Node.js, thiết lập và tối ưu bảo mật các kết nối trong cấu hình NGINX của bạn, và sử dụng SPDY hoặc HTTP/2 nếu muốn. Thêm hỗ trợ HTTP/2 nghĩa là các phiên bản trình duyệt cũng phải hỗ trợ kết nối HTTP/2  giao tiếp với ứng dụng của bạn sử dụng giao thức mới, các phiên bản trình duyệt cũ hơn tiếp tục sử dụng HTTP/1.x.

Học lập trình web trực tuyến chất lượng nhất

Theo như cấu hình đoạn code sau đây dành cho Ghost blog sử dụng SPDY, như mô tả ở đây. Nó gồm các tính năng cao chẳng hạn như gắn với OCSP. Các xem xét quanh việc sử dụng NGINX cho việc chấm dứt SSL, bao gồm lựa chọn gắn với OCSP, xem ở đây. Để có cái nhìn tổng quan cho chủ đề này xem ở đây.

Bạn chỉ cần tạo ra một thay đổi nhỏ để cấu hình ứng dụng Node.js và nâng cấp từ SPDY tới HTTP/2, ngay bây giờ hoặc khi hỗ trợ cho SPDY chấm dứt vào năm 2016.

server {
   server_name domain.com;
   listen 443 ssl spdy;
   spdy_headers_comp 6;
   spdy_keepalive_timeout 300;
   keepalive_timeout 300;
   ssl_certificate_key /etc/nginx/ssl/domain.key;
   ssl_certificate /etc/nginx/ssl/domain.crt;
   ssl_session_cache shared:SSL:10m;  
   ssl_session_timeout 24h;           
   ssl_buffer_size 1400;              
   ssl_stapling on;
   ssl_stapling_verify on;
   ssl_trusted_certificate /etc/nginx/ssl/trust.crt;
   resolver 8.8.8.8 8.8.4.4 valid=300s;
   add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
   add_header X-Cache $upstream_cache_status;
   location / {
        proxy_cache STATIC;
        proxy_cache_valid 200 30m;
        proxy_cache_valid 404 1m;
        proxy_pass http://ghost_upstream;
        proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
        proxy_ignore_headers Set-Cookie;
        proxy_hide_header Set-Cookie;
        proxy_hide_header X-powered-by;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Host $http_host;
        expires 10m;
    }
    location /content/images {
        alias /path/to/ghost/content/images;
        access_log off;
        expires max;
    }
    location /assets {
        alias /path/to/ghost/themes/uno-master/assets;
        access_log off;
        expires max;
    }
    location /public {
        alias /path/to/ghost/built/public;
        access_log off;
        expires max;
    }
    location /ghost/scripts {
        alias /path/to/ghost/core/built/scripts;
        access_log off;
        expires max;
    }
    location ~ ^/(?:ghost|signout) { 
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ghost_upstream;
        add_header Cache-Control "no-cache, private, no-store,
        must-revalidate, max-stale=0, post-check=0, pre-check=0";
        proxy_set_header X-Forwarded-Proto https;
    }
}

Kết luận

Bài viết này mô tả một vài cách cải thiện hiệu suất quan trọng nhất mà bạn có thể làm trong các ứng dụng Node.js của mình. Nó tập trung vào việc bổ sung NGINX tới các ứng dụng của bạn cùng với Node.js - bằng cách sử dụng NGINX như một reverse proxy server, để cache các file tĩnh, cân bằng tải, các kết nối trung gian (proxying) Websocket, và chấm dứt các giao thức SSL/TLS và HTTP/2.

Kết hợp NGINX và Node.js được công nhận rộng rãi như một cách tạo các ứng dụng microservices - thân thiện hoặc thêm tính linh hoạt và khả năng cho các ứng dụng có sẵn dựa trên SOA sử dụng Java hoặc Microsoft .NET. Bài viết này giúp bạn tối ưu hóa các ứng dụng Node.js của mình và nếu bạn chọn, hãy mang theo sự phối hợp giữa Node.js và NGINX tới cuộc sống.

Bản dịch của Trịnh Thị Khánh và Vũ Kim Thành, lập trình viên Node.js tại TechMaster
Nguồn: https://www.nginx.com/blog/5-performance-tips-for-node-js-applications/#gs.zkGGWxk