Tối Ưu Hóa Hiệu Năng Apache Tomcat và Chiến Lược Phân Tách Truy Cập Tĩnh/Động Bằng Nginx

Kiến trúc xử lý yêu cầu web hiện đại đòi hỏi sự phối hợp chặt chẽ giữa máy chủ ứng dụng và reverse proxy. Apache Tomcat tuy linh hoạt nhưng khả năng chịu tải cao thường cần được bổ trợ bởi Nginx để phân tách hiệu quả nội dung tĩnh và động.

1. Các Phương Pháp Cải Thiện Hiệu Năng Tomcat

Toàn bộ quy trình tinh chỉnh Tomcat bao gồm ba lớp chính: cấu hình ứng dụng, tham số nhân hệ thống (kernel) và thiết lập máy ảo Java (JVM). Trong môi trường sản xuất lớn, Tomcat thường không đóng vai trò là gateway trực tiếp mà chỉ xử lý nghiệp vụ logic phía sau lớp cân bằng tải như Nginx hoặc HAProxy.

1.1 Điều Chỉnh Tốc Độ Khởi Động Và Bảo Mật Cơ Bản

Một trong những nguyên nhân gây chậm khởi động là việc sinh số ngẫu nhiên từ nguồn entropy của hệ thống. Để khắc phục tình trạng này, ta cần thay đổi nguồn dữ liệu ngẫu nhiên trong file cấu hình an toàn của Java.

# Sửa đổi file cấu hình bảo mật
cd $JAVA_HOME/jre/lib/security/
# Sao lưu trước khi chỉnh sửa
cp java.security java.security.bak

# Thay đổi dòng chứa random thành urandom để tránh chặn I/O
sed -i 's|file:/dev/urandom|file:/dev/urandom|g' java.security
# Lưu ý: Sử dụng urandom giúp tăng tốc độ nhưng đánh đổi một phần tính ngẫu nhiên mật mã học

1.2 Tối Ưu Bộ Nhúng Thread Và Connector

Cấu hình mặc định trong server.xml thường không phù hợp cho lưu lượng truy cập lớn. Cần điều chỉnh các thuộc tính kết nối HTTP/AJP dưới đây:

  • maxThreads: Giới hạn số lượng luồng xử lý đồng thời (khuyến nghị 400-1000 tùy tài nguyên).
  • minSpareThreads: Số luồng sẵn sàng chờ lệnh ngay khi server khởi động (đề xuất 25-50).
  • acceptCount: Hàng đợi yêu cầu khi tất cả thread đang bận (mặc định 100, có thể tăng lên 500 để tránh drop request tạm thời).
  • connectionTimeout: Thời gian chờ ngắt kết nối (đơn vị ms, đặt 20000 tương đương 20s).
  • enableLookups: Tắt tính năng này (false) để tránh độ trễ do DNS reverse lookup.
  • compression: Kích hoạt nén dữ liệu trả về (on). Thiết lập compressableMimeType bao gồm các định dạng văn bản phổ biến như text/html, application/json, text/css.

Cấu hình ví dụ cho Connector cổng 8080:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" 
           maxThreads="500" minSpareThreads="50"
           enableLookups="false" acceptCount="500"
           compression="on"
           compressableMimeType="text/html,text/plain,application/javascript"/>

1.3 Tối Ưu Hệ Thống (OS Kernel Tuning)

Trước khi nâng cấp ứng dụng, cần đảm bảo nền tảng Linux đủ sức gánh tải. Hai file quan trọng cần chỉnh sửa là giới hạn tài nguyên user và tham số mạng TCP/IP.

Điều chỉnh giới hạn file mở và tiến trình:

# /etc/security/limits.conf
* soft nproc 65535
* hard nproc 65535
* soft nofile 65535
* hard nofile 65535

Thay đổi này cho phép mỗi người dùng mở tối đa 65535 tập tin và chạy số lượng tiến trình lớn, tránh lỗi "too many open files".

Tham số mạng TCP quan trọng trong sysctl.conf:

net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_max_tw_buckets = 2000000
net.core.somaxconn = 2048
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 1000
vm.swappiness = 1

Các tham số trên giúp mở rộng dải cổng đầu ra, quản lý hàng đợi socket tốt hơn và giảm thiểu việc chuyển trang nhớ xuống disk (swap).

1.4 Cấu Hình Quản Lý Bộ Nhớ JVM

JVM cần được cấp phát bộ nhớ cố định để tránh dao động hiệu suất khi co giãn heap tự động. File catalina.sh cần thêm các cờ khởi tạo Java:

JAVA_OPTS="$JAVA_OPTS -server \
-Djava.awt.headless=true \
-Xms2g -Xmx2g \
-Xmn1g \
-XX:+UseConcMarkSweepGC \
-XX:MaxPermSize=512m"

Giải thích các tham số bộ nhớ:

  • -Xms2g / -Xmx2g: Đặt kích thước khởi tạo và tối đa của Heap Space bằng nhau để ngăn JVM tái cấp phát bộ nhớ liên tục.
  • -Xmn1g: Cấp phát khoảng 1/2 đến 1/3 cho vùng Young Generation nơi các đối tượng mới được tạo ra.
  • -XX:+UseConcMarkSweepGC: Chọn thuật toán thu gom rác CMS phù hợp cho ứng dụng low-latency.
  • -Djava.awt.headless=true: Tránh lỗi hiển thị đồ họa khi chạy trên giao diện lệnh Linux.

Bộ nhớ Heap được chia làm Young (Eden/Survivor) và Old Generation. Đối tượng tồn tại lâu sẽ di chuyển sang Old Gen để giảm áp lực garbage collection ở Eden.

2. Tích Hợp Nginx Và Tomcat Để Phân Tách Nội Dung

Kiến trúc chuẩn trong môi trường doanh nghiệp là Nginx nhận mọi request, xử lý file tĩnh (html, css, js, ảnh) và forward các yêu cầu động (.jsp, servlet API) về cụm Tomcat phía sau.

2.1 Mô Hình Một Cổng Nginx Cân Bằng Tải Hai Tomcat

Sử dụng module upstream trong Nginx để định nghĩa nhóm backend servers.

# Cấu hình Nginx (nginx.conf)
worker_processes auto;
events { worker_connections 1024; }

http {
    # Nhóm server backend
    upstream app_backend {
        server 10.10.10.15:8080 weight=1;
        server 10.10.10.16:8080 weight=1 backup;
    }

    server {
        listen 80;
        server_name example.com;

        # Xử lý nội dung tĩnh tại thư mục local
        location /static/ {
            root /var/www/html;
        }

        # Chuyển hướng yêu cầu động tới cluster Tomcat
        location ~ \.jsp$ {
            proxy_pass http://app_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

Các lưu ý khi cấu hình proxy:

  • weight=1: Tỷ lệ phân bổ tải bằng nhau.
  • X-Forwarded-For: Quan trọng để giữ lại địa chỉ IP thực của client cho Tomcat ghi log, tránh việc log chỉ hiện IP của Nginx.
  • Extension ~ \.jsp$: Regex khớp các tệp động, các tệp khác sẽ được Nginx tự phục vụ nếu nằm cùng root.

2.2 Mở Rộng Với Nhiều Tầng Nginx (Layer 4 Forwarding)

Trong các hệ thống quy mô lớn, có thể kết hợp nhiều layer Nginx. Tầng ngoài dùng Stream module để cân bằng tải theo Layer 4 (TCP), tầng sau xử lý Layer 7 (HTTP).

# Cấu hình tầng Nginx Gateway (L4)
stream {
    upstream tomcat_stream {
        server 10.10.10.20:8082; # Nginx internal node 1
        server 10.10.10.21:8083; # Nginx internal node 2
    }
    server {
        listen 80;
        proxy_pass tomcat_stream;
    }
}

Cơ chế này giúp phân tán traffic ngay ở mức truyền tải, giảm tải cho tầng xử lý HTTP phía sau. Việc cấu hình firewall cần mở cổng tương ứng (ví dụ 8082, 8083) giữa các máy chủ trung gian và cụm backend.

Nếu sử dụng JSP, hãy đảm bảo Tomcat đã được cấu hình đúng Context Path và quyền truy cập thư mục làm việc (webapps). Kiểm tra kết nối bằng lệnh wget hoặc curl trực tiếp vào từng node để đảm bảo tính ổn định trước khi đưa vào sản phẩm chung.

Thẻ: Tomcat nginx jvm-tuning linux-kernel load-balancing

Đăng vào ngày 3 tháng 6 lúc 20:48