2 điểm bởi GN⁺ 2025-07-07 | 1 bình luận | Chia sẻ qua WhatsApp
  • Ngay cả với lập trình CGI, vẫn có thể xử lý hơn 200 triệu yêu cầu web mỗi ngày
  • Nhờ hiệu năng phần cứng được cải thiện gần đây, các nhược điểm của mô hình CGI đã giảm đi đáng kể
  • Chương trình CGI sử dụng Go và SQLite cho thấy hiệu năng vượt trội trên CPU 16 luồng
  • CGI cung cấp cấu trúc đặc biệt phù hợp để tận dụng nhiều lõi CPU
  • Nhờ công nghệ hiện đại, cách phát triển ứng dụng web trong quá khứ cũng cho thấy hoàn toàn có tính thực tiễn

Quá khứ và hiện tại của CGI

  • Vào cuối thập niên 1990, tác giả bắt đầu phát triển web bằng CGI và khi đó đã dùng các hệ thống như NewsPro
  • CGI tạo ra overhead lớn vì với mỗi yêu cầu web lại phải lặp lại việc khởi chạy và kết thúc tiến trình mới
  • Vì lý do đó, các công nghệ thay thế hiệu quả hơn như PHP, FastCGI đã được phát triển

Sự phát triển của hiệu năng phần cứng

  • Trong hơn 20 năm qua, tốc độ và hiệu năng máy tính đã tăng mạnh
  • Năm 2020, tác giả sử dụng các công cụ được viết bằng Go và Rust (như ripgrep) và khám phá lại tính thực tiễn của cách tiếp cận dựa trên việc khởi chạy tiến trình

Ưu điểm của cách làm CGI hiện đại

  • Nếu triển khai CGI bằng các ngôn ngữ có tốc độ thực thi nhanh như Go và Rust, phần lớn nhược điểm của CGI kiểu cũ sẽ được giải quyết
  • Chương trình CGI hoạt động như một tiến trình riêng cho mỗi yêu cầu, nên rất tối ưu để tận dụng CPU đa lõi
    • Ví dụ, trong môi trường 16 luồng có thể đạt hơn 2400 yêu cầu/giây = khả năng xử lý hơn 200 triệu yêu cầu/ngày
    • Máy chủ cỡ lớn có thể cung cấp hơn 384 luồng CPU

Góc nhìn về văn hóa phát triển

  • Hiện nay, với sự xuất hiện của các ngôn ngữ như Go và Rust, mô hình CGI của thập niên 1990 có thể lại mang ý nghĩa mới
  • Tuy vậy, đây vẫn không phải là cách phù hợp cho mọi môi trường và cũng không được khuyến nghị như phương pháp chủ đạo
  • Điểm quan trọng là ở thời điểm hiện tại, bằng thực nghiệm có thể chứng minh CGI không còn là một giải pháp kém hiệu quả như trước nữa

Kết luận

  • Nhờ phần cứng hiện đại và sự hỗ trợ của các ngôn ngữ nhanh, lập trình CGI cho thấy hiệu năng không thể so sánh với quá khứ
  • Đây là một ví dụ có thể tận dụng tối đa ưu điểm của mô hình đa tiến trình, mang lại nhiều gợi mở thú vị cho các nhà phát triển web

1 bình luận

 
GN⁺ 2025-07-07
Ý kiến trên Hacker News
  • Dạo này ngay cả với Python, CGI cũng cho cảm giác hiệu năng khá nhanh
    Ngay cả khi script CGI dùng 400 mili giây CPU lúc khởi động, nếu máy chủ có 64 lõi thì vẫn xử lý được 160 yêu cầu mỗi giây, tương đương 14 triệu lượt truy cập mỗi ngày trên mỗi máy chủ
    Nghĩa là với lưu lượng hàng trăm triệu yêu cầu mỗi ngày (không tính tài nguyên tĩnh), việc khởi động tiến trình CGI không hẳn là nút thắt cổ chai
    Trước đây tôi nghĩ đây là kiểu công nghệ "ổn định đến mức nhàm chán" nên lúc nào cũng có trong thư viện chuẩn Python, nhưng những người bảo trì Python hiện nay lại có xu hướng không mấy tích cực với tính ổn định và tương thích ngược
    Vì vậy những module quá "nhàm chán và ổn định" đang bị loại khỏi thư viện chuẩn, và thực tế module cgi đã bị xóa ở phiên bản 3.13
    Có lẽ do thói quen dùng Python để tạo prototype suốt gần 25 năm, nhưng giờ tôi bắt đầu thấy hối tiếc
    Tâm trạng hiện tại là đang phân vân giữa JS và Lua

    • Liên kết giải thích chính thức về việc loại bỏ cgiPEP 594 cgi
      Từ đó dẫn tới PEP 206 được viết từ năm 2000 (25 năm trước), nơi đã mô tả rằng "gói cgi có thiết kế không tốt và cũng khó đụng vào"
      Có thể xem kho jackrosenthal/legacy-cgi để biết về một drop-in replacement thay thế nguyên xi module trong thư viện chuẩn

    • Các nhà phát triển Python chỉ loại bỏ module mang tên cgi
      Việc triển khai script CGI vẫn được hỗ trợ trong CGIHTTPRequestHandler của module http.server
      Cũng cần nhắc rằng module cgi vốn chỉ có vài hàm để phân tích dữ liệu form HTML

    • Tôi hiểu việc chỉ trích Python vì loại module cgi khỏi thư viện chuẩn, nhưng JS thường được đem ra làm lựa chọn thay thế thì ngay từ đầu lại còn chẳng có thư viện chuẩn
      Lua cũng không có module CGI trong stdlib

    • Cá nhân tôi thích PHP hoặc JS hơn
      Trong các trường hợp như thế này, việc JIT có sẵn ngay trong hộp là một điểm tiện lợi
      Tôi dùng Python từ 1.6 tới nay nhưng chủ yếu chỉ cho scripting ở mức hệ điều hành
      Ngày xưa tôi từng tích hợp Tcl với module Apache hoặc IIS, rồi cứ lặp đi lặp lại chuyện phải viết lại module bằng C (1999~2003)

    • Nếu một script CGI dùng 400 mili giây CPU thì thời gian phản hồi của endpoint đó cũng sẽ tối thiểu chừng ấy, nên trải nghiệm sử dụng sẽ bị ảnh hưởng

  • Gần đây tôi chạy golang binary, rabbitmq, redis và MySQL trên một mini server giá 350 USD, và duy trì được 5.000 req/s ngay trên cùng một máy
    Tức là trong 24 giờ có thể xử lý 400 triệu yêu cầu
    Điều đó khiến tôi cảm nhận rõ là các công cụ miễn phí ngày nay thật sự rất tuyệt
    Dù vậy tôi vẫn thấy chi phí cloud quá cao
    Tất nhiên khó so sánh 1:1, nhưng cảm giác được tự tay phát triển và tuning trên một máy chủ dưới tầng hầm nhà mình vẫn rất thỏa mãn

    • Có những trường hợp dùng cả một khối microservice dựa trên Kubernetes làm tốc độ phát triển chậm đi 10 lần
      Nhiều người dường như không nhận ra rằng máy chủ không phải là cỗ máy chỉ xử lý được 1 yêu cầu mỗi giây
      Thực tế là họ đang trả một mức overhead quá mức chỉ vì Google làm như vậy nên cũng bắt chước theo
      Tôi cũng nghĩ mình nên viết một bài về kiến trúc 'modular monolith' vốn đang rất hợp với đội của chúng tôi

    • Tôi từng định tự host side project ở nhà, nhưng rủi ro khá lớn như mất điện, ISP downtime, không truy cập từ xa được, ổ cứng hỏng...
      Cuối cùng nếu tính cả thời gian của bản thân thì lợi ích kinh tế cũng không rõ ràng
      Dịch vụ cloud tận dụng được lợi thế kinh tế theo quy mô nên thực ra vẫn là một lựa chọn hợp lý

    • Không nhất thiết phải dùng cloud, cũng có thể thuê dedicated server từ nhà cung cấp hosting
      Dĩ nhiên vẫn có giới hạn về băng thông/lưu lượng
      Cloud trở thành xu hướng chủ đạo là vì VC và nhà đầu tư có cổ phần trong các công ty đó, hoặc vì nỗi lo rằng "biết đâu lưu lượng sẽ bùng nổ vô hạn"
      Những người bán hàng cloud rất khéo khai thác nỗi bất an của nhà đầu tư

    • Không phải ai cũng chỉ dùng cloud

    • Trong dịch vụ thực tế, lý do chi phí VM cao không phải vì cần compute mạnh mà vì cần dung lượng đĩa cục bộ rất lớn
      Không cần năng lực tính toán cao, chỉ cần 4 ổ cứng 20TB và một CPU vừa phải là đã có thể hình dung ra những dịch vụ rất đáng kể
      Trên cloud gần như không thể tìm được kiểu cấu hình như vậy

  • Nếu trong cgi-bin cần truy cập DB, thì sự bất tiện là mỗi lần đều phải tạo mới DB connection cho tiến trình
    Nếu code chạy trong bộ nhớ như fastcgi thì không chỉ giảm thời gian khởi động, mà còn có thể dùng connection pool tới DB hoặc duy trì connection lâu dài cho từng thread

    • Khi chạy ở quy mô lớn, số lượng DB connection tăng quá nhiều sẽ khiến DB quá tải
      Người ta vận hành nhiều tiến trình vì lý do kiểu như "Python là single-thread, Python chậm nên cần thêm nhiều tiến trình"
      Cuối cùng lại phải tách shared connection pool ra ngoài tiến trình Python bằng thứ như pg bouncer và cần đủ kiểu tuning
      Sau rốt tôi đã phải viết lại bằng một ngôn ngữ dễ kiểm soát hơn, hỗ trợ multithread và hiệu năng tốt hơn, và mọi thứ trở nên đơn giản hơn hẳn

    • Vì vậy rốt cuộc CGI đã tiến hóa thành mô hình giữ lại thông tin giữa các yêu cầu như fastcgi

    • Theo cách truyền thống, người ta cũng từng chạy một daemon độc lập để làm nhiệm vụ proxy, và nếu dùng Unix socket thì kết nối cũng hiệu quả hơn TCP/IP rất nhiều

    • Có ý kiến bảo nên dùng UDP

  • Với tôi, inetd chính là CGI đúng nghĩa
    Nhờ nó mà Internet trở nên thú vị hơn rất nhiều
    Đã từng có thời mà tôi trực tiếp dùng inetd để chạy đủ loại shell script, thậm chí cả HTTP viết hoàn toàn bằng Bash
    Những VPS cũ, laptop không sao lưu hay không dùng version control giờ đều biến mất, nhưng đó là những ký ức rất vui
    Deploy cũng đơn giản với Makefile + scp, còn test thì có thể viết bằng script Bash dùng netcatgrep
    Đúng là thời buổi đáng sống

  • Việc một ứng dụng hello world đạt 2400 rps theo tiêu chuẩn phần cứng hiện nay không tạo cảm giác ấn tượng lắm
    Mà code cũng đâu có đơn giản hơn, nên tôi tự hỏi rốt cuộc đang hy sinh hiệu năng để đổi lấy điều gì

    • Nếu không cần xử lý quá 2000 rps thì cũng chẳng có vấn đề gì
      Lập luận là số website cần mức lưu lượng như vậy thực ra rất ít

    • Xét về con số thì không cao, nhưng trong thực tế vẫn là quá đủ cho nhiều môi trường
      Thậm chí còn có thể chịu được kiểu 'hug of death' mà các lập trình viên HN hay nhắc tới, tức là lưu lượng tăng đột biến trong một thời điểm

  • Ở công ty tôi đến giờ vẫn dùng cách dựng nhanh các webapp nội bộ đơn giản trong thư mục cgi-bin
    Nếu dùng đơn giản thì hiệu quả phát triển rất cao
    Ngay cả với CGI cũng không cần phải tự print trực tiếp http/1.0, mà có thể dùng Python wsgiref.handlers.CGIHandler để chạy bất kỳ ứng dụng wsgi nào dưới dạng script CGI
    Ví dụ với Flask cũng đơn giản như dưới đây

     import wsgiref.handlers, flask
     app = flask.Flask(__name__)
     wsgiref.handlers.CGIHandler().run(app)
    

    Trong công việc thực tế, chúng tôi chạy script bằng plugin cgi của uwsgi
    Cảm giác là đơn giản và linh hoạt hơn nhiều so với chạy mod_cgi trong Apache hay lighttpd
    Vì uwsgi chạy ở cấp hệ thống nên còn có thể tận dụng đầy đủ hardening và sandboxing của systemd
    Ngoài ra, trong phần xử lý cgi của uwsgi còn có thể chỉ định interpreter cho từng loại file

     cgi = /cgi-bin=/webapps/cgi-bin/src
     cgi-allowed-ext = .py
     cgi-helper = .py=/webapps/cgi-bin/venv/bin/python3 # all dependencies go here
    

    Thời gian tới byte đầu tiên là 250~350ms, và với nhu cầu của chúng tôi thì hoàn toàn chấp nhận được
    Tài liệu uwsgi về cgi

    • Mẹo hay đấy
      Việc wsgiref.handlers.CGIHandler vẫn chưa bị deprecated là thông tin rất hữu ích
  • Thread liên quan được thảo luận hôm qua: liên kết

  • Gần đây khi dùng Apache cho side project, tôi thấy tính năng .htaccess rất hữu ích nên đã dùng
    Chỉ cần đặt file .htaccess trong bất kỳ thư mục nào là mỗi request sẽ nạp thêm cấu hình máy chủ tương ứng
    Tài liệu chính thức về htaccess
    Trước đây người ta khuyên nên tránh .htaccess vì overhead hiệu năng do phải truy cập đĩa ở mỗi request, và tốt nhất là gom vào cấu hình chính
    Nhưng bây giờ SSD và RAM đều đã dư dả, nên dẫu hiệu năng có thiệt đi một chút thì CPU cũng đủ mạnh để trong đa số trường hợp có thể bỏ qua
    [Dự án StaticPatch của tôi][https://github.com/StaticPatch/StaticPatch/tree/main] cũng đã áp dụng như vậy

    • Câu nói nổi tiếng của người tạo ra PHP, Rasmus Lerdorf
      "Tôi không phải lập trình viên thực thụ, tôi chỉ làm cho nó chạy rồi đi tiếp. Lập trình viên thực thụ sẽ nói 'bị rò rỉ bộ nhớ nghiêm trọng thế này thì phải sửa'. Còn tôi thì chỉ restart apache mỗi 10 request thôi"
      Sau đó PHP đã trải qua một hành trình dài, vượt qua những sai lầm ban đầu và phát triển rất mạnh
      Cũng có giai thoại rằng ông từng nói: "PHP 8 càng ít code của tôi thì càng tốt"

    • Lẽ ra Apache nên theo dõi file system và chỉ đọc khi có thay đổi, tôi không hiểu vì sao lại khiến nó phải thực hiện truy cập đĩa không cần thiết ở mỗi request
      Kết quả là 99,99% request HTTP đều bị chậm đi

  • Gần đây trong workflow của tôi, tôi cũng cân nhắc kiểu cấu trúc này cho việc tạo prototype nhanh
    Với các ngôn ngữ JIT, nếu không theo kiểu fastcgi thì khâu import sẽ trở thành nút thắt
    Web server h2o mà tôi từng dùng có file cấu hình handler cho mruby và fast-cgi rất đơn giản, nên đặc biệt phù hợp cho công việc script cục bộ
    Tài liệu fastcgi của h2o
    Một ưu điểm nữa là nó hữu ích khi cho phép khách hàng tự thêm mã tùy biến để mở rộng phần mềm cục bộ
    Ví dụ trước đây muốn mở rộng thì phải dùng MCP, còn giờ chỉ cần triển khai các request có cấu trúc bằng CGI là xong

    • Nếu dùng ở môi trường end-user thì việc nối chương trình CGI vào phía trước của MCP cũng là một ý tưởng đáng cân nhắc
      Bản thân dịch vụ MCP có lẽ cũng hoàn toàn có thể được triển khai bằng CGI
      Tôi thấy cần xem kỹ đặc tả hơn

    • Tôi thắc mắc liệu fastcgi có làm mất đi gần như mọi ưu điểm mà cgi vốn có hay không

  • Trước đây tôi từng trực tiếp dùng kết hợp chương trình C và CGI
    Hồi đó không có hơn 100 lõi hay RAM dư dả, và mọi thứ vẫn chạy với tối đa khoảng 1GB bộ nhớ
    Nghĩ đến những gì khi đó còn làm được, tôi tin rằng bây giờ mọi chuyện hẳn còn dễ hơn nhiều

    • Năm 1995, Amazon dùng file thực thi C++ theo cách CGI
      Sau này khi cần load balancing thì họ gặp khó khăn trong việc mở rộng, nhưng trước đó mô hình này hoạt động khá tốt
      Nhân tiện, frontend và backoffice khi đó là 2 file thực thi riêng biệt