原视频链接
https://www.youtube.com/watch?v=vvhC64hQZMk
Whatsapp是基于chat的APP,了解了Whatsapp的design有助于了解其他所有聊天类APP的design。Whatsapp比较重要的两个功能是1) group chatting,2) read reciepts。Whatsapp也有其他一些功能。
本视频提到的主要有以下features。
1) group messaging, 群聊,可支持200人的群聊
2) sent + delievered + read reciepts, 发的信息处于的状态是已发送,发送成功,还是已读
3) online/ last seen,是否在线,上一次上线是什么时候
4) image sharing,几乎所有的chat app 都应该支持图片分享的功能
5) chats are temporary / permant,如果用户删除了APP,聊天记录是被用久的删除还是保留在server
6) one-to-one chat,一对一聊天
以上的features,image sharing已经在tinder的system design里提到过了 (在tinder笔记里的 store profile的部分https://blog.csdn.net/lauraliu123/article/details/106169112),本视频就不再提了。
接下来是对其余feature的说明。
1)one-to-one chat
假设用户A想向用户B发信息,按照Tinder里面的设计用户A不会直接和任何service通信,A只能把信息发给gateway 1。由gateway 1来转发给内部的服务,再由内部的服务转发给B所在的gateway 2,这个gateway再转发给B这条信息。
为了能处理one-to-one chat,需要decouple出来一个单独的session mircoservice。这个micro service 需要有保存有哪个用户连接到哪个gateway的数据表的数据库,也需要有保存message(from A to B)的数据库,这个数据库能对信息所处的已发送,成功发送,已读的状态进行保存,以及对消息保存,保证消息在传输的途中失败了能重传保证一定传到。
在gateway到用户,和session microservice到gateway的过程中,不能使用像http那样的Client-Server protocol,因为这样消息没法实时的从用户B经过gate way,session service传回A,只能等server的定期刷新。所以一个比较好的选择是用基于TCP的web socket。因为web socket允许peer to peer 传输,这样就能实现用户A和用户B之间的实时传输了。
用户A向用户B发消息,只要A的消息到达session service,那么就算已经sent,已发送,因为session service 会保证一定将信息传给B,那么B在确认收到后回复session service,此时才算消息delievered,即消息成功送达,当B读了消息告知session service,就算消息已读了。
2) online/ last seen
当另一个用户B想查看用户A的上一次在线时间,B需要想service 端进行询问,为了实现这个功能,又decouple出来一个新的service - last seen service,这个service保存了所有用户acticity的时间log,就跟绝这个时间的log来决定用户上一次在线的时间。
假设一个用户上一个活动是3秒前(时间很短,基本等于用户当前还在线),那么可以选择不显示是上一次在线时间而是现实用户当前在线,这个时间长短可以自定义。
另一个可能需要考虑的问题是activity到底是真的用户行为向gateway发的请求,还是APP在后台做的更新而向gateway发送的请求,用户的APP应该能比较准确的判定只有真的用户activity才能存入last seen service的数据中。
3) group messaging
loader balancer, heatbeat 有关的一些service, profile service, image service, email service, SMS service 在本视频中都跳过。
将谁在哪个group的信息单独存储在一个group service 上,这样也是decouple system,避免session service 的负荷过重。当某个用户在用户组中发了消息,那么这个消息先通过gateway传到session service,session service 从group service 中获取这个群所有其他的用户,再将消息通过gateway转发到所有其他用户的设备上。如果一个群的用户太多就不太好,因为session service 可能会不停地需要将消息实时转发几百遍甚至更多,所以群聊的人数一般有限制。
因为会有很多的用户连接在gateway上,gateway本身处理web socket的负荷就很重了,应该将一些检查request是否授权,处理不同的协议下的message type的这些责任从gateway中剥离出来,那么一个办法是将没有任何处理(unparsed)的用户message直接传给session service,另一个更好的做法是将unparsed message传给一个parser mircoservice,在这个microservice上对消息进行parse,进行一些授权认证。
在gourp service上,会存有数据表包括group ID对应的user ID。这个service可能包含多个服务器,意味着group的数据会分别存在不同的机器上,为了避免某一些机器存有过量的数据,而另一些机器上数据较少,一个可以balance load的方法是使用基于group ID的 consistent hashing。
一旦某条信息传输失败了,session service 会retry 至传输成功。实现这个retry的一个技术叫message queue。一个message queue可以保证一旦message进入message queue,这个message一定会被成功传输。如果message queue也retry 失败了,这个message queue会通过group service找到这个group,然后再通过session service,接着gateway向这个群里发送信息的用户发送无法传递此信息。
group reciept,就是群消息的已发送,发送成功,已读状态的显示成本很高,因为需要得到所有群成员的回复,一般很多chat app 也选择不实现这个消息。
goup messaging需要保持的几个特点包括1)Retrial;2)Imdempotency;3)Ordering。
视频里的system design很resilient,可以说是相对完整,当然还有一些具体的trick在真正做messging的时候才会知道的更多。
Facebook messaging 有一个比较有意思的是deprioritizing feature。比如在新年的时候大家都会发祝福,这个时候上面提到的feature 比如last seen/ online,比如read recipiet,信息已读,已发送之类的状态这样的功能可以放在用户之间chat的功能之后,这个时候保证信息被顺利及时地传达更重要。