• Feeds

  • Twitter系统运维经验

    最近看到的另外一个介绍Twitter技术的视频[Slides] [Video (GFWed)],这是Twitter的John Adams在Velocity 2009的一个演讲,主要介绍了Twitter在系统运维方面一些经验。 本文大部分整理的观点都在Twitter(@xmpp)上发过,这里全部整理出来并补充完整。

    Twitter没有自己的硬件,都是由NTTA来提供,同时NTTA负责硬件相关的网络、带宽、负载均衡等业务,Twitter operations team只关注核心的业务,包括Performance,Availability,Capacity Planning容量规划,配置管理等,这个可能跟国内一般的互联网公司有所区别。

    1. 运维经验

    * Metrics

    Twitter的监控后台几乎都是图表(critical metrics),类似驾驶室的转速表,时速表,让操作者可以迅速的了解系统当前的运作状态。联想到我们做的类似监控后台,数据很多,但往往还需要浏览者做二次分析判断,像这样满屏都是图表的方法做得还不够,可以学习下这方面经验。 据John介绍可以从图表上看到系统的瓶颈-系统最弱的环节(web, mq, cache, db?)
    根据图表可以科学的制定系统容量规划,而不是事后救火。Twitter operation dashboard

    * 配置管理

    每个系统都需要一个自动配置管理系统,越早越好,这条一整理发到Twitter上去之后引起很多回应。

    * Darkmode

    配置界面可以enable/disable 高计算消耗或高I/O的功能,也相当于优雅降级,系统压力过大时取消一些非核心但消耗资源大的功能。

    * 进程管理

    Twitter做了一个”Seppaku” patch, 就是将Daemon在完成了n个requests之后主动kill掉,以保持健康的low memory状态,这种做法据了解国内也有不少公司是这样做。

    * 硬件

    Twitter将CPU由AMD换成Xeon之后,获得30%性能提升,将CPU由双核/4核换成8核之后,减少了40%的CPU, 不过John也说,这种升级不适合自己购买硬件的公司。

    2. 代码协同经验

    * Review制度

    Twitter有上百个模块,如果没有一个好的制度,容易引起代码修改冲突,并把问题带给最终用户。所以Twitter有一强制的source code review制度, 如果提交的代码的svn comment没有”reviewed by xxx”, 则pre-commit脚本会让提交失败, review过的代码提交后会通过自动配置管理系统应用到上百台服务器上。 有@xiaomics同学在Twitter上马上就问,时间成本能否接受?如果有紧急功能怎么办?个人认为紧急修改时有两人在场,一人修改一人review也不是什么难事。

    * 部署管理

    从部署图表可以看到每个发布版本的CPU及latency变化,如果某个新版本latency图表有明显的向上跳跃,则说明该发布版本存在问题。另外在监控首页列出各个模块最后deploy版本的时间,可以清楚的看到代码库的现状。

    * 团队沟通

    Campfire来协同工作,campfire有点像群,但是更适合协同工作。对于Campfire就不做更多介绍,可参考Campfire官方说明。

    3. cache

    • Memcache key hash, 使用FNV hash 代替 MD5 hash,因为FNV更快。
    • 开发了Cache Money plugin(Ruby), 给应用程序提供read-through, write-through cache, 就像一个db访问的钩子,当读写数据库的时候会自动更新cache, 避免了繁琐的cache更新代码。
    • “Evictions make the cache unreliable for important configuration data”,Twitter使用memcache的一条经验是,不同类型的数据需放在不同的mc,避免eviction,跟作者前文Memcached数据被踢(evictions>0)现象分析中的一些经验一致。
    • Memcached SEGVs, Memcached崩溃(cold cache problem)据称会给这种高度依赖Cache的Web 2.0系统带来灾难,不知道Twitter具体怎么解决。
    • 在Web层Twitter使用了Varnish作为反向代理,并对其评价较高。

    PubSubHubbub的价值

    HTTP是大部分互联网应用接口的首选协议,但是由于HTTP协议短连接且是单向请求(request/response)的特性,决定了调用方要获得实时结果,需要不断的轮询(Polling)服务接口。从而造成大量无意义的请求及服务器相应的开销。针对此现状,许多方案应运而生。比如基于XMPP pubsub的方案、基于HTTP的web-hook的方案、适合即时通讯的comet方案等。但是由于HTTP的简洁及标准的力量,上述方案都没有得到大规模的流行HTTP Polling的现状暂时无人能够改变。

    PubSubHubbub是Google推出的一个基于Web-hook方式的解决方案,它包括PubSubHubbub协议及一个开源的参考实现(Reference Implementation)

    原理

    原理及数据流图在官网的Slide上已经有详细描述,这里以静态图补充。

    pubsubhubbub

    价值

    Publisher发布方

    许多Blog服务提供者来说,RSS对它们来说是一个鸡肋,对运营及广告等业务没什么帮助,但是却流量很大。因此他们经常非常矛盾的维护着这个接口。如果PubSubHubbub能够在业界大范围的适用,至少从访问压力层面解除了BSP对提供RSS接口之忧。

    特例 Realtime RSS(Twitter, 微博服务等)

    Twitter/微博等realtime RSS可以从此方案受益,按照常规的方案,订阅方为了获取realtime的结果,几乎需要以每分钟1次的频率来访问RSS API, 如果订阅方能够以PubSubHubbub的方式来访问RSS,那么RSS API的请求量几乎可以降为0

    Subscriber订阅方

    Subscriber比如RSS阅读器,搜索引擎等类似业务。Google Reader看似PubSubHubbub最大的赢家。
    另外在有hub的前提下,即使Publisher不支持PubSubHubbub, subscriber可以通过hub直接取到feed内容,就是说类似阅读器这样的应用现在就可以完全切换到PubSubHubbub体系上。

    不适合的场景

    Twitter client, 由于client处于防火墙后,通常也没有固定的可直接访问的HTTP Endpoint, 所以没法适用PubSubHubbub

    最后,PubSubHubbub是否在业界大范围的改变现状,我们拭目以待。

    Friendfeed的MySQL key/value存储

    这是一篇2009年初的资料How FriendFeed uses MySQL to store schema-less data,相信大部分人已经看过了。如Fenng的中文介绍FriendFeed 使用 MySQL 的经验。本文从不同的角度再补充下。作者几个月前也曾经在广州技术沙龙作过一次Key value store漫谈的演讲,许多参会人员对key value方向存在强烈的使用意愿,但同时也对完全抛弃MySQL存在疑虑,本文介绍的方案也可以给这些人员一些架构参考。

    需求

    250M entities, entities表共有2.5亿条记录,当然是分库的。

    典型解决方案:RDBMS

    问题:由于业务需要不定期更改表结构,但是在2.5亿记录的表上增删字段、修改索引需要锁表,最长需要1小时到1天以上。

    Key value方案

    评估Document类型数据库,如CouchDB
    CouchDB问题: Performance? 广泛使用? 稳定性? 抗压性?

    MySQL方案

    MySQL相比Document store优点:

    • 不用担心丢数据或数据损坏
    • Replication
    • 非常熟悉它的特性及不足,知道如何解决

    结论

    综合取舍,使用MySQL来存储key/value(schema-less)数据,value中可以放:
    Python dict
    JSON object

    实际friendfeed存放的是zlib压缩的Python dict数据,当然这种绑定一种语言的做法具有争议性。

    表结构及Index设计模式

    feed数据基本上都存在entities表中,它的结构为

    mysql> desc entities;
    +----------+------------+------+-----+-------------------+----------------+
    | Field    | Type       | Null | Key | Default           | Extra          |
    +----------+------------+------+-----+-------------------+----------------+
    | added_id | int(11)    | NO   | PRI | NULL              | auto_increment |
    | id       | binary(16) | NO   | UNI |                   |                |
    | updated  | timestamp  | YES  | MUL | CURRENT_TIMESTAMP |                |
    | body     | mediumblob | YES  |     | NULL              |                |
    +----------+------------+------+-----+-------------------+----------------+

    假如里面存的数据如下

    {
    "id": "71f0c4d2291844cca2df6f486e96e37c",
    "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "title": "We just launched a new backend system for FriendFeed!",
    "link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
    }

    如果要对link字段进行索引,则用另外一个表来存储。

    mysql> desc index_link;
    +-----------+--------------+------+-----+---------+-------+
    | Field     | Type         | Null | Key | Default | Extra |
    +-----------+--------------+------+-----+---------+-------+
    | link      | varchar(255) | NO   | PRI |         |       |
    | entity_id | binary(16)   | NO   | PRI |         |       |
    +-----------+--------------+------+-----+---------+-------+
    2 rows in set (0.00 sec)

    优点是

    • 增加索引时候只需要 1. CREATE TABLE,2.更新程序
    • 删除索引时候只需要 1. 程序停止写索引表(实际就是一个普通表),2. DROP TABLE 索引表

    这种索引方式也是一种值得借鉴的设计模式,特别是key value类型的数据需要索引其中的内容时。