Slack: Giải mã bí ẩn đằng sau việc xử lý hàng tỷ tin nhắn mỗi ngày
Trong thế giới kỹ thuật số nơi thông tin di chuyển cực kì nhanh chóng, Slack đã trở thành một công cụ không thể thiếu trong việc giữ cho dòng thông tin liên tục chảy mạch. Mỗi ngày, hàng tỷ tin nhắn được gửi đi qua nền tảng này, từ những cuộc trò chuyện nhóm nhỏ đến các thông báo quan trọng cấp công ty. Đằng sau màn hình là một hệ thống phức tạp để xử lý và phân phối thông tin một cách chính xác và kịp thời. Bài viết này sẽ giải mã bí ẩn đằng sau công nghệ của Slack, làm thế nào mà nó có thể xử lý một lượng lớn tin nhắn mỗi ngày một cách mượt mà. Hãy cùng khám phá những điều kỳ diệu của thế giới công nghệ thông qua lăng kính của Slack.
Slack là một ứng dụng nhắn tin thời gian thực.
Chúng mình có tạo Group cho các bạn cùng chia sẻ và học hỏi về thiết kế hệ thống nha 😄😄😄
Các bạn tham gia để gây dựng cộng đồng System Design Việt Nam thật lớn mạnh nhé 😍😍😍
Cộng Đồng System Design Việt Nam: https://www.facebook.com/groups/sydexa
Kênh TikTok: https://www.tiktok.com/@sydexa.com
API
- API web: Xử lý các phiên người dùng thông qua giao thức HTTP. Cho phép bạn thực hiện các tác vụ như đăng nhập người dùng, tạo kênh và nhóm, gửi tin nhắn và truy xuất thông tin người dùng.
- API thời gian thực: Xử lý tin nhắn trò chuyện, thông báo đang gõ và trạng thái hoạt động. API này sử dụng WebSockets để giao tiếp hai chiều giữa client và server, cho phép cập nhật thời gian thực về các hoạt động trong Slack.
WebSocket là một giao thức giúp truyền dữ liệu hai chiều giữa server-client qua một kết nối TCP duy nhất.
Ban đầu client sẽ gửi yêu cầu khởi tạo kết nối websocket đến server, server kiểm tra và gửi trả kết quả chấp nhận kết nối, sau đó kết nối được tạo và quá trình gửi dữ liệu sẽ được thực hiện.
Một lựa chọn khác là ta có thể sử dụng HTTP/2 kết hợp với Server-Sent Events (SSE) cho giao tiếp hai chiều. Bởi vì HTTP/2 cung cấp đa luồng bằng cách tái sử dụng cùng một kết nối TCP. Do SSE rất phù hợp cho các yêu cầu dữ liệu phản hồi real time liên tục. Ví dụ như chia sẻ vị trí ta chỉ cần chia sẻ 1 lần, sau đó nó sẽ liên tục trả về vị trí của ta hiện tại.
Ngoài ra, API Slack được phân trang để giảm độ trễ và tận dụng băng thông. Họ đã làm điều này bằng cách sử dụng phân trang dựa trên con trỏ (Cursor-based pagination).
Cursor-based pagination
Việc không sử dụng phân trang với offset/limit là do phân trang với OFFSET/LIMIT có thể chậm và không hiệu quả khi xử lý các tập dữ liệu lớn. Ở đây, Slack đã sử dụng phân trang dựa trên con trỏ giúp tốc độ phân trang cực kì nhanh và hiệu quả.
Phương pháp này sử dụng "con trỏ" để xác định kết quả của trang tiếp theo. Ý tưởng đằng sau việc phân trang dựa trên con trỏ là ta có một con trỏ trỏ đến bản ghi cuối cùng của trang hiện tại. Khi người dùng yêu cầu trang kết quả tiếp theo, yêu cầu của họ được gửi cùng với con trỏ đó để xác định vị trí bắt đầu trang kết quả tiếp theo.
Giả sử ta có một bảng dữ liệu như sau:
id | user_id |
---|---|
1 | 1 |
2 | 1 |
3 | 2 |
… | … |
19 | 22 |
20 | 29 |
Vì truy vấn của chúng ta đã sắp xếp tăng dần theo user_id
, do vậy, ta sẽ sử dụng nó làm con trỏ, và giá trị cho con trỏ cho trang đầu tiên sẽ là 29 (user_id cuối cùng của trang 1). Khi request yêu cầu trang thứ 2 thì sẽ gửi cùng giá trị của con trỏ và trong database sẽ thực hiện câu lệnh bên dưới để lấy 20 records của trang tiếp theo.
SELECT * FROM A WHERE user_id > 29 ORDER BY user_id LIMIT 20
Một hình ảnh thực tế của phân trang dựa trên con trỏ ta có thể gặp khi lướt web
LAMP Stack (Linux-Apache-MySQL-PHP)
Slack chạy trên LAMP Stack (Linux-Apache-MySQL-PHP) - là một bộ phần mềm mã nguồn mở, được dùng trong xây dựng website và web app.
MySQL được chọn là cơ sở dữ liệu để lưu trữ tin nhắn cuộc hội thoại vì những lý do sau:
- Đó là một công nghệ đã được kiểm chứng: MySQL là một hệ quản trị cơ sở dữ liệu (DBMS) mã nguồn mở phổ biến với cộng đồng lớn và nhiều tài liệu hướng dẫn.
- Nó cung cấp hỗ trợ nhiều công cụ: MySQL có nhiều công cụ và thư viện hỗ trợ giúp phát triển và quản lý cơ sở dữ liệu dễ dàng.
- Có rất nhiều kỹ sư có kinh nghiệm trong lĩnh vực SQL
Nhưng MySQL theo mặc định ưu tiên tính nhất quán mạnh (Strong Consistency) tức là sau khi một cập nhật được diễn ra thì tất cả các lần đọc dữ liệu sau đó đều trả về giá trị mới được cập nhật nhưng sẽ có nhược điểm là độ trễ cao. Để giải quyết vấn đề này, Slack đã cấu hình MySQL để hoạt động như một cơ sở dữ liệu tính nhất quán cuối cùng (Eventual Consistency) để đảm bảo khả năng sẵn có cao. Điều này cho phép dữ liệu được cập nhật nhanh chóng với độ trễ thấp, nhưng kết quả trả về có thể không phải lúc nào cũng là mới nhất.
Ngoài ra, ứng dụng desktop Slack được xây dựng bằng ElectronJS và ReactJS. Việc sử dụng ElectronJS và ReactJS cho phép Slack tạo ra một ứng dụng desktop có hiệu suất cao, dễ sử dụng và có thể hoạt động trên nhiều hệ điều hành khác nhau.
ElectronJS là một framework dùng để tạo ra Desktop native application. Là một open-source và cross - platform framework. Nhờ có ElectronJS bạn hoàn toàn có thể build một native app chạy trên được cả Window, MacOS và Linux chỉ cần sử dụng HTML, CSS và Javascript.
ReactJS là một thư viện JavaScript phổ biến để xây dựng giao diện người dùng.
Kiến trúc tin nhắn
Các ứng dụng nhắn tin hiện đại phải đối mặt với một thách thức cơ bản: đảm bảo việc truyền tải tin nhắn đáng tin cậy trong khi vẫn duy trì khả năng mở rộng cho lượng người dùng lớn. Điều này chuyển thành ba thuộc tính chính:
- Tính Hợp lệ (Validity): mọi người dùng nhận được tin nhắn đã gửi đi.
- Tính Toàn vẹn (Integrity): một tin nhắn không được gửi đến người dùng nhiều hơn một lần
- Thứ tự tuyến tính (Total order): tin nhắn được gửi theo cùng một thứ tự.
Những điều trên là không thể thực hiện hoàn toàn được. Vì vậy, họ đã nới lỏng một số ràng buộc để tạo ra Slack.
Họ đã xây dựng Slack với kiến trúc client-server.
Trung tâm của hệ thống là chat server, được triển khai dưới dạng một monolith PHP, thực hiện các hoạt động CRUD (tạo, truy xuất, cập nhật và xóa) trên cơ sở dữ liệu MySQL. Để phân phối tin nhắn hiệu quả đến các máy khách, Slack sử dụng Gateway server. Nó là một dịch vụ stateful in-memory sử dụng web-socket để giao tiếp hai chiều theo thời gian thực.
Slack còn sử dụng consistent hashing để ánh xạ kênh Slack-Gateway server một cách hiệu quả
Để giảm thiểu xung đột trong cơ sở dữ liệu do tính đồng thời cao, Slack sử dụng Vitess để phân chia cơ sở dữ liệu MySQL. Vitess phân vùng dữ liệu dựa trên shard key, là ID kênh trong trường hợp của Slack.
Vitess là một dịch vụ quản lý cấu trúc liên kết cho MySQL và giúp mở rộng dễ dàng.
service registry được thực hiện bằng Consul, giúp các thành phần trong kiến trúc xác định vị trí và giao tiếp với nhau một cách liền mạch. Các tác vụ không quan trọng, như lập chỉ mục tin nhắn cho chức năng tìm kiếm, được đưa vào job queue. Các kĩ sư của Slack đã tạo ra job queue của riêng mình mà không sử dụng giải pháp của bên thứ ba như Kafka. Bởi vì họ muốn đáp ứng nhu cầu của mình với ít hoạt động phức tạp.
Ngắt SSL với Envoy Edge Proxy
Slack sử dụng Envoy Edge Proxy để thực hiện ngắt SSL. Ngoài ra, Envoy còn hỗ trợ khởi động nóng (hot restarts), giúp Slack đạt được khả năng sẵn sàng cao. Khởi động nóng cho phép ứng dụng mà không làm gián đoạn kết nối của người dùng, đảm bảo trải nghiệm người dùng liền mạch trong quá trình phát triển code.
Giảm thiểu độ trễ với Snapshot Service
Khi lượng người dùng tăng lên, kích thước dữ liệu của tin nhắn (payload) trong Slack cũng tăng theo, dẫn đến độ trễ cao hơn. Để giải quyết vấn đề này, Slack đã phát triển snapshot service. Dịch vụ này hoạt động theo nguyên tắc chú thích JIT (just-in-time annotation), dự đoán các đối tượng dữ liệu mà người dùng có khả năng truy vấn tiếp theo và chủ động đẩy dữ liệu đó đến client. Bằng cách này, snapshot service cải thiện đáng kể độ trễ và mang lại hiệu suất cao.
Tối ưu hóa trải nghiệm người dùng trên Mobile
Để đơn giản hóa trải nghiệm người dùng trên thiết bị di động, Slack cho phép người dùng trả lời tin nhắn thông qua API web. Điều này giúp tránh việc phải thiết lập kết nối WebSocket riêng biệt cho các thiết bị di động, giảm thiểu phức tạp và cải thiện hiệu suất.
Đảm bảo tính toàn vẹn tin nhắn
Slack sử dụng kỹ thuật "salting" để ngăn chặn việc hiển thị cùng một tin nhắn nhiều lần. Salting là quá trình thêm một token ngẫu nhiên duy nhất (salt) vào mỗi tin nhắn. Nhờ đó, hệ thống có thể đảm bảo tính toàn vẹn của tin nhắn, tránh trường hợp trùng lặp và gây nhầm lẫn cho người dùng.
Cân bằng tải và lưu trữ File Media
Slack sử dụng bộ cân bằng tải để phân phối lưu lượng truy cập giữa các thành phần hệ thống khác nhau. Điều này giúp tối ưu hóa việc sử dụng tài nguyên và cải thiện hiệu suất tổng thể. Đối với các tệp media được chia sẻ trong tin nhắn trò chuyện, Slack lưu trữ chúng trên Amazon S3 - một dịch vụ lưu trữ đối tượng có khả năng mở rộng cao của AWS. Bên cạnh đó, các media được truy cập thường xuyên còn được lưu trữ cache trong CDN để giảm thiểu độ trễ cho người dùng ở các vị trí địa lý khác nhau.
Truy xuất Tin nhắn Hiệu quả
Để người dùng có thể hiệu quả truy xuất các tin nhắn mới kể từ lần tương tác cuối cùng, Slack sử dụng một kỹ thuật gọi là đồng hồ logic (logical clock). Đồng hồ logic, còn được gọi là đồng hồ vector, là một cơ chế theo dõi thứ tự của các sự kiện trong một hệ thống phân tán. Nó bao gồm một bộ đếm được tăng lên với mỗi tin nhắn trò chuyện, cho phép máy chủ xác định và chỉ phân phối các tin nhắn mới nhất mà người dùng chưa xem.
Định dạng Serialize Thrift
Slack sử dụng định dạng serialize Thrift để truyền dữ liệu giữa các thành phần hệ thống giúp đạt được hiệu suất cao.
Luồng Làm Việc Của Ứng Dụng Slack
Tóm tắt luồng gửi tin nhắn của Slack. Client gửi một tin nhắn đến chat server. Sau đó, chat server yêu cầu job queue đánh chỉ mục tin nhắn trong tìm kiếm. Tin nhắn sau đó được định tuyến đến gateway server thông qua consistent hashing. gateway server duy trì bộ đệm trên đĩa của các tin nhắn chưa được gửi tới người dùng. Cơ chế đệm này phục vụ hai mục đích chính.
- Đầu tiên, nó bảo vệ chống mất dữ liệu trong trường hợp máy chủ gặp sự cố bất ngờ.
- Thứ hai, nó đảm bảo rằng chức năng cốt lõi của Slack vẫn hoạt động ngay cả trong các sự cố máy chủ tạm thời. Bằng cách đệm các tin nhắn chưa được gửi tới người dùng, gateway server có thể khôi phục và truyền lại chúng khi được khôi phục thành công.
Cuối cùng, gateway server chịu trách nhiệm phân phối tin nhắn cho người dùng.
Tổng kết
Slack đã mở rộng quy mô để xử lý hàng tỷ tin nhắn mỗi ngày và duy trì 5 triệu phiên làm việc cùng lúc trong giờ cao điểm. Từ sự phát triển của Slack, chúng ta có thể học được rằng:
- Việc tối ưu hóa là một quá trình liên tục, điều chỉnh theo sự phát triển của sản phẩm.
- Sự phức tạp là cần thiết và có thể chấp nhận được, miễn là nó mang lại giải pháp cho một vấn đề cụ thể.
Sydexa.com xin hẹn gặp lại các bạn ở các bài viết thú vị hơn nha
Lời nhắn
Chúng mình có tạo Group cho các bạn cùng chia sẻ và học hỏi về thiết kế hệ thống nha 😄😄😄
Các bạn tham gia để gây dựng cộng đồng System Design Việt Nam thật lớn mạnh nhé 😍😍😍
Cộng Đồng System Design Việt Nam: https://www.facebook.com/groups/sydexa
Kênh TikTok: https://www.tiktok.com/@sydexa.com
All rights reserved