D.1 App server và runtime
Theo docs
Mỗi giới hạn dưới đây là một chữ L trong định luật Little (chương 0): số việc được xử lý dở cùng lúc. Biết mặc định thì biết hệ thống sẽ kẹt ở đâu trước.
| Tham số | Mặc định | Giá trị hợp lệ | Làm gì | Khi nào đổi · tác động |
|---|---|---|---|---|
Tomcat maxThreadsrủi ro | 200 | số nguyên | Số thread tối đa xử lý request cùng lúc của connector HTTP. | Theo Little: 200 thread, mỗi request 100 ms thì trần khoảng 2.000 request/giây; request chậm thành 1 giây thì trần tụt còn 200. Tăng thread tốn RAM cho stack của mỗi thread và không giúp gì nếu nút cổ chai là database. |
Tomcat acceptCount | 100 | số nguyên | Số kết nối được xếp hàng chờ khi mọi thread đều bận. | Hàng chờ dài hơn thì ít lỗi kết nối hơn lúc đột biến nhưng độ trễ tăng; quá giới hạn thì kết nối mới bị từ chối. |
HikariCP maximumPoolSizerủi ro | 10 | số nguyên | Số kết nối database tối đa một instance app giữ. | Nhân với số instance app phải nhỏ hơn giới hạn kết nối của database. 20 instance × 10 = 200 kết nối, đã vượt mặc định của PostgreSQL. Pool lớn quá làm database chậm vì tranh chấp, không làm nhanh hơn. |
HikariCP connectionTimeout | 30000 ms | số nguyên, ms | Thời gian tối đa một request chờ lấy kết nối từ pool. | Để dài thì lúc database chậm, thread app bị giữ lâu và chuỗi quá tải lan lên tầng trên. Thường nên ngắn hơn timeout của bên gọi. |
nginx worker_connections | 512 | số nguyên | Số kết nối tối đa mỗi worker process của nginx mở cùng lúc, tính cả kết nối tới upstream. | Khi làm reverse proxy, mỗi request dùng hai kết nối (client và upstream), nên số client thật chỉ khoảng một nửa. Tăng thì phải tăng cả giới hạn file mở của hệ điều hành. |
Node.js server.keepAliveTimeoutrủi ro | 5000 ms | số nguyên, ms | Thời gian server giữ kết nối keep-alive rảnh trước khi đóng. 5000 ms là giá trị ở các bản đã phát hành (docs Node 22); docs nhánh phát triển đã đổi thành 65000 ms. | Nếu ngắn hơn idle timeout của load balancer phía trước, server đóng kết nối trong khi load balancer vẫn gửi request vào đó, gây lỗi 502 lẻ tẻ khó tìm. Nên đặt dài hơn idle timeout của load balancer. |
Node.js server.requestTimeout | 300000 ms | số nguyên, ms; 0 là tắt | Thời gian tối đa để nhận xong toàn bộ request từ client. | Giữ để chống client gửi chậm cố ý chiếm kết nối. |
Go http.Server ReadTimeout, WriteTimeout, IdleTimeoutrủi ro | 0 | time.Duration; 0 là không giới hạn | Giới hạn thời gian đọc request, ghi phản hồi và giữ kết nối rảnh. 0 nghĩa là không giới hạn; riêng IdleTimeout bằng 0 thì dùng giá trị của ReadTimeout. | Mặc định không giới hạn: một client chậm hay treo giữ goroutine và kết nối mãi. Service Go chạy thật nên đặt cả ba. |
D.2 Database và connection pool
Theo docs
| Tham số | Mặc định | Giá trị hợp lệ | Làm gì | Khi nào đổi · tác động |
|---|---|---|---|---|
PostgreSQL max_connectionsrủi ro | 100 | số nguyên; đổi phải restart | Số kết nối tối đa vào server. PostgreSQL tạo một process riêng cho mỗi kết nối. Mặc định có thể thấp hơn 100 nếu lúc initdb kernel không cho phép. | Mỗi kết nối là một process nên tốn RAM và CPU chuyển ngữ cảnh. Cần nhiều kết nối từ nhiều instance app thì đặt pooler như PgBouncer phía trước, đừng tăng max_connections lên hàng nghìn. |
MySQL max_connectionsrủi ro | 151 | số nguyên | Số client kết nối cùng lúc tối đa. | Vượt giới hạn thì client nhận lỗi "Too many connections". MySQL dùng thread cho mỗi kết nối nên rẻ hơn process, nhưng vẫn tốn RAM theo buffer của từng kết nối. |
PgBouncer default_pool_size | 20 | số nguyên | Số kết nối thật tới database cho mỗi cặp user và database. | Đây mới là số kết nối database thật sự thấy; nhiều client dùng chung số kết nối này. Đặt theo Little: tốc độ query nhân thời gian query. |
PgBouncer max_client_conn | 100 | số nguyên | Số kết nối client tối đa PgBouncer nhận. | Có thể đặt lớn hơn nhiều so với max_connections của database, đó là mục đích của pooler. Cần đủ file descriptor trên máy chạy PgBouncer. |
Redis maxclients | 10000 | số nguyên | Số client kết nối cùng lúc tối đa; thấp hơn 10000 nếu giới hạn file descriptor của process nhỏ hơn. | Hay chạm khi mỗi instance app mở pool riêng lớn. Redis xử lý lệnh trên một luồng chính, nên thêm kết nối không làm nó nhanh hơn. |
Nhân số instance với kích thước pool trước khi scale ra. Tăng tầng app từ 5 lên 30 pod, mỗi pod giữ 10 kết nối, là đổi từ 50 lên 300 kết nối vào database. PostgreSQL mặc định nhận 100 kết nối sẽ từ chối phần còn lại, dù CPU của nó vẫn rảnh.
D.3 Hệ điều hành, load balancer, Kubernetes
Theo docs
| Tham số | Mặc định | Giá trị hợp lệ | Làm gì | Khi nào đổi · tác động |
|---|---|---|---|---|
Linux net.core.somaxconn | 4096 | số nguyên | Độ dài tối đa hàng chờ kết nối đã bắt tay xong nhưng app chưa nhận (accept). Trước kernel 5.4 mặc định là 128. | Trên kernel cũ giá trị nhỏ làm rớt kết nối lúc đột biến. Giá trị thật còn bị giới hạn bởi tham số backlog mà app truyền vào listen(). |
AWS ALB idle timeoutrủi ro | 60 giây | giây | Thời gian load balancer giữ kết nối không có dữ liệu trước khi đóng. | Request chạy lâu hơn mức này mà không gửi byte nào sẽ bị cắt. Keep-alive của app phía sau nên dài hơn giá trị này để tránh lỗi 502. |
GCP external Application LB backend service timeoutrủi ro | 30 giây | giây | Thời gian load balancer chờ backend trả lời. Backend serverless (serverless NEG) có giới hạn khác. | Request lâu hơn mức này bị trả lỗi dù backend vẫn đang xử lý; backend làm việc vô ích. Việc dài nên chuyển sang xử lý bất đồng bộ (chương 6). |
Kubernetes probe timeoutSecondsrủi ro | 1 giây | giây | Thời gian chờ một lần kiểm tra liveness hoặc readiness. | Mặc định rất ngắn. Lúc app quá tải, probe trả lời chậm, Kubernetes tưởng app chết và restart, làm phần tải dồn sang các pod còn lại: một vòng sập dây chuyền. |
Kubernetes probe periodSeconds, failureThreshold | 10 giây; 3 lần | giây; số lần | Bao lâu kiểm một lần, và thất bại bao nhiêu lần liên tiếp thì coi là hỏng. | Thời gian phát hiện hỏng xấp xỉ periodSeconds × failureThreshold. Ngắn thì phát hiện nhanh nhưng dễ restart nhầm lúc tải cao. |
Timeout phải xếp bậc từ ngoài vào trong. Timeout của bên gọi nên dài hơn tổng timeout bên trong, và keep-alive của backend nên dài hơn idle timeout của load balancer. Xếp ngược lại thì bên ngoài bỏ cuộc trong khi bên trong vẫn làm, hoặc kết nối bị đóng giữa chừng.