Tự xây dựng hệ thống cân bằng tải với Nginx

1
Dịch vụ dạy kèm gia sư lập trình

Dạo gần đây mình lướt Facebook thấy anh em hay gặp vấn đề performance với ứng dụng Node.js. Kiểu như ứng dụng mới chỉ phục vụ được vài chục request đồng thời đã làm máy chủ “lê lết”.

Tất nhiên, performance là một vấn đề phức tạp, bị ảnh hưởng bởi nhiều yếu tố, có thể kể:

  • Code “cùi”, không được tối ưu hiệu năng.
  • Server “cùi”, cấu hình quá yếu.
  • Không có giải pháp cache nào.
  • v.v…

Khi bạn làm một ứng dụng web, mục tiêu tối thượng cuối cùng là càng có nhiều người truy cập càng tốt. Đúng không?

Mà đến khi có nhiều người truy cập mà máy chủ lăn ra “chết” thì thật là buồn phải không!

Một trong các giải pháp tăng khả năng chịu tải của máy chủ, đó chính là cân bằng tải cho máy chủ (thuật ngữ chuyên ngành là Load Balancing).

Vậy cân bằng tải là gì? Cách triển khai hệ thống cân bằng tải như thế nào? Bài viết này mình sẽ hướng dẫn các bạn chi tiết cách làm với sự trợ giúp của Nginx.

Nginx là gì?

Nginx là một phần mềm mã nguồn mở, có thể đóng nhiều vai trò trong hệ thống. Nginx được sử dụng nhiều nhất với vai trò là một web server với hiệu suất cao.

Một số ứng dụng của Nginx phổ biến:

  • Webserver cho các ứng dụng website (phổ biến nhất là WordPress)
  • Proxy server cho hệ thống email (IMAP, POP3, và SMTP).
  • Cân bằng tải – Load balancer cho HTTP, TCP, và UDP servers.
  • Quản lý Content Cache tăng hiệu năng cho hệ thống.
  • Hỗ trợ WebSockets.
  • Hỗ trợ Security Controls (ví dụ: thiết lập giới hạn kết nối từ một địa chỉ IP).

NGINX ra mắt vào năm 2004 bởi lập trình viên lão luyện Igor Sysoev. Ngix bắt đầu được nghiên cứu từ năm 2002 để giải quyết vấn đề C10k. C10k là giới hạn của việc xử lý 10 ngàn kết nối cùng lúc. Ngày nay, các web server hiện đại, việc xử lý bài toán C10K đã quá bình thường.

NGINX sử dụng kiến trúc hướng sự kiện (Event-Driven), bất đồng bộ (Asynchronous) và có khả năng mở rộng. Với ưu điểm về tốc độ nên Ngix đặc biệt sử dụng trong các trường hợp cần phục vụ nội dung tĩnh (hình ảnh, css, js …) và các truy vấn đồng thời số lượng lớn.

Kiến trúc của Ngix
Kiến trúc của Ngix

Cài đặt Ngix

Bạn có thể download nginx cho hệ thống từ trang chủ.

Nếu bạn đang dùng Ubuntu thì không cần phải download thủ công, bạn chỉ cần gõ câu lệnh sau để cài đặt luôn:

sudo apt-get update && sudo apt-get install nginx

Sau khi cài đặt thành công, bạn có thể start Ngix service bằng lệnh: sudo service nginx start.

Để kiểm tra, bạn gõ lệnh sau:

$ curl localhost
//Kết quả sau khi chạy
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>

Đơn giản vậy thôi. Cài đặt Ngix không khó, nhưng cấu hình để Ngix chạy mượt và đúng yêu cầu ứng dụng mới là nghệ thuật.

Load balancing là gì?

Load balancing là giải pháp phân bố lượng traffic giữa hai hay nhiều server có cùng chức năng trong hệ thống.

Với giải pháp này, chúng ta hạn chế tối đa tình trạng một máy chủ bị quá tải và phải dừng hoạt động. Bạn hiểu đơn giản, nếu 1 triệu truy cập cùng đổ vào 1 server thì nó chết chắc. Nhưng nếu dàn trải cho 3 server thì mỗi con chỉ chịu có vài trăm nghìn truy cập thì lại OK.

Máy chủ cân bằng tải (load balancer) là máy chủ nằm giữa client và ứng dụng web. Nó sẽ quyết định điều hướng truy cập vào server chứa ứng dụng đang sẵn sàng.

Bạn nhìn hình dưới đây sẽ dễ hiểu hơn.

load balancing - Cân bằng tải
Mô hình hệ thống cân bằng tải đơn giản

Xây dựng hệ thống cân bằng tải với Ngix

Phần tiếp theo, chúng ta cùng nhau triển khai một bộ cân bằng tải cho ứng dụng Node.js nhé.

Tạo một ứngdụng Node.js đơn giản

Giờ mình sẽ tạo một ứng dụng Node.js đơn giản để mình họa cho bài viết này.

Tạo mới dự án:

mkdir nodeBalanceDemo && cd nodeBalanceDemo
npm init --y

Cài đặt Express framework:

npm i --S express

Sau khi tạo xong dự án, chúng ta tạo một node server đơn giản như sau:

const express = require('express');
const router = express.Router();
const app = express();

router.get('/', (req,res) => {
    res.send('Hello');
});

app.use('/', router);

app.listen(process.argv[2] || process.env.PORT || 3000, () => {
    console.log(App is listening at ${process.argv[2] || process.env.PORT || 3000});
});

Đoạn code trên rất đơn giản, chỉ có một route duy nhất và lắng nghe port 3000.

Sách học lập trình Node.js thật đơn giản
Góc quảng cáo! Bọn mình đã hoàn thành một cuốn sách học lập trình Node.js đặc biệt dành cho bạn đây. Với cuốn sách này bạn sẽ làm chủ Node.js bằng cách thực hành một dự án từ A-Z. Ngại gì mà không thử!
Đọc ngay

Để tạo nhiều instance Node, chúng ta cần tới một trình quản lý process. Mình khuyến khích sử dụng PM2 để làm điều này.

Đầu tiên, chúng ta tiến hành cài đặt PM2:

npm i --g pm2

Ok, giả sử chúng ta muốn tạo 4 server instances (được hiểu là có 4 servers trong hệ thống cân bằng tải).

pm2 start app.js -f --3000
pm2 start app.js -f --3001
pm2 start app.js -f --3002
pm2 start app.js -f --3003

Sau khi đã có 4 servers, chúng ta tiến hành cấu hình Nginx để thực hiện bằng tải giữa 4 servers này.

Cấu hình Ngix

Trong thư mục cấu hình của Nginx (/etc/nginx/conf.d), bạn tạo một file cấu hình với nội dung sau:

upstream app_servers {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3002;
}

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;
    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;
        proxy_pass         http://app_servers;
    }
}
Lưu ý: Thay thế domain bằng domain thực tế của bạn nhé.

Mọi thay đổi cấu hình đều phải restart lại Nginx thì mới có hiệu lực.

sudo service nginx restart

Để kiểm tra xem hệ thống cân bằng tải đã hoạt động chưa? Bạn mở PM2 log bằng câu lệnh: pm2 logs all

Sau đó truy cập vào ứng dụng thông qua trình duyệt như bình thường. Bạn kiểm tra log sẽ thấy, các request được nhận và chuyển tới 4 node application server theo vòng tròn.

Theo mặc định, Nginx sử dụng thuật toán Round-robin (vòng tròn) cho load balancing.

Các thuật toán cân bằng tải phổ biến

Thuật toán cân bằng tải chính là thuật toán dùng để load balancer quyết định điều hướng request tới máy chủ ứng dụng nào. Có 3 thuật toán cơ bản:

  • Round-robin (mặc định).
  • Least connections.
  • Least time.

Mình sẽ giải thích sơ lược từng thuật toán nhé.

1. Round-robin

Đây là thuật toán mặc định và cũng là đơn giản nhất. Trong đó, Nginx sẽ điều hướng các request tới các máy chủ theo vòng tròn, lần lượt từng server sẽ nhận request.

Với thuật toán này thì Nginx không quan tâm tới máy chủ nhận request có đang bận hay không? có đang quá tải so với các máy chủ khác hay không? Cứ đến lượt là phải nhận request thôi.

2. Least connections

Với thuật toán này, Nginx sẽ điều hướng request tới server mà đang có phục vụ ít request nhất. Nhờ điều này mà đảm bảo các máy chủ trong hệ thống không bị mất cân bằng, máy chủ nào cũng cũng phục vụ số request như nhau.

Để sử dụng thuật toán này, bạn cấu hình Nginx như sau:

upstream app_servers {
    least_conn;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3002;
}

3. Least time

Thuật toán này cao cấp hơn so với thuật toán least connections một chút. Tức là ngoài việc tính toán đến số lượng request mà máy chủ đó đang phục vụ mà còn tính tới thời gian phản hồi trung bình cho các request trong quá khứ.

Lý do là trong hệ thống nhiều máy chủ, không phải máy chủ nào cũng có cấu hình như nhau. Có máy chủ mạnh, có máy chủ yếu. Nhờ trọng số thời gian phản hồi trung bình mà Nginx đảm bảo “công bằng xã hội”, thằng nào khỏe thì làm nhiều, thằng yếu thì làm ít hơn.

Lưu ý:Thuật toán này là tính năng trả phí và chỉ có trong bản Nginx plus.

Thuật toán cân bằng tải nào tốt nhất?

Câu trả lời chính xác nhất là: “Còn tùy”.

Bởi vì nó phụ thuộc vào quy mô và tính chất lưu lượng truy cập vào ứng dụng của bạn. Ví dụ, đối với trang VNTALKING này, nội dung chủ yếu là các bài viết tĩnh, do vậy thời gian phản hồi cho mỗi lần hiển thị một bài viết khá giống nhau, khách truy cập cũng ít tương tác qua lại với nhau. Do vậy, thuật toán least connection là phù hợp nhất.

Với các ứng dụng cần phải tính toàn nhiều cho mỗi request, ví dụ ứng dụng convert video online chẳng hạn. Mỗi request khác nhau thì thời gian phải hồi khác nhau, tùy vào dung lượng video người dùng yêu cầu… Do vậy, với ứng dụng kiểu này thì thuật toán least time là tối ưu nhất.

Tạm kết

Như vậy, qua bài viết này chúng ta đã tìm hiểu cơ bản về cơ chế và thuật toán của cân bằng tải. Và cách cân bằng tải cho ứng dụng Node.js.

Mình hi vọng các bạn có thể ứng dụng kỹ thuật này giúp cải thiện hiệu suất ứng dụng, nhanh chóng đạt được mục tiêu hàng triệu khách hàng.

🙆 Đọc thêm:

Bài viết được tham khảo từ: freecodecamp, netguru, Ngix document Official

Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trướcReact Native Animation với Animated API và LayoutAnimation
Bài tiếp theo[Video] Tặng khóa học Xây dựng RESTful API bằng Node.js
Sơn Dương
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

1
Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Mới nhất Cũ nhất Nhiều voted nhất
Thông báo
Quang Duy
Guest
Quang Duy

Cám ơn tác giả ạ