Tầng ứng dụng (Application Layer)
Chủ đề · 10 phút đọc· The System Design Primer - mục "Application layer"
Mục lục Nội dung gốc Ghi chú của người dịch Tầng ứng dụng (Application Layer)
Nội dung gốc
Nguồn: Intro to architecting systems for scale
Tách tầng web (web layer) khỏi tầng ứng dụng (application layer, còn gọi là tầng nền tảng - platform layer) cho phép bạn mở rộng và cấu hình hai tầng này một cách độc lập. Thêm một API mới đồng nghĩa với thêm máy chủ ứng dụng, mà không nhất thiết phải thêm máy chủ web. Nguyên tắc đơn trách nhiệm (single responsibility principle) khuyến khích các dịch vụ nhỏ, tự chủ, phối hợp với nhau. Các nhóm nhỏ phụ trách các dịch vụ nhỏ có thể lên kế hoạch mạnh tay hơn cho tăng trưởng nhanh.
Bếp và bộ phận phục vụ trong nhà hàng Nháp
Nhà hàng tách riêng bộ phận phục vụ bàn (đón khách, ghi order, bưng đồ) và bộ phận bếp (nấu ăn). Khi quán đông khách gọi món cầu kỳ, chủ quán chỉ cần thuê thêm đầu bếp mà không cần thuê thêm người phục vụ, và ngược lại nếu khách đông nhưng gọi món đơn giản. Tương tự, tách tầng web (đón request, trả file tĩnh) khỏi tầng ứng dụng (xử lý logic) cho phép mở rộng hoặc cấu hình riêng từng tầng đúng nơi đang quá tải. → Không phải lúc nào thêm tải cũng cần thêm cả hai tầng cùng lúc.
Tầng web giữ nguyên số máy trong khi tầng ứng dụng co giãn riêng — thêm máy chủ ứng dụng mới (ví dụ cho một API mới) không đòi hỏi phải thêm máy chủ web. Chiều ngược lại cũng đúng: tầng web có thể mở rộng mà không đụng tới tầng ứng dụng.
Các worker ở tầng ứng dụng cũng giúp hiện thực hóa xử lý bất đồng bộ (asynchronism) .
Microservices
Liên quan tới chủ đề này là microservices , có thể mô tả là một tập các dịch vụ nhỏ, dạng mô-đun, triển khai được độc lập. Mỗi dịch vụ chạy một tiến trình riêng và giao tiếp qua một cơ chế nhẹ, được định nghĩa rõ ràng, để phục vụ một mục tiêu nghiệp vụ. 1
Ví dụ, Pinterest có thể có các microservice sau: hồ sơ người dùng, người theo dõi, bảng tin (feed), tìm kiếm, tải ảnh lên, v.v.
Mỗi quầy trong khu ẩm thực chỉ bán một món Nháp
Ở khu ẩm thực (food court), mỗi quầy chuyên bán một món: quầy phở, quầy cơm, quầy nước. Mỗi quầy tự quản lý nguyên liệu, nhân sự, công thức riêng, không phụ thuộc quầy khác — quầy phở đóng cửa sửa chữa không ảnh hưởng quầy cơm. Microservices chia hệ thống theo cách tương tự: mỗi dịch vụ nhỏ (hồ sơ người dùng, tìm kiếm, tải ảnh...) tự triển khai, tự mở rộng độc lập. → Đổi lại, phối hợp giữa các "quầy" (dịch vụ) phức tạp hơn một gian bếp chung duy nhất.
Khám phá dịch vụ (Service Discovery)
Các hệ thống như Consul , Etcd và Zookeeper giúp các dịch vụ tìm thấy nhau bằng cách theo dõi tên, địa chỉ và cổng (port) đã được đăng ký. Kiểm tra sức khỏe (health check) giúp xác minh dịch vụ còn hoạt động đúng, và thường được thực hiện qua một endpoint HTTP . Cả Consul và Etcd đều có sẵn một kho khóa - giá trị (key-value store) , hữu ích để lưu giá trị cấu hình và các dữ liệu dùng chung khác.
Ứng dụng gọi xe biết tài xế nào đang online gần đó Nháp
Ứng dụng gọi xe không ghi cứng "tài xế A luôn chạy ở khu X": tài xế bật app thì vào danh sách, lâu không thấy tín hiệu thì bị gạch tên, khách chỉ được ghép với người đang thực sự online. Khám phá dịch vụ cũng vậy: mỗi bản sao dịch vụ đăng ký tên, địa chỉ, cổng; hệ thống định kỳ gọi health check (thường là một endpoint HTTP) và gạch bản không trả lời. → Dịch vụ khác tra danh sách "đang sống" này, thay vì gọi vào một địa chỉ cứng có thể đã chết.
Nhược điểm: tầng ứng dụng
Thêm một tầng ứng dụng gồm các dịch vụ liên kết lỏng (loosely coupled) đòi hỏi một cách tiếp cận khác về kiến trúc, vận hành và quy trình (so với một hệ thống nguyên khối - monolithic).
Microservices có thể làm tăng độ phức tạp trong triển khai và vận hành.
Nguồn và đọc thêm
Ghi chú của người dịch 1. Bản gốc gộp hai ý khác nhau - tách ra mới hiểu đúng
Mục này nói hai chuyện có liên quan nhưng không giống nhau:
Tách tầng web khỏi tầng ứng dụng : tầng web lo phần "vỏ" (nhận kết nối, TLS, phục vụ tệp tĩnh, render trang), tầng ứng dụng lo logic nghiệp vụ. Hai tầng có đặc điểm tải khác nhau nên mở rộng riêng rẽ thì tiết kiệm hơn. Việc này làm được ngay cả khi ứng dụng vẫn là một khối .
Chia tầng ứng dụng thành microservices : cắt logic nghiệp vụ theo miền (domain), mỗi phần triển khai độc lập. Đây là quyết định lớn hơn nhiều, và cái giá chủ yếu nằm ở tổ chức và vận hành, không nằm ở code.
Điều kiện chung cho cả hai: tầng ứng dụng phải không giữ trạng thái (stateless) , như đã bàn ở Clones . Có vậy mới thêm, bớt máy chủ ứng dụng sau bộ cân bằng tải một cách tự do.
2. Microservices năm 2026: hết thời "mặc định", quay về cân nhắc
Sau khoảng một thập kỷ làm phong trào, cộng đồng đã tỉnh táo hơn:
Nguyên khối mô-đun (modular monolith) thường là điểm khởi đầu hợp lý: một đơn vị triển khai, nhưng bên trong chia mô-đun theo miền với ranh giới rõ ràng. Khi một mô-đun thật sự cần mở rộng hoặc phát hành độc lập thì mới tách ra.
Bài viết năm 2023 của nhóm Prime Video (Amazon) về việc gộp một hệ giám sát chất lượng video từ kiến trúc phân tán về một tiến trình để giảm chi phí là ví dụ hay được trích dẫn: kiến trúc phải theo bài toán, không theo mốt.
Định luật Conway : kiến trúc hệ thống có xu hướng phản chiếu cấu trúc giao tiếp của tổ chức. Microservices chủ yếu giải quyết bài toán nhiều nhóm cùng phát hành độc lập . Một nhóm 5 người mà có 20 dịch vụ thường là dấu hiệu chia quá sớm.
Nguyên khối (có mô-đun) Microservices Triển khai Một đơn vị, đơn giản Nhiều đơn vị, cần CI/CD và hạ tầng tốt Gọi giữa các phần Gọi hàm trong tiến trình, nhanh, không lỗi mạng Gọi qua mạng: độ trễ, timeout, thử lại Giao dịch dữ liệu Transaction cơ sở dữ liệu bình thường Phải dùng saga, outbox, chấp nhận nhất quán cuối cùng Mở rộng Cả khối cùng mở rộng Mở rộng riêng từng dịch vụ Quan sát, gỡ lỗi Một log, một stack trace Cần distributed tracing (OpenTelemetry) Hợp với Nhóm nhỏ, sản phẩm đang tìm hướng Nhiều nhóm, miền nghiệp vụ đã ổn định
3. Bẫy lớn nhất: "nguyên khối phân tán" (distributed monolith)
Chia code thành nhiều dịch vụ nhưng chúng vẫn dùng chung một cơ sở dữ liệu, phải triển khai cùng lúc, hoặc gọi nhau đồng bộ thành chuỗi dài - đó là nhận đủ nhược điểm của cả hai kiểu mà không có ưu điểm nào. Dấu hiệu nhận biết:
Sửa một tính năng phải phát hành đồng thời nhiều dịch vụ.
Nhiều dịch vụ cùng đọc ghi chung một bảng.
Một request đi qua một chuỗi dài lời gọi đồng bộ. Như đã tính ở Các mẫu sẵn sàng , mỗi phụ thuộc nối tiếp là một phép nhân làm giảm độ sẵn sàng.
Nguyên tắc thường dùng: mỗi dịch vụ sở hữu dữ liệu của riêng nó (database per service), các dịch vụ khác chỉ truy cập qua API hoặc sự kiện.
4. Khám phá dịch vụ hiện nay trông thế nào
Bản gốc viết khi phải tự dựng Consul hay ZooKeeper. Hiện nay bức tranh đã khác:
Kubernetes là môi trường phổ biến nhất, và nó có sẵn cơ chế khám phá dịch vụ: đối tượng Service cấp một tên DNS ổn định trỏ tới các pod phía sau. Bản thân Kubernetes lưu trạng thái cụm trong etcd - nên etcd vẫn rất quan trọng, chỉ là ít khi bạn dùng trực tiếp.
Service mesh (Istio, Linkerd, hoặc proxy Envoy) đưa khám phá dịch vụ, mã hóa mTLS, thử lại, timeout và ngắt mạch ra khỏi code ứng dụng, đặt vào lớp hạ tầng.
Consul vẫn được dùng, nhất là trong môi trường lai giữa máy ảo và container.
ZooKeeper đang rút dần khỏi một số hệ sinh thái lớn: Apache Kafka đã chuyển sang cơ chế KRaft và bỏ hẳn ZooKeeper từ bản 4.0.
Các link Etcd trong bản gốc (coreos.com) đã cũ, tài liệu hiện nằm tại etcd.io.
5. Kiểm tra sức khỏe: phân biệt cho đúng
Bản gốc chỉ nói "health check qua HTTP endpoint", nhưng thực tế có ít nhất hai loại, và nhầm lẫn giữa chúng là nguyên nhân của nhiều sự cố:
Liveness : tiến trình còn sống không? Sai thì khởi động lại. Không nên kiểm tra phụ thuộc bên ngoài ở đây - nếu cơ sở dữ liệu chết mà liveness lại kiểm tra cơ sở dữ liệu, toàn bộ máy chủ ứng dụng sẽ bị khởi động lại liên tục, làm sự cố tệ hơn.
Readiness : đã sẵn sàng nhận lưu lượng chưa? Sai thì tạm rút khỏi bộ cân bằng tải, không khởi động lại. Dùng khi đang khởi động, đang làm ấm cache, hoặc đang quá tải.
Kubernetes có sẵn cả hai loại probe này (cùng startup probe), còn gRPC có giao thức health checking chuẩn riêng.
6. Nối với các mục khác
Clones : điều kiện không trạng thái để nhân bản máy chủ ứng dụng.
Bộ cân bằng tải và Reverse proxy : đứng trước tầng web và tầng ứng dụng, phân phối lưu lượng tới các bản sao.
Xử lý bất đồng bộ : các worker ở tầng ứng dụng lấy việc từ hàng đợi; đây cũng là cách giảm phụ thuộc đồng bộ giữa các microservice.
Giao tiếp : HTTP, REST, RPC - các "cơ chế nhẹ, định nghĩa rõ ràng" mà microservices dùng để nói chuyện với nhau.
Cơ sở dữ liệu : mục kế tiếp, nơi bàn chuyện chia dữ liệu - thứ khó nhất khi tách dịch vụ.
7. Câu hỏi nên tự hỏi trong buổi phỏng vấn system design
Có thật sự cần microservices không, hay tách tầng web và tầng ứng dụng, cộng thêm worker bất đồng bộ là đủ? (Người phỏng vấn thường đánh giá cao việc biện minh được lựa chọn đơn giản.)
Ranh giới dịch vụ đặt theo đâu? (Theo miền nghiệp vụ và quyền sở hữu dữ liệu, không theo tầng kỹ thuật.)
Các dịch vụ gọi nhau đồng bộ hay qua sự kiện? Chuỗi gọi đồng bộ dài nhất là bao nhiêu bước?
Khi một dịch vụ chậm, điều gì ngăn nó kéo sập cả hệ thống? (Timeout, ngắt mạch, giới hạn tải.)
Dịch vụ tìm thấy nhau bằng cách nào, và làm sao biết một bản sao đã hỏng để rút nó ra?
Đối chiếu README gốc của repo:
Đánh dấu hoàn thành Hoàn tác
Bài tiếp theo Cơ sở dữ liệu (Database)