解剖 Twitter【2】三段论
网站的架构设计,传统的做法是三段论。所谓 “传统的”,并不等同于 “过时的”。大型网站的架构设计,强调实用。新潮的设计,固然吸引人,但是技术可能不成熟,风险高。所以,很多大型网站,走的是稳妥的传统的路子。
2006 年 5 月 Twitter 刚上线的时候,为了简化网站的开发,他们使用了 Ruby-On-Rails 工具,而 Ruby-On-Rails 的设计思想,就是三段论。
1. 前段,即表述层 (Presentation Tier) 用的工具是 Apache Web Server,主要任务是解析 HTTP 协议,把来自不同用户的,不同类型的请求,分发给逻辑层。
2. 中段,即逻辑层 (Logic Tier)用的工具是 Mongrel Rails Server,利用 Rails 现成的模块,降低开发的工作量。
3. 后段,即数据层 (Data Tier) 用的工具是 MySQL 数据库。
先说后段,数据层。
Twitter 的服务,可以概括为两个核心,1. 用户,2. 短信。用户与用户之间的关系,是追与被追的关系,也就是 Following 和 Be followed。对于一个用户来说,他只读自己 “追” 的那些人写的短信。而他自己写的短信,只有那些 “追” 自己的人才会读。抓住这两个核心,就不难理解 Twitter 的其它功能是如何实现的 [7]。
围绕这两个核心,就可以着手设计 Data Schema,也就是存放在数据层 (Data Tier) 中的数据的组织方式。不妨设置三个表 [8],
1. 用户表:用户 ID,姓名,登录名和密码,状态(在线与否)。
2. 短信表:短信 ID,作者 ID,正文(定长,140 字),时间戳。
3. 用户关系表,记录追与被追的关系:用户 ID,他追的用户 IDs (Following),追他的用户 IDs (Be followed)。
再说中段,逻辑层。
当用户发表一条短信的时候,执行以下五个步骤,
1. 把该短信记录到 “短信表” 中去。
2. 从 “用户关系表” 中取出追他的用户的 IDs。
3. 有些追他的用户目前在线,另一些可能离线。在线与否的状态,可以在 “用户表” 中查到。过滤掉那些离线的用户的 IDs。
4. 把那些追他的并且目前在线的用户的 IDs,逐个推进一个队列 (Queue) 中去。
5. 从这个队列中,逐个取出那些追他的并且目前在线的用户的 IDs,并且更新这些人的主页,也就是添加最新发表的这条短信。
以上这五个步骤,都由逻辑层 (Logic Tier) 负责。前三步容易解决,都是简单的数据库操作。最后两步,需要用到一个辅助工具,队列。队列的意义在于,分离了任务的产生与任务的执行。
队列的实现方式有多种,例如 Apache Mina[9] 就可以用来做队列。但是 Twitter 团队自己动手实现了一个队列,Kestrel [10,11]。Mina 与 Kestrel,各自有什么优缺点,似乎还没人做过详细比较。
不管是 Kestrel 还是 Mina,看起来都很复杂。或许有人问,为什么不用简单的数据结构来实现队列,例如动态链表,甚至静态数组?如果逻辑层只在一台服务器上运行,那么对动态链表和静态数组这样的简单的数据结构,稍加改造,的确可以当作队列使用。Kestrel 和 Mina 这些 “重量级” 的队列,意义在于支持联络多台机器的,分布式的队列。在本系列以后的篇幅中,将会重点介绍。
最后说说前段,表述层。
表述层的主要职能有两 个,1. HTTP 协议处理器 (HTTP Processor),包括拆解接收到的用户请求,以及封装需要发出的结果。2. 分发器 (Dispatcher),把接收到的用户请求,分发给逻辑层的机器处理。如果逻辑层只有一台机器,那么分发器无意义。但是如果逻辑层由多台机器组成,什么样的请求,发给逻辑层里面哪一台机器,就大有讲究了。逻辑层里众多机器,可能各自专门负责特定的功能,而在同功能的机器之间,要分摊工作,使负载均衡。
访问 Twitter 网站的,不仅仅是浏览器,而且还有手机,还有像 QQ 那样的电脑桌面工具,另外还有各式各样的网站插件,以便把其它网站联系到 Twitter.com 上来 [12]。因此,Twitter 的访问者与 Twitter 网站之间的通讯协议,不一定是 HTTP,也存在其它协议。
三段论的 Twitter 架构,主要是针对 HTTP 协议的终端。但是对于其它协议的终端,Twitter 的架构没有明显地划分成三段,而是把表述层和逻辑层合二为一,在 Twitter 的文献中,这二合一经常被称为 “API”。
综上所述,一个能够完成 Twitter 基本功能的,简单的架构如 Figure 1 所示。或许大家会觉得疑惑,这么出名的网站,架构就这么简单?Yes and No,2006 年 5 月 Twitter 刚上线的时候,Twitter 架构与 Figure 1 差距不大,不一样的地方在于加了一些简单的缓存 (Cache)。即便到了现在,Twitter 的架构依然可以清晰地看到 Figure 1 的轮廓。
Figure 1. The essential 3-tier of Twitter architecture
Courtesy http://farm3.static.flickr.com/2683/4051785892_e677ae9d33_o.png
Reference,
[7] Tweets 中常用的工具(http://www.ccthere.com/article/2383833)
[8] 构建基于 PHP 的微博客服务 (http://webservices.ctocio.com.cn/188/9092188.shtml)
[9] Apache Mina Homepage (http://mina.apache.org/)
[10] Kestrel Readme (http://github.com/robey/kestrel)
[11] A Working Guide to Kestrel. (http://github.com/robey/kestrel/blob/master/docs/guide.md)
[12] Alphabetical List of Twitter Services and Applications (http://en.wikipedia.org/wiki/List_of_Twitter_services_and_applications)