1. Giới thiệu về Nginx
-
Nginx là một máy chủ HTTP cao hiệu suất, máy chủ proxy ngược, máy chủ email và máy chủ proxy ngược cho TCP/UDP.
-
Việc xử lý yêu cầu của Nginx là bất đồng bộ và không chặn, nhờ đó nó có thể duy trì hiệu suất cao và tiêu thụ tài nguyên thấp ngay cả trong môi trường tải cao. Nó thường được sử dụng trong các hệ thống phân cụm để hỗ trợ cân bằng tải.
-
Nginx cũng xử lý các tệp tĩnh rất nhanh, nên có thể được dùng làm máy chủ cho các trang web phía frontend.
2. Lý do sử dụng Nginx
Một hệ thống đơn lẻ chỉ có khả năng xử lý một lượng yêu cầu từ khách hàng nhất định. Khi số lượng yêu cầu vượt quá khả năng xử lý của hệ thống, hiệu năng máy chủ sẽ giảm sút, tốc độ phản hồi chậm lại, ảnh hưởng trực tiếp đến trải nghiệm người dùng. Để cải thiện hiệu suất, chúng ta tạo ra nhiều phiên bản dịch vụ, hình thành một hệ thống phân cụm nhằm đảm bảo tính sẵn sàng cao.
Hệ thống nào phù hợp với việc áp dụng hệ thống phân cụm? Theo quan điểm cá nhân, có hai yếu tố chính: thứ nhất là lượng người dùng lớn, dẫn đến số lượng yêu cầu tăng cao; thứ hai là tần suất yêu cầu dày đặc. Ví dụ như ứng dụng tra cứu mã sức khỏe trong thời gian gần đây, nếu loại bỏ các yếu tố liên quan đến tích hợp với các hệ thống khác, thì 99% công việc của nó là tra cứu dữ liệu, với lượng truy vấn và tải đồng thời rất lớn – đây là trường hợp lý tưởng cho việc triển khai hệ thống phân cụm.
3. Nguyên lý phân luồng truy vấn trong Nginx
1. Thiết kế mô-đun hóa
Thiết kế mô-đun hóa cao là nền tảng kiến trúc của Nginx. Trong Nginx, ngoài một số đoạn mã cốt lõi, mọi thứ đều là mô-đun. Các mô-đun này được phân cấp và phân loại rõ ràng. Theo phân loại chính thức của Nginx, có năm loại mô-đun: mô-đun lõi, mô-đun cấu hình, mô-đun sự kiện, mô-đun HTTP, và mô-đun mail. Trong số này, mô-đun cấu hình và mô-đun lõi có mối liên hệ chặt chẽ với framework của Nginx. Mô-đun sự kiện là nền tảng cho mô-đun HTTP và mô-đun mail. Hai mô-đun còn lại tương đương nhau, đều tập trung vào mức ứng dụng và sử dụng các mô-đun lõi cơ bản.
2. Mô hình đa tiến trình
So với mô hình đa luồng truyền thống của Memcached, Nginx sử dụng mô hình đa tiến trình. Sau khi khởi động, Nginx chạy nền với một tiến trình master và nhiều tiến trình worker. Số lượng tiến trình worker có thể cấu hình, thường dựa trên số lõi CPU của máy chủ. Ví dụ, nếu máy tính có 12 lõi CPU, ta có thể đặt worker_processes là 12. Khi đó, hệ thống sẽ hiển thị 13 tiến trình Nginx đang chạy.
3. Kiến trúc dựa trên sự kiện
Khi xử lý các sự kiện yêu cầu, các consumer sự kiện chỉ được gọi tạm thời bởi tiến trình phân phát sự kiện. Thiết kế này giúp nâng cao hiệu năng mạng và thời gian phản hồi, đảm bảo mỗi yêu cầu được xử lý kịp thời. Tuy nhiên, điều này đòi hỏi các consumer sự kiện không được chặn, vì nếu không, chúng sẽ chiếm dụng tiến trình phân phát sự kiện, khiến các sự kiện khác bị chậm. Tính không chặn của Nginx dựa vào việc tất cả các mô-đun đều đáp ứng yêu cầu này. Nên Nginx hoạt động tốt nhất trên Linux, nơi I/O và xử lý sự kiện mạnh hơn Windows. Số lượng sự kiện mà Nginx có thể xử lý có thể được thiết lập trong file cấu hình, tùy thuộc vào hiệu năng hệ thống và yêu cầu nghiệp vụ.
4. Máy chủ ảo, proxy ngược, cân bằng tải
- Máy chủ ảo dùng để thực hiện proxy ngược cho toàn bộ hệ thống ứng dụng.
- Proxy ngược nghĩa là Nginx thay mặt máy chủ phía sau xử lý yêu cầu, trái ngược với proxy ngược đại diện cho việc xử lý yêu cầu của khách hàng.
- Cân bằng tải phân phối lưu lượng đều đến các máy chủ backend.
4. Triển khai Nginx
Trước tiên, hãy phân tích tình huống thực tế, rồi lựa chọn phương án triển khai phù hợp cho từng trường hợp sử dụng.
1. Thực hành cân bằng tải
-
Tạo một hệ thống ứng dụng, ở đây dùng ví dụ "tra cứu mã sức khỏe" như đã đề cập. Đây chỉ là ví dụ minh họa, không phản ánh chính xác thực tế hệ thống mã sức khỏe, vì tôi không tham gia phát triển hoặc hiểu rõ kiến trúc kỹ thuật thực tế của hệ thống này. Bạn có thể coi đây là bất kỳ hệ thống nào bạn muốn, hoặc bỏ qua phần này nếu thấy không phù hợp.
-
Tải xuống Nginx làm máy chủ. Trong ví dụ này, tôi sử dụng môi trường Windows. Tuy nhiên, việc triển khai trên Linux hay Docker đều được hỗ trợ, nhưng vì môi trường phát triển là Windows, nên việc sử dụng Windows tiện lợi hơn.
-
- Khởi tạo dịch vụ mã sức khỏe
-
- Tải xuống Nginx
- Tạo cụm dịch vụ: tạo 2 phiên bản dịch vụ để mô phỏng cụm, sau đó sử dụng Nginx để phân phối lưu lượng.
-
- Khởi động 2 phiên bản dịch vụ thông qua dòng lệnh, lần lượt gắn với cổng 8081 và 8082. Trong thực tế, số lượng và vị trí triển khai có thể khác biệt.
-
- Cấu hình proxy ngược và cân bằng tải cho Nginx
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
# Máy chủ ảo
server{
listen 8080;
server_name localhost;
# Cấu hình proxy ngược
location / {
proxy_pass http://HealthCode;
}
error_page 500 502 503 504 /50x.html;
}
}
# Cấu hình cân bằng tải
upstream HealthCode{
server localhost:8081;
server localhost:8082;
}
-
- Khởi động Nginx và truy cập máy chủ ảo
2. Phân tích trường hợp sử dụng cân bằng tải
Trong ví dụ trên, chúng ta đã sử dụng Nginx để thực hiện cân bằng tải. Nhưng cần hiểu cách Nginx chuyển hướng yêu cầu đến các instance dịch vụ khác nhau. Dưới đây là các thuật toán cân bằng tải phổ biến trong Nginx và cách cấu hình tương ứng.
1. Thuật toán vòng tròn (round-robin)
Giả sử các yêu cầu được xử lý tuần tự theo thứ tự 1, 2, 3, 4... Đây là thuật toán mặc định của mô-đun upstream, thích hợp khi các máy chủ có khả năng xử lý tương đương. Nếu một instance bị lỗi, nó sẽ tự động bị loại khỏi danh sách. Có thể thêm trọng số để điều chỉnh tỷ lệ phân phối, trọng số tỷ lệ thuận với tỷ lệ truy cập. Ví dụ: 20% và 80%.
# Cấu hình cân bằng tải
upstream HealthCode{
server localhost:8081;
server localhost:8082;
#server localhost:8081 weight=2;
#server localhost:8082 weight=8;
}
2. Thuật toán kết nối tối thiểu (least_conn)
Khi có nhiều yêu cầu đến Nginx, nó sẽ chuyển tiếp đến các instance 8081 và 8082. Nếu instance 8082 xử lý chậm, sẽ gây tích tụ yêu cầu. Trong trường hợp này, nên chuyển yêu cầu đến máy chủ ít kết nối hơn để đạt hiệu quả cân bằng tải tốt hơn. Sử dụng least_conn.
# Cấu hình cân bằng tải
upstream HealthCode{
# Sử dụng thuật toán kết nối tối thiểu
least_conn;
server localhost:8081;
server localhost:8082;
}
3. Thuật toán hash nhất quán (ip_hash)
Do lượng truy vấn quá lớn, để nâng cao khả năng phục hồi, chúng ta thêm bộ nhớ đệm, ví dụ như trả về dữ liệu từ cache trong vòng 3 phút. Khi khách hàng gửi yêu cầu, Nginx chuyển tiếp đến các instance. Với thuật toán vòng tròn hoặc kết nối tối thiểu, việc truy cập cache sẽ bị giảm hiệu suất do mất trạng thái cache. Yêu cầu sẽ được chuyển đến instance không có cache, dẫn đến truy vấn cơ sở dữ liệu và làm giảm hiệu năng.
Trong trường hợp này, nên sử dụng ip_hash để liên kết địa chỉ IP của khách hàng với một instance cụ thể.
# Cấu hình cân bằng tải
upstream HealthCode{
# Sử dụng thuật toán hash IP
ip_hash;
server localhost:8081;
server localhost:8082;
}
4. Chiến lược phục hồi
Cơ chế thử lại
Khi Nginx nhận yêu cầu từ khách hàng và chuyển tiếp đến instance 8081, nếu instance đó gặp sự cố, yêu cầu sẽ thất bại. Để đảm bảo thành công, sử dụng cơ chế thử lại trong Nginx:
# Cấu hình cân bằng tải động
upstream HealthCode{
ip_hash;
# Đặt tối đa 2 lần thử lại, thời gian chờ 10 giây
server localhost:8081 max_fails=2 fail_timeout=10s;
server localhost:8082 max_fails=2 fail_timeout=10s;
}
Sao lưu máy chủ (backup)
Trong trường hợp cả hai instance đều gặp sự cố, hệ thống sẽ không hoạt động. Trong tình huống này, có thể sử dụng backup để giải quyết. Lưu ý rằng, máy chủ sao lưu chỉ hoạt động khi các máy chủ chính không hoạt động. Nếu dùng ip_hash, chức năng này sẽ không hoạt động do IP đã được liên kết với máy chủ cụ thể.
# Cấu hình cân bằng tải động
upstream HealthCode{
ip_hash;
server localhost:8081 max_fails=2 fail_timeout=10s;
server localhost:8082 max_fails=2 fail_timeout=10s;
# Máy chủ sao lưu
server localhost:8083 backup;
}
5. Cấu hình HTTPS
Để đảm bảo an toàn cho yêu cầu, cần sử dụng giao thức HTTPS. Cần cấu hình máy chủ ảo HTTPS, điều này yêu cầu chứng chỉ gồm khóa riêng (server-key.pem) và chứng chỉ (server-cert.pem).
1. Cài đặt cơ bản
- Tải xuống OpenSSL, sử dụng công cụ để tạo chứng chỉ.
- Tìm đường dẫn tạo chứng chỉ.
- Thêm cấu hình máy chủ ảo vào file cấu hình Nginx và cấu hình HTTPS.
# Máy chủ ảo HTTPS
server {
listen 4435 ssl;
server_name localhost;
ssl_certificate D:/cert/server-cert.pem;
ssl_certificate_key D:/cert/server-key.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
location / {
proxy_pass http://HealthCode;
}
}
2. Chuyển đổi HTTP sang HTTPS
Trong hệ thống luôn có các yêu cầu HTTP mặc định. Sử dụng module ngx_http_rewrite_module để chuyển đổi yêu cầu HTTP sang HTTPS.
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
# Máy chủ ảo
server{
listen 8080;
server_name localhost;
# Mặc định chuyển hướng sang HTTPS
if($scheme = http)
{
return 301 https://$host:4435$request_url
}
# Cấu hình proxy ngược
location / {
proxy_pass http://HealthCode;
}
error_page 500 502 503 504 /50x.html;
}
}
# Cấu hình cân bằng tải
upstream HealthCode{
server localhost:8081;
server localhost:8082;
}