大纲
- Design WhatsApp : 聊天系统
- Work Solution
- Real-time Service
- Online Status: Pull vs Push
- Design Rate Limiter
- Design Datadog :一种网站的数据统计,类似于google anaysis
这节课之后您可以学会
- 设计聊天系统的核心:Realtime Service
- Pull 与 Push 的进一步比较分析
相关设计题
- Design Facebook Messenger
- Design WeChat
聊天系统
Scenario 场景
设计功能
- 基本功能
- 登录注册
- 通讯录
- 两个用户互相发消息
- 群聊
- 用户在线状态
- 其它功能
- 历史消息
- 多机登录 multi devices
系统限制
- 活跃
- 1B 月活跃用户
- 75% 日活跃 / 月活跃
- 约750M日活跃用户 ——数据来自 Facebook 官方,截止2016年3月
为了计算方便起见,我们来设计一个100M日活跃的WhatsApp
- QPS:
- 假设平均一个用户每天发20条信息
- Average QPS = 100M * 20 / 86400 ~ 20k
- Peak QPS = 20k * 5 = 100k
- 存储:
- 假设平均一个用户每天发10条信息
- 一天需要发 1B,每条记录约30bytes的话,大概需要30G的存储
Service 服务
- Message Service : 负责管理信息
- Real-time Service :负责实时推送信息给接受者
Storage 存储
最简单方法
既然是聊天软件,自然需要一个message table。那么我们需要在message table里面存什么呢?能想到的就是:
那么如果按照这个表来存储,那么如果要查询A与B之间的对话,则需要:
1 | SELECT * FROM message_table |
存在两个问题:
- 效率太低。
- 群聊咋办?gg了
改进——引入thread
thread表示两个的对话!如下图所示,左边是thread。右边是message。
那么thread table里面存什么呢?
看起来没什么问题。但我们漏了一个东西——如何查询我参与了哪些thread呢?
优化
thread table里面加一个owner_id
有几个拥有者,那么thread table就复制几份!——这样虽然比较浪费空间,但是thread里面有些东西是私有的,比如昵称什么的。
问题来了,那么thread table的primary key是什么呢?—— 其实就是owner_id + thread_id的组合呀
流程梳理
- message table,存储每个人发的每条消息,以及对应的thread_id。即[id, thread_id,user_id,content, created_id]
- thread table, 存储每个thread的信息,每个人独享一份数据。即[owner_id,thread_id, participant_ids, is_muted, nickname, created_at, updated_at]
- 查询我的所有thread list——从thead_table里查询给定user_id的所有thread_id即可。因此thread_table用SQL
- 查询我的某个thread的大概信息——从thread_table里查询给定owner_id+thread_id的信息
- 查询我的某个thread的具体消息——从message_table中,根据thread_id查询所有的消息,并且返回.因此message_table用NoSQL
- 查询我与另一个用户是否有一个thread?
即:
Word Solution
用户如何发送消息?
- Client 把消息和接受者信息发送给 server
- Server为每个接受者(包括发送者自己)创建一条 Thread (如果没有的话)
- 创建一条message(with thread_id)
用户如何接受消息?
- 可以每隔10秒钟问服务器要一下最新的 inbox 虽然听起来很笨,但是也是我们先得到这样一个可行解再说
- 如果有新消息就提示用户
Scale 扩展
sharding
- Message 是 NoSQL,自带 Scale 属性
- Thread 按照 user_id 进行 sharding
每隔十秒要inbox?太慢了,优化——Socket
Socket——TCP长连接
Push Service——提高Socket连接服务,可以与Client保持TCP长连接
- 当用户打开APP之后,就连接上Push Service中属于自己的socket
- 有人发消息时,Message Service收到消息,通过Push Service把消息发出去
- 如果一个用户长期不活跃(比如10分钟),就可以断开连接,释放掉网络断开
- 断开连接之后,如何收到消息?
- 打开APP时主动pull + android GCM/IOS APNS
- Socket连接与HTTP连接的最主要区别;
- HTTP连接下,只能客户端问服务器要数据
- Socket连接下,服务器可以主动推送数据给客户端
流程: