[System design interview] CHƯƠNG 10: THIẾT KẾ HỆ THỐNG THÔNG BÁO
Đây là bản dịch tiếng Việt của "System design interview" (Tác giả: Unknown Author). Bài được dịch tự động bởi Aha! Mind Interpreter — pipeline dịch sách kỹ thuật sử dụng Gemini Flash.
⚠️ Bản dịch tự động — có thể có lỗi. Vui lòng đối chiếu với bản gốc tiếng Anh khi cần độ chính xác cao.
CHƯƠNG 10: THIẾT KẾ HỆ THỐNG THÔNG BÁO
Hệ thống thông báo đã trở thành một tính năng rất phổ biến cho nhiều ứng dụng trong những năm gần đây. Một thông báo sẽ cảnh báo người dùng về các thông tin quan trọng như tin tức nóng hổi, cập nhật sản phẩm, sự kiện, ưu đãi, v.v. Nó đã trở thành một phần không thể thiếu trong cuộc sống hàng ngày của chúng ta. Trong chương này, chúng ta sẽ được yêu cầu thiết kế một hệ thống thông báo.
Một thông báo không chỉ đơn thuần là thông báo đẩy (push notification) trên di động. Ba định dạng thông báo phổ biến là: thông báo đẩy trên di động, tin nhắn SMS và Email. Hình 10-1 minh họa một ví dụ cho mỗi loại thông báo này.

Bước 1 - Hiểu vấn đề và xác định phạm vi thiết kế
Xây dựng một hệ thống có khả năng mở rộng (scalable system) để gửi hàng triệu thông báo mỗi ngày không phải là một nhiệm vụ dễ dàng. Nó đòi hỏi sự hiểu biết sâu sắc về hệ sinh thái thông báo. Câu hỏi phỏng vấn được thiết kế có chủ đích là mở và mơ hồ, và trách nhiệm của chúng ta là đặt câu hỏi để làm rõ các yêu cầu.
Ứng viên : Hệ thống hỗ trợ những loại thông báo nào? Người phỏng vấn : Thông báo đẩy (push notification), tin nhắn SMS và email.
Ứng viên: Đây có phải là một hệ thống thời gian thực (real-time system) không? Người phỏng vấn: Hãy coi đây là một hệ thống thời gian thực mềm (soft real-time system). Chúng ta muốn người dùng nhận được thông báo càng sớm càng tốt. Tuy nhiên, nếu hệ thống đang chịu tải cao, một chút chậm trễ là chấp nhận được.
Ứng viên: Các thiết bị được hỗ trợ là gì? Người phỏng vấn: Thiết bị iOS, thiết bị Android và máy tính xách tay/máy tính để bàn.
Ứng viên: Điều gì kích hoạt các thông báo? Người phỏng vấn: Thông báo có thể được kích hoạt bởi các ứng dụng khách (client applications). Chúng cũng có thể được lên lịch ở phía máy chủ (server-side).
Ứng viên: Người dùng có thể chọn không nhận thông báo (opt-out) không? Người phỏng vấn: Có, những người dùng chọn không nhận thông báo sẽ không còn nhận được thông báo nữa.
Ứng viên: Mỗi ngày có bao nhiêu thông báo được gửi đi? Người phỏng vấn: 10 triệu thông báo đẩy trên di động, 1 triệu tin nhắn SMS và 5 triệu email.
Bước 2 - Đề xuất thiết kế cấp cao và nhận được sự đồng thuận
Phần này trình bày thiết kế cấp cao hỗ trợ nhiều loại thông báo khác nhau: thông báo đẩy iOS, thông báo đẩy Android, tin nhắn SMS và Email. Nó được cấu trúc như sau:
-
Các loại thông báo khác nhau
-
Luồng thu thập thông tin liên hệ
-
Luồng gửi/nhận thông báo
Các loại thông báo khác nhau
Chúng ta bắt đầu bằng cách xem xét cách mỗi loại thông báo hoạt động ở cấp độ cao.
Thông báo đẩy iOS
Chúng ta chủ yếu cần ba thành phần để gửi một thông báo đẩy iOS:
-
Provider (Nhà cung cấp). Một nhà cung cấp xây dựng và gửi các yêu cầu thông báo đến Apple Push Notification Service (APNS). Để tạo một thông báo đẩy, nhà cung cấp cung cấp các dữ liệu sau:
-
Token thiết bị (Device token): Đây là một định danh duy nhất được sử dụng để gửi thông báo đẩy.
-
Payload: Đây là một đối tượng JSON (JSON dictionary) chứa dữ liệu Payload của thông báo. Dưới đây là một ví dụ:
-
-
APNS: Đây là m ột dịch vụ từ xa do Apple cung cấp để truyền tải các thông báo đẩy đến các thiết bị iOS.
-
Thiết bị iOS: Đây là ứng dụng khách cuối cùng (end client), nơi nhận các thông báo đẩy.
Thông báo đẩy Android Android áp dụng một luồng thông báo tương tự. Thay vì sử dụng APNS, Firebase Cloud Messaging (FCM) thường được sử dụng để gửi thông báo đẩy đến các thiết bị Android.


Tin nhắn SMS Đối với tin nhắn SMS, các dịch vụ SMS của bên thứ ba như Twilio [1], Nexmo [2] và nhiều dịch vụ khác thường được sử dụng. Hầu hết chúng là các dịch vụ thương mại.
Email Mặc dù các công ty có thể tự thiết lập máy chủ email của riêng mình, nhiều công ty chọn sử dụng các dịch vụ email thương mại. Sendgrid [3] và Mailchimp [4] là một trong những dịch vụ email phổ biến nhất, cung cấp tỷ lệ gửi thành công tốt hơn và phân tích dữ liệu (data analytics).
Hình 10-6 hiển thị thiết kế sau khi đã bao gồm tất cả các dịch vụ của bên thứ ba.


Luồng thu thập thông tin liên hệ
Để gửi thông báo, chúng ta cần thu thập token thiết bị di động, số điện thoại hoặc địa chỉ email. Như được hiển thị trong Hình 10-7, khi người dùng cài đặt ứng dụng của chúng ta hoặc đăng ký lần đầu, các máy chủ API sẽ thu thập thông tin liên hệ của người dùng và lưu trữ vào cơ sở dữ liệu (database).
Hình 10-8 hiển thị các bảng cơ sở dữ liệu được đơn giản hóa để lưu trữ thông tin liên hệ. Địa chỉ email và số điện thoại được lưu trữ trong bảng user, trong khi token thiết bị được lưu trữ trong bảng device. Một
người dùng có thể có nhiều thiết bị, cho thấy rằng một thông báo đẩy có thể được gửi đến tất cả các thiết bị của người dùng.
Dòng chảy gửi/nhận thông báo
Đầu tiên, chúng ta sẽ trình bày thiết kế ban đầu; sau đó, đề xuất một số tối ưu hóa.
Thiết kế cấp cao
Hình 10-9 minh họa thiết kế, và mỗi thành phần hệ thống được giải thích dưới đây.
Dịch vụ 1 đến N : Một dịch vụ có thể là một micro-service, một cron job, hoặc một distributed system kích hoạt các sự kiện gửi thông báo. Ví dụ, một dịch vụ thanh toán gửi email nhắc nhở khách hàng về khoản thanh toán đến hạn hoặc một trang web mua sắm thông báo cho khách hàng rằng gói hàng của họ sẽ được giao vào ngày mai qua tin nhắn SMS.

Hệ thống thông báo : Hệ thống thông báo là trọng tâm của việc gửi/nhận thông báo. Bắt đầu với một thiết kế đơn giản, chỉ một máy chủ thông báo được sử dụng. Nó cung cấp các API cho các dịch vụ từ 1 đến N, và xây dựng các payload thông báo cho các dịch vụ bên thứ ba.
Các dịch vụ bên thứ ba: C ác dịch vụ bên thứ ba chịu trách nhiệm gửi thông báo đến người dùng. Khi tích hợp với các dịch vụ bên thứ ba, chúng ta cần đặc biệt chú ý đến khả năng mở rộng (extensibility). Khả năng mở rộng tốt có nghĩa là một hệ thống linh hoạt có thể dễ dàng thêm hoặc loại bỏ một dịch vụ bên thứ ba. Một yếu tố quan trọng khác cần xem xét là một dịch vụ bên thứ ba có thể không khả dụng ở các thị trường mới hoặc trong tương lai. Ví dụ, FCM không khả dụng ở Trung Quốc. Do đó, các dịch vụ bên thứ ba thay thế như Jpush, PushY, v.v. được sử dụng ở đó.
iOS, Android, SMS, Email : Người dùng nhận thông báo trên thiết bị của họ.
Ba vấn đề được xác định trong thiết kế này:
-
Điểm lỗi duy nhất (SPOF): Một máy chủ thông báo duy nhất đồng nghĩa với SPOF.
-
Khó mở rộng: Hệ thống thông báo xử lý mọi thứ liên quan đến push notification trên một máy chủ duy nhất. Việc mở rộng các database, cache và các thành phần xử lý thông báo khác nhau một cách độc lập là một thách thức.
-
Nút thắt hiệu suất (Performance bottleneck): Xử lý và gửi thông báo có thể tốn nhiều tài nguyên. Ví dụ, việc xây dựng các trang HTML và chờ phản hồi từ các dịch vụ bên thứ ba có thể mất thời gian. Xử lý mọi thứ trong một hệ thống duy nhất có thể dẫn đến quá tải hệ thống, đặc biệt trong giờ cao điểm.
Thiết kế cấp cao (cải tiến) Sau khi liệt kê các thách thức trong thiết kế ban đầu, chúng ta cải tiến thiết kế như sau:
-
Di chuyển database và cache ra khỏi máy chủ thông báo.
-
Thêm nhiều máy chủ thông báo và thiết lập horizontal scaling tự động.
-
Giới thiệu message queues để tách rời các thành phần hệ thống.
Hình 10-10 minh họa thiết kế cấp cao đã được cải tiến.

Cách tốt nhất để xem xét sơ đồ trên là từ trái sang phải:
Dịch vụ 1 đến N : Chúng đại diện cho các dịch vụ khác nhau gửi thông báo thông qua các API được cung cấp bởi các máy chủ thông báo.
Các máy chủ thông báo : Chúng cung cấp các chức năng sau:
-
Cung cấp các API cho các dịch vụ để gửi thông báo. Các API này chỉ có thể truy cập nội bộ hoặc bởi các client đã được xác minh để ngăn chặn spam.
-
Thực hiện các xác thực cơ bản để kiểm tra email, số điện thoại, v.v.
-
Truy vấn database hoặc cache để lấy dữ liệu cần thiết để hiển thị thông báo.
-
Đưa dữ liệu thông báo vào message queues để xử lý song song.
Dưới đây là một ví dụ về API để gửi email:
POST https://api.example.com/v/sms/send Request body

Cache : Thông tin người dùng, thông tin thiết bị, các mẫu thông báo (notification template) được cache.
DB : Nó lưu trữ dữ liệu về người dùng, thông báo, cài đặt, v.v.
Message queues : Chúng loại bỏ sự phụ thuộc giữa các thành phần. Message queues đóng vai trò là bộ đệm khi có khối lượng lớn thông báo cần được gửi đi. Mỗi loại thông báo được gán một message queue riêng biệt để sự cố ở một dịch vụ bên thứ ba sẽ không ảnh hưởng đến các loại thông báo khác.
Workers : Workers là một danh sách các máy chủ kéo các sự kiện thông báo từ message queues và gửi chúng đến các dịch vụ bên thứ ba tương ứng.
Các dịch vụ bên thứ ba : Đã được giải thích trong thiết kế ban đầu.
iOS, Android, SMS, Email : Đã được giải thích trong thiết kế ban đầu.
Tiếp theo, chúng ta hãy xem xét cách mỗi thành phần hoạt động cùng nhau để gửi một thông báo:
- Một dịch vụ gọi các API được cung cấp bởi các máy chủ thông báo để gửi thông báo.
- Các máy chủ thông báo lấy metadata như thông tin người dùng, device token và cài đặt thông báo (notification setting) từ cache hoặc database.
- Một sự kiện thông báo được gửi đến hàng đợi tương ứng để xử lý. Ví dụ, một sự kiện push notification của iOS được gửi đến hàng đợi PN của iOS.
- Các Workers kéo các sự kiện thông báo từ message queues.
- Các Workers gửi thông báo đến các dịch vụ bên thứ ba.
- Các dịch vụ bên thứ ba gửi thông báo đến thiết bị của người dùng.