Bẵng một thời gian, chúng ta lại quay lại với loạt bài Lập trình web với Python. Trong kỳ này, chúng ta sẽ bàn đến mô hình FastCGI (FCGI).

Cái tên FCGI khiến người ta liên tưởng đến mô hình CGI như đã trình bày trong kỳ trước. Nhưng, thật tế là FCGI là một mô hình rất khác so với CGI.

Các ứng dụng FCGI là các ứng dụng bền, tức là một tiến trình FCGI có thể phục vụ nhiều yêu cầu chứ không phải chỉ một yêu cầu như ứng dụng CGI. Do đó, các ứng dụng FCGI có thể sử dụng các biến có phạm vi ứng dụng (application-scoped variable, tương đương với các biến toàn cục, global variable), tồn tại qua nhiều yêu cầu.

Xét ví dụ một chương trình web hiển thị số lần truy cập vào trang.

  • Với mô hình CGI, khi có yêu cầu gửi đến, máy chủ web (web server, httpd) sẽ tạo một tiến trình khác, chạy chương trình CGI, chương trình này khởi tạo biến đếm với giá trị 0, tăng nó lên 1, và hiển thị số 1 rồi tiến trình kết thúc. Khi có yêu cầu thứ hai đến, máy chủ lại tạo một tiến trình khác, chạy chương trình CGI, chương trình này khởi tạo biến đếm với giá trị 0, tăng nó lên 1, và hiển thị số 1, và kết thúc tiến trình. Như vậy, vì mỗi yêu cầu được một tiến trình riêng phục vụ nên chúng ta sẽ cần phải lưu biến đếm vào một tài nguyên ngoài (ví dụ như tập tin, hoặc cơ sở dữ liệu), và các tiến trình truy cập vào cùng tài nguyên này.
  • Với mô hình FCGI, một tiến trình FCGI sẽ được chạy trước. Chương trình này được gọi là một FCGI server. Chương trình này khởi tạo biến đếm với giá trị 0 và bước vào một vòng lập vô tận để nhận yêu cầu. Trình duyệt gửi yêu cầu đến máy chủ web. Khi này máy chủ web sẽ chuyển yêu cầu này đến ứng dụng FCGI đã được chạy trước đó. Như vậy, máy chủ web đóng vai trò là một FCGI client. Ở phía FCGI server, yêu cầu được chấp nhận (accept), biến đếm được tăng lên thành 1, kết quả trả về lại cho httpd, và FCGI server quay lại vòng lập nhận yêu cầu. Máy chủ web trả lại kết quả cho trình duyệt. Khi có yêu cầu kế tiếp, máy chủ httpd lại chuyển yêu cầu đó cho cùng FCGI server. Yêu cầu này được chấp nhận, biến đếm được tăng thành 2 vì đang trong cùng một tiến trình, kết quả trả về lại cho httpd và tiếp tục như đã bàn.

Vì mô hình hoạt động của FCGI khác với CGI nên việc trao đổi giữa FCGI client và FCGI server (giữa httpd và ứng dụng FCGI) không thể sử dụng cùng một giao thức đơn giản như ứng dụng CGI nữa. Việc này đòi hỏi một giao thức riêng cho FCGI.

Mô hình hoạt động đơn giản của FCGI được miêu tả trong hình dưới.

Ngoài lợi ích về tốc độ (do không phải tạo tiến trình mới để phục vụ mỗi yêu cầu, và khả năng chia sẻ tài nguyên thông qua biến có phạm vi ứng dụng), các ứng dụng FCGI còn có thể được triển khai trên hệ thống khác với hệ thống máy chủ web, giúp cho việc phân bổ tài nguyên giữa httpd và FCGI server được hiệu quả hơn. FCGI hỗ trợ hai cách giao tiếp là thông qua stream pipe, và thông qua socket. Cách giao tiếp stream pipe tương tự như việc gửi và nhận dữ liệu qua stdin, và stdout. Cách giao tiếp này được sử dụng cho các ứng dụng FCGI triển khai trên cùng hệ thống với httpd. Cách giao tiếp socket sử dụng một kết nối TCP để đóng gói và gửi dữ liệu của env-vars, stdin, stdout, và stderr tới ứng dụng FCGI. Điều này cho phép ứng dụng FCGI được triển khai trên một hệ thống khác, và httpd sẽ liên lạc với ứng dụng này dựa vào kết nối mạng TCP. Do đó, việc tối ưu tài nguyên hệ thống có thể được thực hiện dễ dàng hơn so với ứng dụng CGI. Đối với ứng dụng FCGI, FCGI server có thể được chạy trong chế độ đa tiến trình, hoặc đa tiểu trình đều được. FCGI server có thể được chạy trên một máy 8 lõi, trong khi httpd chạy trên một máy khác đơn nhân. FCGI server phục vụ các dữ liệu động, trong khi httpd phục vụ các dữ liệu tĩnh như hình ảnh, trang web tĩnh.

Chúng ta vừa xem qua mô hình hoạt động của FCGI. Đi sâu hơn vào cấu trúc từng ứng dụng FCGI thì dúng ta có ba phần chính sau:

  • Mã khởi tạo
  • Vòng lặp nhận yêu cầu
  • Xử lý yêu cầu và trả kết quả

Trong ví dụ về ứng dụng hiển thị số lượt truy cập, mã khởi tạo là việc khởi tạo biến đếm toàn cụ với giá trị 0.

Vòng lặp nhận yêu cầu thường là một vòng lặp vô hạn, tương tự như:

while True:
  fcgi_accept()
  # xử lý yêu cầu như CGI

Trong khi yêu cầu được chấp nhận, thì dữ liệu nhập cũng được tách ra thành env-vars, stdin, stdout, và stderr. Điều này khiến cho việc xử lý yêu cầu của một ứng dụng FCGI không khác bao nhiêu so với việc xử lý của một ứng dụng CGI. Tất cả đều được thực hiện trên các biến môi trường, và các luồng chuẩn. Điều khác biệt đó là sau khi xử lý xong yêu cầu thì ứng dụng FCGI lại quay lại vòng lặp nhận yêu cầu mà không chấm dứt tiến trình như một ứng dụng CGI.

Vì không phải chấm dứt tiến trình sau mỗi yêu cầu nên các tiến trình FCGI có thể được "sử dụng lại". Một ứng dụng FCGI có thể được khởi tạo trước, với 10 tiến trình chẳng hạn, và đặt trong một bể (pool) tiến trình sẵn sàng. Khi có yêu cầu đến, thì máy chủ web có thể ngay lập tức lấy ra một tiến trình từ bể này, và gửi thông tin đến nó. Khi kết thúc xử lý yêu cầu, máy chủ web lại đưa tiến trình này vào bể. Việc khởi tạo trước tiết kiệm được thời gian khởi tạo ban đầu, nhưng sẽ chiếm bộ nhớ, ngay cả khi chưa có yêu cầu nào được nhận.

Sau đây là mô hình chi tiết của một ứng dụng FCGI.

Hẹn gặp các bạn vào bài tới. Chúng ta sẽ viết thử một ứng dụng FCGI đơn giản với mô-đun fcgi của Allan Saddi.