Linh tinh
Mục lục

Mô hình 1 thread / 1 request của một web server

Bài viết này giải thích mô hình thread / request của một web server, lấy ví dụ trên Apache Tomcat và Spring framework để giải thích cách một web server xử lý request từ phía client.

Thread per request model

Hiện nay, nhiều web server sử dụng mô hình 1 thread / 1 request để xử lý request từ client.

thread model

Có 2 loại thread với chức năng hoàn toàn khác nhau ở mô hình này:

Có một điểm cần chú ý ở đây là thuật ngữ request sẽ khác với connection, request là các yêu cầu HTTP GET/POST/PUST,... còn connection là TCP connection.

Hình sau mô tả chi tiết hơn các thành phần trong quá trình xử lý một connection và request.

request_model_sequence_diagram

Một số phân tích về ưu nhược điểm

Với việc sử dụng riêng một thread để phát hiện khi nào có request sẵn sàng, server sẽ không bị chiếm dụng tài nguyên cho các long-live connection, tức là client có thể mở một TCP connection và gửi nhiều request HTTP trên đó, worker thread chỉ được sử dụng thực sự khi có request.

Mô hình này còn cho phép debug lỗi dễ dàng ở tầng ứng dụng, mỗi request sẽ được xử lý bởi một worker thread, các thông tin về request context sẽ không bị nhầm lẫn, và có thể xem được đầy đủ stacktrace khi có lỗi xảy ra.

Bên cạnh các ưu điểm, mặt tài nguyên của hệ thống cần được phân tích kĩ lưỡng khi sử dụng model này, nếu ở tầng ứng dụng sử dụng các thao tác blocking như kiểm tra dữ liệu, truy vấn database, gọi các service bên thứ 3, thì worker thread sẽ bị chiếm dụng trên toàn bộ thời gian này, có nghĩa là số request tối đa được xử lý tại một thời điểm sẽ bằng số lượng worker thread tối đa. Tối đa là bao nhiêu???

Về lý thuyết, một chương trình có thể tạo ra số lượng threads không giới hạn, nhưng quản lý threads tiêu tốn tài nguyên và nhiều threads dẫn tới context-switch của CPU cao, nếu không cấu hình giới hạn, chương trình có thể bị OOM, rất rủi ro.

Nếu có cấu hình, con số bao nhiêu là đủ? Tuỳ thuộc vào workload và kết quả benchmark, tất nhiên rồi. Tuy nhiên cần làm rõ thêm hành vi của request một khi không còn worker rảnh rỗi.

Demo

Github link: thread-per-request

Chuơng trình này tạo 1 web server đơn giản Spring và embedded Tomcat:

Hình sau thêm thành phần task queue vào giữa I/O thread và worker thread, khi worker thread pool không còn thread nào rảnh rỗi, những request mới sẽ được thêm vào queue chờ xử lý. Nếu worker thread mất quá nhiều thời gian để xử lý những request, client có thể gặp hiện tượng timeout với các giai đoạn:

request_model_sequence_diagram-with-task-queue

Với worker thread size = 1, api lấy thống kê threads trả về như sau:

thread-stats

Tiếp tục mô phỏng kiểm tra cách hoạt động của mô hình này bằng request cần thời gian xử lý lâu.

Với worker thread size = 1, queue size = 1 thì request thứ 3 sẽ bị reject, request thứ 2 sẽ phải chờ request thứ nhất xử lý xong.

Request thứ nhất cần 60s.

slow-request-result

Request thứ hai được đưa vào queue và chỉ được xử lý sau request thứ nhất.

threads-stats-result

Request thứ ba bị từ chối vì task queue đã đầy.

threads-stats-failed-result

Tổng kết

Bài viết đã trình bày mô hình thread per request của các web server và lấy ví dụ bằng Tomcat + Spring. Việc hiểu rõ mô hình này là một bước quan trọng trong việc hiểu rõ chương trình, là nền tảng cho các công việc benchmark, tối ưu hiệu năng.