Twitter目前有1.5亿活跃用户(30天内登陆过), Timeline查询是300K QPS, 每天新产生4亿条tweet,折合每秒平均4600 TPS[1],近期(8.2) 高峰期的TPS甚至冲到14万[2]。在如此的规模下做出一个可靠、可扩展的系统实属不易,而做到实时分发和低延迟查询更困难,我们来看看twitter是怎么搞定的。
Twtter的核心业务是将Tweet实时(5秒内)分发到粉丝的Timeline中。Timeline分为两种, home_timeline和user_timeline, home_timeline包含用户所关注的人发的tweet列表,user_timeline是用户自己发表的tweet列表。 Timeline定义为有序的记录列表,记录是三元组(tweetid, userid, flag),三元组按照tweetid排序,最多保留800条。 tweetid由snowflake[3]生成,带有时间字段,因此Timeline实际上是按照时间排序的。
Twitter采取了服务化架构,主要包含UserService(Gizmoduck ), TimelineService,SocalGraphService, Tweet Obect Service(Tweetypie)等。 TimlineService负责管理Timeline数据结构。 Timeline主要存储于Redis,复制3份,内存总空间是几个TB, Redis配置了持久化, user_timeline保存到flock[4]。Tweet Obect Service负责存储Tweet内容, tweet存储基于Gizzard[6]构建的分布式Mysql数据,并使用memcached作缓存服务器。
Twitter的主要流程如上图所示。客户端通过tweet api提交tweet到队列, tweet deamon从队列取tweet,如果是新tweet, deamon调用分发程序fanout服务分发tweet。 fanout从SocalGraph Service(基于Flock构建[4])查询粉丝列表,调用delivery服务分发tweet,每个delivery服务器负责处理4000个粉丝, 服务间调用基于通用的finagle RPC框架[5]。
只有活跃用户(1个月内登陆过)的timeline才缓存到redis。用户查询home_timeline时若遇到redis不命中,则根据Social Graph从所有粉丝的user_timeline构建,并缓存到redis, redis失配一般耗时400ms。
文章[2]指出 twitter架构最大的挑战是处理明星用户(粉丝很多的用户),譬如lady gaga有三千多万粉丝, 分发一条tweet至少5分钟,这个时间差导致转发可能先于原tweet达到粉丝。 twitter的工程师正在考虑不分发明星tweet,而是用读时拉取机制替代。
以上只是twitter大致的架构,仍然有大量细节是不明确的。譬如:
· redis采用3副本,那么复制如何实现?宕机如何做恢复? redis副本如何保证一致?
· redis持久化到磁盘有什么用? redis操作失败会导致Timeline redis副本和数据库不一致,该如何处理?
· 如何保证非活跃用户的timeline不进入redis。活跃用户是通过登录行为或者Timeline查询行为来确定的,即便是非活跃用户也会关注热门用户,所以其home_timeline的更新热度也是很高的。可行的办法是每日统计活跃用户,调用redis expire命令刷新缓存时间。
· 各种并发问题,譬如同一个用户发的消息是否会并发执行? redis不命中时,加载操作和事件分发之间的并发问题。
参考
[1] Twtter 的访问量: https://blog.twitter.com/2013/new-tweets-per-second-record-and-how
[2] Twitter 的架构: http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html
[3] snowflake: https://github.com/twitter/snowflake
[4] flockdb: https://github.com/twitter/flockdb
[5] finagle: https://github.com/twitter/finagle
[6] gizzard: https://github.com/twitter/gizzard