第一步:确定需求
先确定功能性的需求点
- 发twitter
- 转发twitter
- 点击关注
- 搜索功能
其次是非功能性的需求
- twitter平台属于高频率的读操作平台,twitter的读写比很高(相比写操作,读操作的频率非常高),设计模式需要考虑这点
- 页面可以快速渲染
- 支持快速发布twitter
- 可接受一定的延迟,注意这里的延迟是指可以允许获取关注人的动态数据的延迟,不是页面渲染的延迟
- 高并发,twitter平均每天每秒会发布5k左右的推文, 加上高频率的读取操作,并发量是成倍增加的,系统必须支持很高的QPS(Query per second)
第二步:确定用户群体
说明:将我们的用户进行类别划分,不同的用户采取不同的实现方式
- 名人:包括法政界、商界、娱乐圈、体育名人,该人群的特点是被广泛关注
- 活跃用户:近期使用过系统,比如近三天登录过系统的用户可划分为活跃用户
- 在线用户:当前正在使用系统的用户
- 非活跃用户:拥有账号,但是近三天没有用过系统
- 僵尸用户:被逻辑删除了的用户(不是实际删除),相当于用户不存在了
第三步: 系统架构
说明:下图是整个twitter系统架构,由于存在高并发以及读操作频繁的情况,我们需要很大程度上用到缓存机制。
第四步:3大工作流
说明:这里主要以service层的功能来进行阐述,每个工作流可能涉及多个service以及service相互调用的情况。
1. 首页工作流:
- service: user service
- 主要功能有查看和存储用户个人信息,提供登录和注册服务
- 数据存储采用mysql数据库+redis(因为用户数量是在可控范围,并且存在关联,比较适合用mysql,redis可以提供缓存,减少数据库读取的压力)
2. 用户关注工作流:
- Graph Service:点击“关注”动作可以用图结构,所有用户就如同图结构中的节点,相互关联。Graph Service包含所有跟关注相关的实现。
- 通过api传参user_id获取关注人,或者通过api传参user_id获取被关注人
- 数据存储依然采用mysql数据库+redis。关联的关系比较稳定,不会经常变动,所以采用redis再合适不过。缓存存储信息可分为两部分:某个人的关注人和被关注人。
- Analytics Service:我们可以将用户之间的互动操作数据,导入到kafka进行实时数据操作。Analytics Service中实现这种触发动作
- User Live Websocket service:在线用户可以即时获取关注人的动态信息,采用websocket技术,服务期和所有在线用户建立一个开放的网路通道,当用户所关注的人有新消息时,可以立即收到信息。
用户的行为数据,也将同步到kafka进行数据分析。比如用在线时长和在线时间段等。并将用户最后的活跃时间保存到redis, 或供其他系统使用。
3. twitter工作流
- Asset service: 用户可发送推文或者查看推文,推文内容可包含文字、图片、视频或链接。
推文长度控制再140字,因此我们需要将URL转换成短链接以节省空间。 - Tweet ingestion service: 不对外提供api, 主要功能就是提交tweet后的一系列操作。将数据同步到kafka
- Tweet Service: 用户可通过user_id/tweet_id来获取推文内容
- **Tweet processor:**用户主页有一个展示自己动态的时间线,或者可查看所关注的人的推文的时间线,如果一次性进行获取,页面渲染速度将会非常慢。所以采用缓存技术,提前计算活跃用户的时间线并存储到redis, 这样活跃用户可及时获取时间线。
- Timeline Service :
针对活跃用户:我们在redis中缓存他的时间线内容。比如说U1被U2,U3所关注,当U1发布新推文,那么通过图结构找到U1的关注者,并同步在缓存中更新他们的时间线。U1自身的时间线也在缓存中得到更新。
针对不活跃用户:比如P1用户登录了,系统将到user Service提取P1的关注人列表数据,再到tweet service获取关注人的推文时间线,存到缓存中,最后将数据返给用户
针对在线用户:通过创建的websocket,通知用户最新的时间线消息
名人用户:假如美国总统川普发了一个推文,如果按照活跃用户的方法去更新,系统操作量非常庞大。所以Timeline service从graph service获取当前用户所关注的名人列表,并从tweet service获取名人推文,然后在redis中进行更新并添加时间戳。当用户下次请求的时候,会先在redis中检查时间戳是否最新,最新的话就直接redis中读取,否则从tweet service获取。
被名人关注的名人用户:听起来有点绕口,我们举个例子,比如美国总统川普和马斯克相互关注,如果川普更新推文,那么关注他的名人马斯克必须优先被通知。同样利用tweet processor方法,该方法接收到kafka的关于川普的推文,那么就会更新关注川普的名人的缓存。但该方法也有瓶颈,Cassandra会面临巨大的负载,Redis需要高效地扩容,所以我们需要确保这些组件支持水平扩容,并且redis避免存储老数据,避免占用内存空间; - Search Service:
上面我们提过tweet ingestion service,当创建了一条推文,kafka将被触发,推文被存储到Elasticsearch数据库。当用户开始搜索时,Search Service会到elasticsearch获取数据,并返回用户。
具体流程:一条热搜的推文,将会有很多人进行搜索,当search service从elasticsearch获取结果后,会将它存储到redis,并保留2-3分钟,这样用户搜索时会首先查看redis,如果没有则search service从
elasticsearch获取。 - Trend Service: 热搜排名,kafka会分析出热搜关键词,并在用户侧进行展示,这些热搜词不需要进行永久存储,我们采用Redis进行短期存储。可以看到Redis在我们系统中占据重要的位置,但redis是内存存储方式,因此我们需要将数据持久化,确保系统故障时数据可恢复。
下图是twitter工作流所涉及的service层之间的关联图,结合文字描述,值得细细品味~
以上,如果你能在系统设计面试中呈现出如上这样的逻辑,那么基本上面试过关就没什么问题了!