Giới thiệu
Trong phần này, chúng ta sẽ cùng tìm hiểu thiết kế hệ thống của ứng dụng Chat (Nhắn tin). Mỗi chúng ta đều sẽ có ít nhất một ứng dụng chat, nó có thể là Messengers, Whatsapp, Wechat, Line, Discord…
Mỗi ứng dụng Chat thì sẽ có những chức năng khác nhau, vì vậy việc xác định rõ ràng các yêu cầu của User là cực kỳ quan trọng. Ví dụ như bạn không thể thiết kế hệ thống tập trung vào nhắn tin nhóm, trong khi ý của nhà tuyển dụng là chat 1v1.
Theo như Phần 2: Công thức cho buổi phỏng vấn về Thiết kế Hệ thống, ta sẽ đi theo 4 bước. Bài viết này sẽ gồm 2 bước, và Phần 4 sẽ gồm 2 bước tiếp theo.
- – B1: Xác định vấn đề và phạm vi thiết kế
- – B2: Thiết kế tổng quan và xác định các thành phần
Bước 1: Xác định vấn đề và phạm vi thiết kế
Trước tiên là cần xác định được ứng dụng Chat sẽ được thiết kế. Trên thị trường, có loại tập trung vào 1v1 như Messenger, WeChat, Whatapp.
Ngoài ra có loại tập trung vào Chat theo nhóm như Slack, hay chat phục vụ cho chơi game như Discord – ứng dụng này tập trung vào tương tác trong nhóm lớn (nhắn tin, âm thanh) và cần độ trễ thấp khi nói chuyện. Dưới đây là một ví dụ về cuộc nói chuyện có thể xảy ra:
- Ứng viên: Ứng dụng Chat này phục vụ cho khách hàng như nào? 1v1 hay chat theo nhóm?
- Người phỏng vấn: Cho cả chat 1v1 và chat theo nhóm
- Ứng viên: Đây có phải là ứng dụng di động không? Hay là ứng dụng web? Hoặc cả hai?
- Người phỏng vấn: Cả hai.
- Ứng viên: Quy mô của ứng dụng là gì? Là ứng dụng khởi nghiệp hay quy mô lớn? Người phỏng vấn: Nó nên hỗ trợ 50 triệu người dùng hàng ngày (DAU – Daily Active Users).
- Ứng viên: Đối với nhóm chat, giới hạn thành viên là bao nhiêu người? Người phỏng vấn: Tối đa 100 người.
- Ứng viên: Những tính năng quan trọng cho ứng dụng chat là gì? Nó có hỗ trợ đính kèm không?
- Người phỏng vấn: Chat 1-1, nhóm chat, hiển thị trạng thái trực tuyến. Hệ thống chỉ hỗ trợ tin nhắn văn bản.
- Ứng viên: Có giới hạn kích thước tin nhắn không?
- Người phỏng vấn: Có, chiều dài văn bản phải ít hơn 100,000 ký tự.
- Ứng viên: Yêu cầu có mã hóa end-to-end không?
- Người phỏng vấn: Hiện tại không cần thiết, nhưng nếu có thời gian, chúng ta sẽ thảo luận về điều đó.
- Ứng viên: Lưu trữ lịch sử chat trong bao lâu?
- Người phỏng vấn: Vĩnh viễn.
Với những yêu cầu trên, chúng ta có thể tóm gọn lại và cần phải thiết kế một ứng dụng chat như Facebook Messenger, tập trung vào những tính năng sau:
- Chat 1-1 với độ trễ thấp
- Nhóm chat nhỏ (tối đa 100 người)
- Chức năng báo trạng thái trực tuyến
- Hỗ trợ nhiều thiết bị. Cùng một tài khoản có thể đăng nhập vào nhiều tài khoản cùng một lúc.
- Thông báo
- Cần phải chú ý về khả năng chịu tải. Chúng ta sẽ thiết kế một hệ thống hỗ trợ 50 triệu DAU
Bước 2: Đề xuất thiết kế tổng quan
Để phát triển một thiết kế chất lượng cao, chúng ta nên có kiến thức cơ bản về cách Clients và Servers tương tác. Trong hệ thống chat, Client có thể là ứng dụng di động hoặc ứng dụng web.
Client không giao tiếp trực tiếp với nhau. Thay vào đó, mỗi Client kết nối với một Ứng dụng Chat, hỗ trợ tất cả các tính năng đã đề cập ở trên. Hãy cùng tập trung vào các hoạt động cơ bản. Ứng dụng Chat phải hỗ trợ các chức năng sau:
- Nhận tin nhắn từ các Client.
- Tìm người nhận (Client) phù hợp cho mỗi tin nhắn và chuyển tin nhắn đến người nhận.
- Nếu người nhận không online, giữ tin nhắn cho người nhận đó trên máy chủ cho đến khi người đó online.
- Hình thể hiện mối quan hệ giữa các máy khách (người gửi và người nhận) và dịch vụ chat.
Khi một Client muốn giao tiếp với Ứng dụng Chat, Client kết nối với Ứng dụng Chat bằng một hoặc nhiều giao thức mạng. Đặc biệt đối với Ứng dụng Chat, sự chọn lựa về giao thức mạng là rất quan trọng. Vấn đề này cần hỏi kỹ với người Phỏng vấn.
Đối với hầu hết các ứng dụng Client/Server, Request (Yêu cầu) được khởi tạo bởi Client. Điều này cũng đúng cho phía gửi trong ứng dụng chat. Trong Hình trên, khi người gửi gửi một tin nhắn đến người nhận qua Ứng dụng Chat, nó sử dụng giao thức HTTP được kiểm định thời gian, đó là giao thức web phổ biến nhất.
Khi ấy, Client mở một kết nối HTTP với Ứng dụng Chat và gửi tin nhắn, thông báo cho Ứng dụng Chat gửi tin nhắn đến người nhận. Việc sử dụng Header keep-alive hiệu quả trong trường hợp này vì cho phép Client duy trì một kết nối liên tục với Ứng dụng Chat. Nó cũng giảm số lần handshakes TCP.
HTTP là một lựa chọn tốt ở phía Client, và nhiều ứng dụng chat phổ biến như Facebook đều sử dụng HTTP khi gửi tin nhắn.
Tuy nhiên, để Ứng dụng Chat gửi tin nhắn cho người nhận thì phức tạp hơn. Vì HTTP được khởi tạo bởi Client, nên việc gửi tin nhắn từ Server không hề dễ dàng. Trong nhiều năm qua, nhiều kỹ thuật đã được triển khai để gửi tin nhắn từ phía server được triển khai như polling, long polling, và WebSocket. (So sánh poolling, long polling và WebSocket sẽ được viết ở một bài khác).
Trong trường hợp này, chúng ta sẽ chọn WebSocket làm kênh giao thức chính giữa Client và Server bởi vì khả năng giao tiếp 2 chiều. (Một lưu ý nhỏ là không phải mọi request đều cần giao thức WebSocket, mà có vẫn có thể dùng HTTP truyền thống). Hình dưới cho thấy ứng dụng Chat được chia thành 3 phần chính: stateless, stateful và third-party integration.
Khả năng mở rộng:
- Đối với một Ứng dụng Chat nhỏ, các dịch vụ ở trên có thể để trong 1 server. Số lượng kết nối cùng lúc có thể bị nghẽn ở đây. Ví dụ có 1 triệu người dùng sử dụng cùng lúc, mỗi người dùng cần 10K memory, thì chỉ cần 10GB memory để đảm bảo được nhu cầu. Có rất nhiều lý do vì sao không nên chỉ dùng 1 server, tuy nhiên việc bắt đầu bằng 1 server cũng không sao, điều quan trọng là phải nói với người phỏng vấn về điều này.
- Khi kết hợp mọi thứ lại với nhau, ta có sơ đồ hoạt động như sau:
Chức năng của các thành phần:
- Client duy trì một kết nối WebSocket liên tục đến Server Chat để truyền tin nhắn theo thời gian thực
- Các Server Chat hỗ trợ việc gửi/nhận tin nhắn
- Server trạng thái quản lý trạng thái online/offline
- Các Server API xử lý mọi thứ bao gồm đăng nhập, đăng ký, thay đổi thông tin người dùng
- Server thông báo gửi thông báo đẩy (Push notifications)
- Cuối cùng, hệ thống key-value store được sử dụng để lưu trữ lịch sử chat. Khi một người dùng offline trở lại online, họ sẽ có thể thấy toàn bộ lịch sử chat trước đó.
Data Models
Lưu trữ tin nhắn 1v1:
- message_id (PK)
- message_from
- message_to
- content
- created_at
Lưu trữ tin nhắn Nhóm:
- channel_id (PK)
- message_id (PK)
- user_id
- content
- created_at
Điều kiện để tạo message_id cần phải đảm bảo được việc thể hiện thứ tự của tin nhắn và phải không bị trùng lặp. Có thể dùng Global 64bit Sequence Number Generator như Snowflake hay dùng Local Sequence Number Generator. Để đơn giản thì sẽ dùng cách 2.
(Còn tiếp…)
Nguồn tài liệu thảm khảo: