2 điểm bởi GN⁺ 2025-08-23 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp
  • Để xây dựng máy chủ web hiệu năng cao, trước đây người ta sử dụng nhiều mô hình dựa trên sự kiện như select(), poll(), epoll
  • Tuy nhiên, do giới hạn hiệu năng của các system call này, io_uring đã xuất hiện, đưa vào cách đưa yêu cầu vào hàng đợi để kernel xử lý bất đồng bộ
  • kTLS đảm nhiệm việc mã hóa TLS trong kernel, cho phép thêm các tối ưu như khả năng dùng sendfile() và offload phần cứng
  • Việc đưa vào descriptorless files cung cấp cách tiếp cận được tối ưu cho io_uring mà không cần truyền trực tiếp file descriptor
  • Thông qua dự án mã nguồn mở tarweb kết hợp Rust, io_uring và kTLS, bài viết trình bày cách cung cấp HTTPS mà không cần thêm system call cho mỗi request, đồng thời bàn về các vấn đề liên quan đến an toàn và quản lý bộ nhớ

Sự tiến hóa của kiến trúc máy chủ web hiệu năng cao

  • Từ đầu những năm 2000, nhu cầu về máy chủ web dung lượng lớn ngày càng tăng
  • Ban đầu, cách tạo một tiến trình mới cho mỗi request là phổ biến, nhưng do chi phí cao nên kỹ thuật preforking đã xuất hiện
  • Sau đó, hệ thống tiếp tục phát triển theo hướng đưa vào thread và sử dụng select(), poll() để giảm chi phí chuyển ngữ cảnh
  • Tuy vậy, khi số lượng kết nối tăng lên, select() và poll() vẫn có giới hạn mở rộng vì phải thường xuyên truyền các mảng lớn vào kernel

Sự xuất hiện của epoll

  • Trong môi trường Linux, epoll được đưa vào để xử lý nhiều kết nối hiệu quả hơn so với các cách trước đó
  • epoll chỉ xử lý các thay đổi (delta), nhờ đó giảm lãng phí tài nguyên không cần thiết
  • Dù không loại bỏ hoàn toàn mọi system call, chi phí đã được giảm đáng kể

Tổng quan về io_uring

  • io_uring không gọi system call cho từng request, mà thêm yêu cầu vào hàng đợi trong bộ nhớ để kernel có thể xử lý bất đồng bộ
  • Ví dụ, nếu đưa accept() vào hàng đợi, kernel sẽ xử lý rồi trả kết quả vào completion queue
  • Máy chủ web hoạt động bằng cách thêm yêu cầu vào hàng đợi và kiểm tra kết quả trong một vùng nhớ riêng
  • Để tránh busy loop, nếu không có thay đổi trong hàng đợi thì cả máy chủ web lẫn kernel chỉ gọi system call khi thực sự cần, giúp tiết kiệm điện năng
  • Nếu sử dụng thư viện phù hợp, máy chủ đang hoạt động có thể xử lý request mà không cần system call bổ sung

Môi trường đa lõi và NUMA

  • Khi xét đến môi trường CPU đa lõi hiện đại, chiến lược một thread trên mỗi lõi và giảm tối đa việc chia sẻ cấu trúc dữ liệu là hiệu quả
  • Trong môi trường NUMA, mỗi thread được tối ưu để chỉ truy cập bộ nhớ ở node cục bộ của mình
  • Việc cân bằng phân phối request một cách hoàn hảo vẫn cần thêm nghiên cứu

Cấp phát bộ nhớ

  • Việc cấp phát bộ nhớ vẫn tồn tại ở cả phía kernel lẫn máy chủ web, và cấp phát trong user space cuối cùng cũng dẫn tới system call
  • Ở phía máy chủ web, có thể cấp phát trước các khối bộ nhớ kích thước cố định cho từng kết nối để tránh phân mảnh và thiếu hụt
  • Ở phía kernel cũng cần buffer I/O cho từng kết nối, và có thể điều chỉnh một phần bằng các tùy chọn socket
  • Khi xảy ra thiếu bộ nhớ, hệ thống có thể dẫn đến sự cố nghiêm trọng

Giới thiệu kTLS (kernel TLS)

  • kTLS là tính năng để Linux kernel đảm nhiệm các phép mã hóa và giải mã
  • Phần handshake do ứng dụng xử lý, nhưng sau đó kernel sẽ truyền dữ liệu như thể đó là văn bản thuần
  • Điều này cho phép sử dụng sendfile(), từ đó giảm sao chép bộ nhớ giữa user space và kernel space
  • Nếu card mạng hỗ trợ, còn có lợi thế offload cả phép mã hóa sang phần cứng

Descriptorless Files

  • Đây là cách tiếp cận nhằm giảm overhead phát sinh khi truyền trực tiếp file descriptor từ user space sang kernel space
  • Thông qua register_files, nó dùng một số file nguyên riêng chỉ có hiệu lực trong io_uring và không hiển thị trong /proc/pid/fd
  • Giới hạn ulimit của hệ thống vẫn được áp dụng

Giới thiệu dự án tarweb

  • tarweb là dự án máy chủ web mã nguồn mở minh họa cho việc áp dụng tất cả các công nghệ trên
  • Nó có cấu trúc phục vụ nội dung từ một file tar duy nhất, kết hợp Rust, io_uring, kTLS và các công nghệ hiệu năng cao hiện đại
  • Trong quá trình triển khai thực tế, đã có các vấn đề tương thích giữa io_uring và kTLS (như chưa hỗ trợ setsockopt), và một số vấn đề đã được xử lý bằng Pull Request
  • Dự án vẫn đang ở giai đoạn chưa hoàn thiện, và thư viện rustls của Rust có thể thực hiện cấp phát bộ nhớ trong quá trình handshake
  • Điểm cốt lõi là có thể cung cấp dịch vụ HTTPS mà không cần thêm system call cho từng request

Benchmark và đo hiệu năng

  • Tác giả cho biết vẫn chưa thực hiện benchmark đầy đủ và dự định sẽ kiểm thử hiệu năng sau khi dọn dẹp lại mã nguồn

Vấn đề an toàn của io_uring và Rust

  • Không giống system call đồng bộ, với io_uring thì buffer bộ nhớ không được giải phóng trước khi sự kiện hoàn tất xảy ra
  • Crate io-uring không đảm bảo tính an toàn ở thời điểm biên dịch của Rust, và cũng thiếu các kiểm tra runtime
  • Nếu dùng sai, nó có thể dẫn đến các vấn đề nghiêm trọng tương tự như trong C++, làm suy yếu tính an toàn vốn có của Rust
  • Cần có một crate safer-ring riêng tận dụng mạnh mẽ pinning và borrow checker
  • Vấn đề này hiện đã được cộng đồng thảo luận

Tham khảo và liên kết bổ sung

  • Nội dung này là bài đăng được thảo luận trên HackerNews tính đến ngày 2025-08-22

Chưa có bình luận nào.

Chưa có bình luận nào.