煮酒论坛

 找回密码
 申请新用户
搜索
热搜: 活动 交友 discuz
查看: 6454|回复: 6

Flickr 网站架构研究

[复制链接]
发表于 2010-3-18 00:10:15 | 显示全部楼层 |阅读模式
http://www.ccthere.com/article/2357486
(1)
Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被微软看中成为Vista壁纸御用摄影师。
  Flickr.com 是最初由位于温哥华的Ludicorp公司开发设计并于2004年2月正式发布的,由于大量应用了WEB 2.0技术,注重用户体验,使得其迅速获得了大量的用户,2007年11月,Flickr迎来了第20亿张照片,一年后,这个数字就达到了30亿,并且还在以加速度增长。
  2005年3月,雅虎公司以3千500万美元收购了Ludicorp公司和Flickr.com。虽然Flickr并不是最大的照片共享网站(Facebook以超过100亿张照片排名第一),但这笔收购仍然被认为是WEB 2.0浪潮中最精明的收购,因为仅仅一年后,Google就以16亿美元的高价收购了YouTube,而2007年10月,微软斥资2.4亿美元收购Facebook 1.6%股份,此举使Facebook估值高达150亿美元。估计Ludicorp公司的创始人Stewart Butterfield和Caterina Fake夫妇现在还在后悔吧。
  在2005年温哥华PHP协会的简报以及随后的一系列会议上,Flickr的架构师Cal Henderson公开了大部分Flickr所使用的后台技术,使得我们能有机会来分享和研究其在构建可扩展Web站点的经验。 本文大部分资料来自互联网和自己的一点点心得,欢迎大家参与讨论,要是能够起到抛砖引玉的作用,本人将不胜荣幸。

Flickr 网站架构综述

  在讨论Flickr 网站架构之前,让我们先来看一组统计数据(数据来源:April 2007 MySQL Conf and Expo和Flickr网站)

。每天多达40亿次的查询请求
。squid总计约有3500万张照片(硬盘+内存)
。squid内存中约有200万张照片
。总计有大约4亿7000万张照片,每张图片又生成不同尺寸大小的4-5份图片
。每秒38,000次Memcached请求 (Memcached总共存储了1200万对象)
。超过2 PB 存储,其中数据库12TB
。每天新增图片超过 40万(周日峰值超过200万,约1.5TB)
。超过8百50万注册用户
。超过1千万的唯一标签(tags)

  你如果觉得这些过时的数据都已经很惊人了,那么让我们来看看Cal Henderson在2008年9月的一次会议上公布的另一组数据,在短短的一秒钟内:

。响应4万个照片访问请求
。处理10万个缓存操作
。运行13万个数据库查询

  这张是Flickr的网站架构图,我们这里只作一些简要的描述,具体的分析请静待后续文章。

。Pair of ServerIron's
  - Load Balancer
。Squid Caches
  - 反向代理,用于缓存静态的HTML和照片
。Net App'
  - NetApp 公司的Filer, NAS存储设备,用于存储照片
。PHP App Servers
  - 运行REDHAT LINUX,Apache上的PHP应用,Flickr网站的主体是大约6万行PHP代码
  - 没有使用PHP session, 应用是stateless,便于扩展,并避免PHP Server故障所带来的Session失效。
  - 每个页面有大约27~35个查询(不知道为什么这么设计,个人觉得没有必要
  - 另有专门的Apache Web Farm 服务于静态文件(HTML和照片)的访问
。Storage Manager
  - 运行私有的,适用于海量文件存储的Flickr File System
。Dual Tree Central Database
  - MySQL 数据库,存放用户表,记录的信息是用户主键以及此用户对以的数据库Shard区,从中心用户表中查出用户数据所在位置,然后直接从目标Shard中取出数据。
  - “Dual Tree"架构是”Master-Master"和“Master-Slave"的有效结合,双Master 避免了“单点故障”,Master-Slave又提高了读取速度,因为用户表的操作90%以上是读。
。Master-master shards
  - MySQL 数据库,存储实际的用户数据和照片的元数据(Meta Data),每个Shard 大约40万个用户,120GB 数据。每个用户的所有数据存放在同一个shard中。
  - Shard中的每一个server的负载只是其可最大负载的50%,这样在需要的时候可以Online停掉一半的server进行升级或维护而不影响系统性能。
  - 为了避免跨Shard查询所带来的性能影响,一些数据有在不同的Shard有两份拷贝,比如用户对照片的评论,通过事务来保证其同步。
。Memcached Cluster
  - 中间层缓存服务器,用于缓存数据库的SQL查询结果等。
。Big Search Engine  
  - 复制部分Shard数据(Owner’s single tag)到Search Engine Farm以响应实时的全文检索。
  - 其他全文检索请求利用Yahoo的搜索引擎处理(Flickr是Yahoo旗下的公司

服务器的硬件配置:
- Intel或AMD 64位CPU,16GB RAM
- 6-disk 15K RPM RAID-10.
- 2U boxes.

服务器数量:(数据来源:April 2008 MySQL Conference & Expo)
166 DB servers, 244 web servers(不知道是否包括 squid server?), 14 Memcached servers
 楼主| 发表于 2010-3-18 00:11:05 | 显示全部楼层
(2)数据库最初的扩展-Replication

  也许有人不相信,不过Flickr确实是从一台服务器起步的,即Apache/PHP和MySQL是运行在同一台服务器上的,很快MySQL服务器就独立了出来,成了双服务器架构。随着用户和访问量的快速增长,MySQL数据库开始承受越来越大的压力,成为应用瓶颈,导致网站应用响应速度变慢,MySQL的扩展问题就摆在了Flickr的技术团队面前。
  不幸的是,在当时,他们的选择并不多。一般来说,数据库的扩展无外是两条路,Scale-Up和Scale-Out,所谓Scale-Up,简单的说就是在同一台机器内增加CPU,内存等硬件来增加数据库系统的处理能力,一般不需要修改应用程序;而Scale-Out,就是我们通常所说的数据库集群方式,即通过增加运行数据库服务器的数量来提高系统整体的能力,而应用程序则一般需要进行相应的修改。在常见的商业数据库中,Oracle具有很强的Scale-Up的能力,很早就能够支持几十个甚至数百个CPU,运行大型关键业务应用;而微软的SQL SERVER,早期受Wintel架构所限,以Scale-Out著称,但自从几年前突破了Wintel体系架构8路CPU的的限制,Scale-Up的能力一路突飞猛进,最近更是发布了SQL 2008在Windows 2008 R2版运行256个CPU核心(core)的测试结果,开始挑战Oracle的高端市场。而MySQL,直到今年4月,在最终采纳了GOOGLE公司贡献的SMP性能增强的代码后,发布了MySQL5.4后,才开始支持16路CPU的X86系统和64路CPU的CMT系统(基于Sun UltraSPARC 的系统)。
  从另一方面来说,Scale-Up受软硬件体系的限制,不可能无限增加CPU和内存,相反Scale-Out却是可以"几乎"无限的扩展,以Google为例,2006年Google一共有超过45万台服务器(谁能告诉我现在他们有多少?!);而且大型SMP服务器的价格远远超过普通的双路服务器,对于很多刚刚起步或是业务增长很难预测的网站来说,不可能也没必要一次性投资购买大型的硬件设备,因而虽然Scale-Out会随着服务器数量的增多而带来管理,部署和维护的成本急剧上升,但确是大多数大型网站当然也包括Flickr的唯一选择。
 经过统计,Flickr的技术人员发现,查询即SELECT语句的数量要远远大于添加,更新和
删除的数量,比例达到了大约13:1甚至更多,所以他们采用了“Master-Slave”的复制模式,即所有的“写”操作都在发生在“Master",然后”异步“复制到一台或多台“Slave"上,而所有的”读“操作都转到”Slave"上运行,这样随着“读”交易量的增加,只需增加Slave服务器就可以了。   

   让我们来看一下应用系统应该如何修改来适应这样的架构,除了”读/写“分离外,对于”读“操作最基本的要求是:1)应用程序能够在多个”Slave“上进行负载均分;2)当一个或多个”slave"出现故障时,应用程序能自动尝试下一个“slave”,如果全部“Slave"失效,则返回错误。Flickr曾经考虑过的方案是在Web应用和”Slave“群之间加入一个硬件或软件的”Load Balancer“,如下图

这样的好处是应用所需的改动最小,因为对于应用来说,所有的读操作都是通过一个虚拟的Slave来进行,添加和删除“Slave"服务器对应用透明,Load Balancer 实现对各个Slave服务器状态的监控并将出现故障的Slave从可用节点列表里删除,并可以实现一些复杂的负载分担策略,比如新买的服务器处理能力要高过Slave群中其他的老机器,那么我们可以给这个机器多分配一些负载以最有效的利用资源。一个简单的利用Apache proxy_balancer_module的例子如下:
。。。。。。。。。。。。。。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so
。。。。。。。。。。。。。。。。。。。。
<Proxy balancer://mycluster>
BalancerMember "http://slave1:8008/App"   loadfactor=4
BalancerMember "http://slave2:8008/App"   loadfactor=3
BalancerMember "http://slave3:8008/App"   loadfactor=3
....................
///slave load ratio 4:3:3.

   最终,Flickr采用了一种非常“轻量”但有效的“简易”PHP实现,基本的代码只有10几行,

function db_connect($hosts, $user, $pass){
        shuffle($hosts);    //shuffle()是PHP函数,作用是将数组中每个元素的顺序随机打乱。
        foreach($hosts as $host){
                debug("Trying to connect to $host...");
                $dbh = @mysql_connect($host, $user, $pass, 1);
                if ($dbh){
                        debug("Connected to $host!");
                        return $dbh;
                }
                debug("Failed to connect to $host!");
    }
    debug("Failed to connect to all hosts in list - giving up!");
        return 0;
}
   在上述代码中,如果需要对特定的Slave赋予更高的负载,只要在$hosts中多出现一次或多次就可以了。这段代码只要稍稍改进,就可以实现更复杂的功能,如当connect失败时自动将host从hosts列表中去除等。
   “Master”-"Slave"模式的缺点是它并没有对于“写'操作提供扩展能力,而且存在单点故障,即一旦Master故障,整个网站将丧失“更新”的能力。解决的办法采用“Master"-"Master"模式,即两台服务器互为”Master“-"Slave",这样不仅”读/写“能力扩展了一倍,而且有效避免了”单点故障“,结合已有的“Master"-"Slave",整个数据库的架构就变成了下面的”双树“结构,

   “双树”架构并没有支撑太久的时间,大概6个月后,随着用户的激增,系统再一次达到了极限,不仅”写”操作成为了瓶颈,而且“异步复制"也由于”Slave“服务器过于繁忙而出现了严重的滞后而造成读数据的不一致。那么,能不能在现有架构加以解决,比如说增加新的”Master“服务器和考虑采用”同步复制“呢?答案是否定的,在Master超过两台的设置中,只能采用”闭环链“的方式进行复制,在大数据量的生产环境中,很容易造成在任意时刻没有一个Master或Slave节点是具有全部最新数据的(有点类似于”人一次也不能踏进同一条河“?),这样很难保障数据的一致性,而且一旦其中一个Master出现故障,将中断整个复制链;而对于”同步复制“,当然这是消除”复制滞后“的最好办法,不过在当时MySQL的同步复制还远没有成熟到可以运用在投产环境中。
   Flickr网站的架构,需要一次大的变化来解决长期持续扩展的问题。
 楼主| 发表于 2010-3-18 00:12:19 | 显示全部楼层
(3)Shard - 大型网站数据库扩展的终极武器?
 2005年7月,另一位大牛(MySQL 2005、2006年度 "Application of the Year Award"获得者)Dathan Pattishall加入了Flickr团队。一个星期之内,Dathan解决了Flickr数据库40%的问题,更重要的是,他为Flickr引进了Shard架构,从而使Flickr网站具备了真正“线性”Scale-Out的增长能力,并一直沿用至今,取得了巨大的成功。
 Shard主要是为了解决传统数据库Master/Slave模式下单一Master数据库的“写”瓶颈而出现的,简单的说Shard就是将一个大表分割成多个小表,每个小表存储在不同机器的数据库上,从而将负载分散到多个机器并行处理而极大的提高整个系统的“写”扩展能力。相比传统方式,由于每个数据库都相对较小,不仅读写操作更快,甚至可以将整个小数据库缓存到内存中,而且每个小数据库的备份,恢复也变得相对容易,同时由于分散了风险,单个小数据库的故障不会影响其他的数据库,使整个系统的可靠性也得到了显著的提高。
 对于大多数网站来说,以用户为单位进行Shard分割是最合适不过的,常见的分割方法有按地域(比如邮编),按Key值(比如Hash用户ID),这些方法可以简单的通过应用配置文件或算法来实现,一般不需要另外的数据库,缺点是一旦业务增加,需要再次分割Shard时要修改现有的应用算法和重新计算所有的Shard KEY值;而最为灵活的做法是以“目录”服务为基础的分割,即在Shard之前加一个中央数据库(Global Lookup Cluster),应用要先根据用户主键值查询中央数据库,获得用户数据所在的Shard,随后的操作再转向Shard所在数据库,例如下图:


 而应用的主要修改在于要添加一个Lookup访问层,例如将以下的代码
string connectionString = @"Driver={MySQL};SERVER=dbserver;DATABASE=CustomerDB;";      
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open();
 变为
string connectionString = GetDatabaseFor(customerId);         
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open();
 GetDatabaseFor()函数完成根据用户ID获取Shard connectionString的作用。

  对应我们前面所提到过的Flickr架构

 Dual Tree Central Database就是中央数据库,存放用户表,记录的信息是用户主键以及此用户对以的数据库Shard区;而Master-Master Shards就是一个个的Shard用户数据库,存储实际的用户数据和照片的元数据(Meta Data)。
 Flickr Shard的设计我们在Flickr 网站架构研究(1)中已经总结过了,在此不再赘述。我们在此谈一下Shard架构的主要问题和Flickr的解决办法:1)Shard只适用于不需要join操作的表,因为跨Shard join操作的开销太大,解决的办法是将一个用户的所有数据全部存放在同一个Shard里,对于一些传统方式下需要跨Shard查询的数据,只能采取冗余的方法,比如Shard1的用户A对Shard2的用户B的照片进行了评论,那么这条评论将同时存放在Shard1和Shard2中。这样就存在一个数据一致性的问题,常规的做法是用数据库事务(Transaction)、”两阶段提交“(2 phase commit)来解决,但做过两阶段提交(2PC)应用的都知道,2PC的效率相对较差,而且实际上也不能100%保证数据的完整性和一致性;另外,一旦由于其中一个Shard故障而提交失败回滚,用户只能放弃或再试一遍,用户体验较差。Flickr对于数据一致性的解决方案是Queue(Flickr用PHP开发了一个强大的Queue系统,将所有可以异步的任务都用Queue来实现,每天处理高达1千万以上的任务。),事实上当用户A对用户B的照片进行评论时,他并不关心这条评论什么时候出现在用户B的界面上,即将这条评论添加到用户B的交易是可以异步的,允许一定的迟延,通过Queue处理,既保证了数据的一致性,又缩短了用户端的相应时间,提高了系统性能。2)Shard的另一个主要问题Rebalancing,既当现有Shard的负载达到一定的阀值,如何将现有数据再次分割,Flickr目前的方式依然是手工的,既人工来确定哪些用户需要迁移,然后运行一个后台程序进行数据迁移,迁移的过程用户账户将被锁住。(据说Google做到了完全自动的Rebalancing,本着”萨大“坑里不再挖坑的原则,如果有机会的话,留到下一个系列再研究吧)
Memcached的应用和争论
 大家应该已经注意到,Flickr为中央数据库配置了Memcached作为数据库缓存,接下来的问题是,为什么用Memcached?为什么Shard不需要Memcached?Memcached和Master,Slave的关系怎样?笔者将试图回答这些问题供大家参考,网上的相关争论很多,有些问题尚未有定论。
 Memecached是一个高性能的,分布式的,开源的内存对象缓存系统,顾名思义,它的主要目的是将经常读取的对象放入内存以提高整个系统,尤其是数据库的扩展能力。Memcached的主要结构是两个Hash Table,Server端的HashTable以key-value pair的方式存放对象值,而Client端的HashTable的则决定某一对象存放在哪一个Memcached Server.举个例子说,后台有3个Memecached Server,A、B、C,Client1需要将一个对象名为”userid123456“,值为“鲁丁"的存入,经过Client1的Hash计算,"userid123456"的值应该放入Memcached ServerB, 而这之后,Client2需要读取"userid123456"的值,经过同样的Hash计算,得出"userid123456"的值如果存在的话应该在Memcached ServerB,并从中取出。最妙的是Server之间彼此是完全独立的,完全不知道对方的存在,没有一个类似与Master或Admin Server的存在,增加和减少Server只需在Client端"注册"并重新Hash就可以了。
 Memcached作为数据库缓存的作用主要在于减轻甚至消除高负载数据库情况下频繁读取所带来的Disk I/O瓶颈,相对于数据库自身的缓存来说,具有以下优点:1)Memecached的缓存是分布式的,而数据库的缓存只限于本机;2)Memcached缓存的是对象,可以是经过复杂运算和查询的最终结果,并且不限于数据,可以是任何小于1MB的对象,比如html文件等;而数据库缓存是以"row"为单位的,一旦"row"中的任何数据更新,整个“row"将进行可能是对应用来说不必要的更新;3)Memcached的存取是轻量的,而数据库的则相对较重,在低负载的情况下,一对一的比较,Memcached的性能未必能超过数据库,而在高负载的情况下则优势明显。
 Memcached并不适用于更新频繁的数据,因为频繁更新的数据导致大量的Memcached更新和较低的缓冲命中率,这可能也是为什么Shard没有集成它的原因;Memcached更多的是扩展了数据库的”读“操作,这一点上它和Slave的作用有重叠,以至于有人争论说应该让"Relication"回到它最初的目的”Online Backup"数据库上,而通过Memcached来提供数据库的“读”扩展。(当然也有人说,考虑到Memcached的对应用带来的复杂性,还是慎用。)
 然而,在体系架构中增加Memecached并不是没有代价的,现有的应用要做适当的修改来同步Memcached和数据库中的数据,同时Memcached不提供任何冗余和“failover”功能,这些复杂的控制都需要应用来实现。基本的应用逻辑如下:
  对于读操作,
   $data = memcached_fetch( $id );
   return $data if $data
   $data = db_fetch( $id );
   memcached_store( $id, $data );
   return $data;
  对于写操作
  db_store( $id, $data );
  memcached_store( $id, $data );
  我们看到在每一次数据更新都需要更新Memcached,而且数据库或Memcached任何一点写错误应用就可能取得“过期”的数据而得到错误的结果,如何保证数据库和Memcached的同步呢?

复制滞后和同步问题的解决
 我们知道复制滞后的主要原因是数据库负载过大而造成异步复制的延迟,Shard架构有效的分散了系统负载,从而大大减轻了这一现象,但是并不能从根本上消除,解决这一问题还是要靠良好的应用设计。
 当用户访问并更新Shard数据时,Flickr采用了将用户“粘”到某一机器的做法,
    $id = intval(substr($user_id, -10));
    $id % $count_of_hosts_in_shard
 即同一用户每次登录的所有操作其实都是在Shard中的一个Master上运行的,这样即使复制到Slave,也就是另一台Master的时候有延时,也不会对用户有影响,除非是用户刚刚更新,尚未复制而这台Master就出现故障了,不过这种几率应该很小吧。
 对于Central Database的复制滞后和同步问题,Flickr采用了一种复杂的“Write Through Cache"的机制来处理:

  "Write Through Cache"就是将所有的数据库”写“操作都先写入”Cache",然后由Cache统一去更新数据库的各个Node,“Write Through Cache"维护每一个Node的更新状态,当有读请求时,即将请求转向状态为”已同步“的Node,这样即避免了复制滞后和Memcached的同步问题,但缺点是其实现极为复杂,“Write Throug Cache"层的代码需要考虑和实现所有”journal","Transaction“,“failover”,和“recovery"这些数据库已经实现的功能,另外还要考虑自身的"failover"问题。我没有找到有关具体实现的说明,只能猜测这一部分的处理可能也是直接利用或是实现了类似于Flickr的Queue系统吧。

 Flickr 网站架构研究的数据库部分到此结束,后面的重点将转向海量文件管理部分。
 楼主| 发表于 2010-3-18 00:13:02 | 显示全部楼层
(4)海量文件系统设计的考虑因素
在具体讨论Flickr的文件存储系统设计之前,我们这里先来简要的介绍一下海量文件系统设计时需要考虑的一些基本因素:

1。数据库还是文件系统?
 文件存在哪儿?这个可能是大家问的第一个也可能是最多的问题了,一般来说,业界公认的方法是数据库存放元数据和文件路径(可以是绝对路径,也可以是相对路径+卷路径),而实际文件则存放在文件系统,大多数大型图片网站如Flickr和企业内容管理(ECM)软件厂商都采用这种方法(或者同时支持数据库和文件系统两种方式),原因在于
 1)数据库系统在增大到一定规模后,比如上TB级后管理维护和效率都是一个很大的挑战(升级硬件是一个办法,如果有钱),例如Flickr现今已有超过6PB的照片文件,全部以BLOB方式存入数据库几乎不可能。当然也有例外,比如微软的Sharepoint 2007就是全部文件都存入SQL SERVER数据库的,微软的建议是一个Sharepoint library的文件数不要超过5百万,如果超过的话则可以采用scale-out方式。记得几年前在MSDN上看过一篇文章,介绍微软采用了几百台sharepoint 2001 server来搭建自己的一个内部知识库网站。采用数据库的方式,虽然有些限制,但也不是绝对不可以。
 2)以BLOB方式存储文件,当文件大小超过一定阀值后数据库的读写响应将急剧下降。下面的图摘自某著名ECM厂商的研究报告,这张是比较不同大小的文件存入数据库和文件系统的响应时间的(蓝线是数据库,红线是文件系统)

这张是比较从数据库和文件系统读取不同大小的文件的响应时间的

从中可以看出,这个阀值是大约500KB-1MB左右,小于这个阀值,数据库的读写效率要高于文件系统,而大于这个阀值,文件系统的优势就体现出来了。不同的测试条件和软硬件下这个阀值会有所不同,但趋势应该是一致的。
 采用文件系统方式的一个主要缺点是难以保证元数据和文件的一致性(在数据库方式下可以通过事务的方式来保证这一点),所以需要定期运行特定的程序进行一致性检查。

2。通用文件系统的问题。
 早期的通用文件系统的设计初始并没有考虑到海量文件的存取,主要的表现为当一个目录下的文件数超过一定时,访问这个目录下文件的时间将大大增加,例如在NT4.0时代,这个值是大约5000,后来的Windows 2000/2003和Unix/Linux要好的多,不过这个限制依然存在。早期的NTFS或基于BSD Fast Filesystem的Unix/Linux文件系统,对于目录/inode的设计是采用采用简单的顺序查找的方式来定位目录下的文件,虽然稍后一些的文件系统设计增加了cache来提高效率,不过还是难以应付一个目录下有大量文件的情况;较为现代的JFS, Reiserfs, XFS, HFS和较新的NTFS版本对于目录结构采用了B-Tree的设计,因而大大提高了效率,但存在的问题是一旦较高层次的父节点数据损害,则可能会丢失所有的子节点。Linux 2.6的ext3和ext4文件系统有鉴于此,采用了一种相对简单但较高效和安全的HTree(Hash Tree)的方法,即可以利用hash table来提高访问效率,又保留了原来的顺序查找的结构,这样一旦hash table的数据丢失也依然可以通过顺序查找的结构来访问和恢复。
 尽管效率有所提高,还是不建议在一个目录和一个文件系统下放置太多的文件。对于NTFS下的目录,个人理解这个临界点大约是30万,微软建议,当一个NTFS目录下文件数超过30万时,关掉短文件名(即DOS兼容模式的8.3)的生成(可通过注册表或fsutil behavior set disable8dot3 1命令)。对于Linux/Unix的目录以及单个文件系统,没有看到过权威的数据。(个人建议不要超过2级目录,总文件数不超过1亿吧。)一个现实的例子就是Youtube.com,Youtube最初采用几台Linux服务器存放所有视频的截屏(一个视频大约4-5个截屏),后来由于目录下文件太多以至大大影响了性能,在被Google收购之后,移植到了Google的BigTable,才最终解决了问题。
 对于类似的照片网站,另一个建议是关掉文件系统的last access time的读写。缺省情况下,每一次文件访问,文件系统都会更新文件的last access time,这样会造成大量的写操作。对于这类网站,其实并不关心每一个文件的最后访问时间。对于Unix/Linux,可以通过在mount时选用noatime参数来关掉last access time;对于NTFS,可通过注册表或fsutil behavior set disablelastaccess 1的命令。但要注意的是,一些备份软件/策略可能根据这个时间来进行备份,在关掉这个选项之前需要仔细考虑替代方案。
 除了通用文件系统之外,也有一些基于通用文件系统加以改造或是干脆重起炉灶重写的分布式文件系统比如Google File system(GFS),IBM 的General Parallel File System (GPFS),和著名的memcached师出同门的MogileFS,以及Facebook新近研发成功的Haystack系统等,这些系统在一定程度上克服了通用文件系统的限制,但在成熟度,开放性和技术支持上还有待观察。
3。存储设备的选择。
 存储设备的选择是个见仁见智的事情,大厂商的存储设备通常有一些独特的功能,比如snapshot,replication,WORM-Like Disk(类似CD-R以满足法律法规的要求)等,缺点当然就是价格昂贵;而通用硬件价格便宜,采购灵活,缺点是应用软件需要实现更多的功能,而且维护成本较高,可能需要几百台服务器来达到同样的存储容量。对比两大网站:Flickr最初采用了通用的硬件而后似乎是基于维护的原因而转向NetApp;而Facebook在砸出大把大把的银子购进NetApp后最近由于性能和TCO的原因开始考虑通用硬件。重要的在于软件和应用系统的设计要不依赖于特定厂商的产品和功能,这样在需要的时候可以有更多的选择,虽然数据移植是一件很痛苦的事。
 楼主| 发表于 2010-3-18 00:13:52 | 显示全部楼层
(5)"Flickr File System"探秘(上)
  Flickr在美国东西海岸各建了4个数据中心用于提供静态内容(主要是照片)的后台访问服务,组成了4个相互镜像和冗余的“Photo Farm Pair",各自服务于不同地域的客户。同Flickr"Master-Maste Pair"数据库一样,每个数据中心的设计容量是能够处理整个"air"100%的访问,这样在"air"中的一个数据中心因为停电,地震等灾难或必要维护而停止服务时,另一个数据中心仍然能够支持全部的访问而不降低性能。
  每个"hoto Farm"的架构示意图如下:

  这个就是所谓的"Flickr File System",主要由三部分组成:1)Perbal(未在图中标出)和PHP APP Servers提供文件访问的权限控制,获取文件的内部URL并转向Squid/Apache服务器;2)Layer 7 Load Balancer和多台Squid/Apache Servers(Apache未在图中标出)提供文件“读”访问服务;3)几台Storage Managers用于文件“写”访问服务,PHP APP Server和Storage Manager之间采用了Flickr自己开发的“文件传输轻量协议“。NetApp用于文件存储,Squid/Apache Server和Storage Manager Server和NetApp之间采用NFS连接。
   我们先来看访问控制这一部分。
   Flickr的访问权限控制是通过Perlbal(Perlbal是一个Perl 编写的单线程的事件驱动服务器,可充当 reverse proxy,Web 服务器 和 HTTP Balancer,与大名鼎鼎的memcache同出于www.danga.com)的"reproxy"功能来实现的,如下图:

   例如,当Perlbal收到以下URL访问请求时,”http://www.flickr.com/photos/41174610@N05/3999568691/“,首先将访问请求转向后台的PHP App Server 进行权限检查,如果当前浏览器用户具有访问权限,则向Perlbal返回HTTP Response 200,并设置"X-REPROXY-URL" Header为实际的静态URL地址,既”“,Perlbal收到X-REPROXY-URL后,将请求转向Asset Server既Squid/Apache Server进行处理,整个过程对用户透明。
   以上例子中我们看到Flickr采用了两种不同方式的URL格式,第一种方式(例如”http://www.flickr.com/photos/41174610@N05/3999568691/“)我们姑且称之为“动态URL",当最终用户采用这种方式访问时,后台系统将进行权限检查;而如果用户采用第二种方式(例如”“),我们姑且称之为“静态URL",系统将略过权限检查,直接指向Squid/Apache服务器。这就是Flickr所谓“Permission URLs”的概念,即将授权信息作为URL的一部分,从而省略授权过程而提高网站的访问性能和吞吐量。
   个人认为,Flickr是鼓励采用其Flickr API开发第三方接口的合作伙伴采用这种“静态URL"方式的。Flickr的API文档中提供了这种静态URL的信息格式

”http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg“
   or
”http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg“
   or
”http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)“

Size Suffixes

The letter suffixes are as follows:
s   small square 75x75
t   thumbnail, 100 on longest side
m   small, 240 on longest side
-   medium, 500 on longest side
b   large, 1024 on longest side (only exists for very large original images)
o   original image, either a jpg, gif or png, depending on source format
  
  “静态URL"也可以通过“动态URL”来获得,而且是“终生授权”,即使将来作者修改了照片的权限,采用静态URL依然可以继续访问,除非作者删除照片,或者对照片进行修改,一旦照片经过修改,则系统自动生成一个新的“静态URL",原有的授权则因URL不存在而自动失效。
   对于Flickr这类以"照片共享"为主要目的的社交类网站,安全并不是其首要的考虑因素,因而采用这种“折衷”方式的权限控制方式是可以接受的。(听说Amazon S3也是采用了这种方式,不禁为那些S3商业用户的数据安全悄悄捏了把汗

"Flickr File System"探秘(中)

   对于Flickr这样每秒需要响应和处理4万个以上的照片文件读取请求的大型网站系统,Web Cache和reverse proxy系统的重要性,无论怎么强调都不过分。常见的Web Cache系统有,Squid,Varnish,Apache mod_cache等。
   Flickr采用Squid作为缓存代理服务器,这里有一张2005年Flickr展示的系统配置原理示意图:

   简单解释一下,Squid采用2GB 内存作为一级缓存(注这个值仅仅是Squid用于缓存的内存大小,Squid实际所用的内存要大于这个值);服务器内置的6块15K RPM 高速硬盘作为二级缓存;小于64KB的文件缓存于内存,大于64KB而小于512KB的缓存于高速硬盘;当缓存空间不够时采用LRU(Least recently used)算法将”最近最少使用的文件“清除。
   对于不在Squid缓存的文件,Squid则将请求转向Apache Web Server,Apache 根据ReWrite规则将URL转化成实际的NFS路径并取得相应的文件返回(图中只列出了一个Filer卷,实际上每个Squid服务器通过NFS mount了多个NetApp Filer的所有的卷)。值得一提的是,Apache在处理大于10MB以上的文件时,性能会有显著的下降,这也是为什么YouTube采用lighttpd的原因,所幸的是,一般照片的尺寸远远小于10MB,因而Flickr并不需要考虑额外维护另外一个Web Server,Apache已经足够满足需要。
   Web缓存代理服务器常见的两个问题是:
   1)如何避免”多次缓存“,即我们希望同一份文件只缓存一次,而不是在多个Squid中保留多份COPY,以最大限度的利用缓存空间。解决这个问题的办法是在Squid服务器集群之前放置Layer 7的Load Balancer。相对于传统的基于IP地址加PORT端口的Layer 4的Load Balancer,Layer 7的Load Balancer可以根据规则,对应用层的信息,如HTTP的Header,URL,Cookie Name等进行哈希或CRC计算,从而确保同一URL的请求总是指向后台集群中的同一台Squid服务器。
   根据需要和负载,网站可以选择基于硬件的Layer 7 Load Balancer,如Flickr;也可以选择开源的软件方案,如具有Layer 7负载均衡功能的HAProxy,Perlbal,或者是利用Apache的mod_rewrite功能通过script来简易实现,示例代码如下:

RewriteEngine  on
RewriteMap  balance    prg:/var/balance.pl
RewriteLock  /var/balance.lock
RewriteRule  ^/(.*)$    ${balance1}    [P,L]

   每一个到达的URL请求将被转向到/var/balance.pl脚本,由balance.pl脚本决定最终由哪台Cache服务器提供服务。

#!/usr/bin/perl -w
use strict;
use String::CRC;
$|++;
my @servers = qw(cache1 cache2 cache3);
while (<STDIN>) {
  my $crc = String::CRC::crc($_, 16);
  my $server = $servers[$crc % scalar @servers];
  print "http://$server/$_";
}
(以上脚本省略了fail-over和后台服务器状态检查部分)
  
   2)如何处理缓存”过期”问题,即当后台文件(如照片)修改后,如何保证用户能够访问到最新的版本而不是过期的版本。简单的更新Squid缓存并不总能解决问题,过期的文件依然有可能缓存在客户端的浏览器Cache,或是互联网上CDN,Proxy Server等处,而且还增加了后台应用的处理复杂程度和系统负荷。
   Flickr的方案是引入了“虚拟版本”的概念(对于Flickr这样的应用,没有必要采用真正的版本管理和维护多个版本),让我们来回顾一下Flickr "静态URL"的格式:“http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg”,
   每当用户修改照片时,数据库将重新生成一个新的的版本号码,即{secret}号码,从而产生一个新的静态URL并返回给客户,原有的缓存中的老文件则随着时间而“自然死亡”。(为了节省I/O,真正的物理文件名是没有这个{secret}号码的,在Apache的ReWrite处理中将这一部分去掉了。)这种方案,应该说至少可以保证通过“动态URL”访问的客户总是取得最新的文件版本,通过Flickr API访问的用户,则总是可以通过相应的应用逻辑判断来获取最新的“静态URL"。至于那些的Bookmark在浏览器端的旧的“静态URL",看起来也只能听之任之了,好象也没有更好的办法。

   Flickr的缓存系统是相当高效的,这里有一些2007年的数据:
  


  总体来看Squid的命中率在75%-80%之间,命中时的响应时间为15-50ms,应该是相当不错了。Flickr的运营团队得出的结论是在当前服务器配置下,每一台Squid服务器可以支持每秒900-1000个请求,同时响应时间控制在100ms以内。换句话说就是,随着业务的发展,当每秒新增加900个请求时,只要添加一台新的Squid服务器就可以了。
 楼主| 发表于 2010-3-18 00:14:23 | 显示全部楼层
(6)"Flickr File System"探秘(下)
   最后来说一说"Stoage Manager"。最早的Flickr系统是没有这一层的,Web/PHP Server在收到用户上载的照片后直接通过NFS协议将照片文件写入后台的NetApp存储。这样带来的问题是:
  1。NFS协议的“文件传输”效率成为瓶颈。高效网络文件传输的要点在于使用尽可能大的数据报文和尽量减少传输控制报文的开销,不幸的是,NFS在这两点上都差强人意。
  早期的NFS version 2.0使用UDP作为传输协议,但一次最大只能传输8KB.NFS version 3.0消除了这个8KB的限制,并引入了“异步写”的概念,理论上可以将多个小的“写”缓存在“cache”中再合并成一个大的写操作而提高效率,但在实际中往往要看协议的实现情况和操作系统内核的支持,比如至少在早期的LINUX实现中并不理想。
  2004年初,University of Massachusetts & IBM Almaden Research Center & University of California联合做了一项关于比较NFS和iSCSI应用于IP存储性能的研究,“A Performance Comparison of NFS and iSCSI for IP-Networked Storage”。在这份研究中,其中一项是在网络协议层比较两者“顺序和随机读写“的效率,结果见下图

(Sequential and Random reads and writes: completion times, number of messages and bytes transferred for reading and writing a 128MB file.)
  
   我们看到NFSv3的“读”效率和iSCSI基本相当,但在写效率上则有数量级上的差距。从网络协议报文的数量上看,iSCSI远远小于NFSv3,这是因为iSCSI能够以较大的数据包(大约128KB)执行异步写操作,而NFSv3的平均数据包只有4.7KB,其原因是NFSv3在Linux上的实现受到了Linux 内核2.4 cache最大“pending write"数量的限制因而不能充分利用异步写所带来的好处。
   在另一项应用层的测试中,研究人员使用PostMark测试了两者在创建,删除,读和附加(append)文件的效率,结果如下:

     Completion time (s)    Messages
 Files    NFSv3    iSCSI          NFS v3    iSCSI
1,000    146    12            371,963    101
5,000    201    35            451,415    276
25,000    516    208            639,128    66,965

(Completion times and message counts are reported for 100,000 operations on 1,000, 5,000 and 25,000 files)
   这项测试表明NFSv3协议在文件相关操作中有非常高的"meta-data related overhead",研究人员在分析上面的测试结果发现这一类message占全部message数量的65%。
  同样是在2004年,一些操作系统包括Linux等开始部分支持NFSv4。NFSv4除了采用了TCP之外,引入了一些新的特性,其中之一是“Compound RPCs",即可以一次发送多个RFC请求并取得结果,这样无疑可以大大减少"meta-data"相关的开销,显著的提高协议的总体效率。另一方面,随着Linux内核2.6的成熟并得到逐步采用,Linux在NFS的实现上也有了很大的提高,例如消除了"pending write" 数量的限制等。
  然而不幸的是,这些提高对于Flickr所需的“大文件传输”并没有帮助。根据“NFSv4 Test Project”的测试结果

  在异步模式下,对于小于512KB的文件,NFSv4(红线)大约是NFSv3 (绿线)效率的3倍,但对于大文件和同步模式,则几乎相当或略有提高。
  另一项2008年的测试表明,在File Creation操作上,NFSv4甚至要慢于NFSv3.

  
  究其原因,NFS是以支持"文件共享"为主要目的,侧重于多路并发访问条件下文件内的修改和同步控制。但是对于"文件传输"为主要目的的应用来说,这些额外的同步和锁机制就成为多余和不必要的开销。
  
  那么Flickr为什么不用FTP呢?FTP的主要问题是FTP需要两个端口,一个用于传输实际的文件,另一个用于传输控制命令。我们知道一台机器的端口(0-65535)是有限的,除去保留的系统端口外,真正能用的不过6万3千多个,而在一个高负载的,每秒成千上万访问的环境中,操作系统很容易出现“端口耗尽“的情况而拒绝新的连接。笔者曾经作过一个测试,在200个线程并发访问,每个线程平均5-10秒一笔交易下,30分钟左右,一台缺省配置的Windows 2003 Server就提示端口用尽。虽然可以通过调整Windows注册表或UNIX内核参数即减少tcp wait time的值来加快tcp端口的释放,但毕竟不如只用一个端口。
  2。Web/PHP Server直连NetApp的另一个坏处我想大家都应该想到了,那就是应用层和存储管理层绑定的太紧。在直连的情况下,每一台Server都要Mount全部的NFS卷,而任一个NFS卷的故障都可能会影响到所有的Web服务器,或者至少每一个服务器在写操作之前,都要检查目标卷是否可用,如果标记为故障的话要选择并写到另一个卷;而且还要监控所有卷的使用情况,在卷剩余空间小于阀值或者文件数多于阀值时报警并将此卷置为”只读“等。
  这些最终导致了专门负责存储管理和写操作的Storage Manager层的出现。Flickr为Web/PHP Server和Storage Manager之间专门设计了”轻量文件传输协议“。协议的主要编码规则如下:
  请求:
  |STORE|number files|{file1}|{file2}|{file3}|...
    每一个{file}块的格式:
    |filename|leading byte|file content byte length|file content|
      leading byte的值是”file content byte length"字符串的长度。例如1MB的文件,file content byte length 是1048576,则leading byte的值是"1048576"的长度7.
  响应:
  |OK|volume number of where the file store|   or
    |Failed|the reason of failed|
  协议本身是非常简单的,而且可以在一个报文里传输多个文件。而接受程序则根据文件长度直接截取文件内容,省去了诸如base64等编解码的开销。整个协议代码约600行PHP,包括opening,closing sockets, hot failover to redundant servers, and safe read and writes等.
 Web/PHP端的调用示例如下:

function store_file($storage_hosts, $filename){
        shuffle($storage_hosts);
        foreach($storage_hosts as $host){
                $result = store_file_2($host, $filename);
                if ($result){ return $result; }
        }
        return 0;
}

function store_file_2($host, $filename){
        ...
        if ($connection_failed){
                return 0;
        }
        ...
        if ($operation_failed){
                return 0;
         }
         return $result;
}
  代码中的storage_hosts既是Farm中的几台Storage Manager,和前面介绍过的db_query一样,上述代码也实现了简单而有效的load balance.
  Storage Manager接受到文件后,将文件放入Offline Queue队列,再由专门的图像处理服务器进行整理,压缩和格式转化(如果需要的话),生成不同尺寸大小的文件,并通过NFS写入NetApp存储。虽然这一步仍然要通过NFS,但此时的操作已是非实时,效率的影响已经不大了。
  应当指出,除非不得已,在绝大多数情况下,放弃成熟的公共协议而构建自己的私有协议并非上策。而笔者在文中列出的NFS和FTP的不足,也只是说明当前版本的NFS和FTP不适合于Flickr的特殊应用要求而已。 (NFS的优化不在本文讨论范围内,有兴趣的读者可以参考以下连接:NetApp的关于NFS Linux白皮书NFS-HOWTO
  那么有没有协议可以满足此类要求呢?个人认为值得关注的有:一。NFS4.1:NFS4.1版本将支持pNFS,即并行NFS,pNFS是近20年来NFS首次在性能上的重大升级,将可能成为高性能、共享文件存储的未来;另一个是SAN+Cluster FileSystem(如RedHat的Global File System和IBM的General Parallel File System)。传统上的SAN协议不直接支持多路并发写,而Cluster FS则弥补了这一缺陷为底层的共享块设备在文件系统级别提供并发的读写功能,因此更能发挥SAN存储架构的协议性能优势。有兴趣的话,大家可以看看这篇文章Red Hat GFS vs. NFS: Improving performance and scalability
 楼主| 发表于 2010-3-18 00:17:00 | 显示全部楼层
(7)系统监控和故障管理

    对于Flickr这样运行几百台甚至上千台服务器,以及网络和存储设备的大型网站来说,有效的监视整个系统各部分的运行状况,及时发现并处理故障和潜在的问题,是保障网站平稳和可持续运营的关键。

    以系统监控和网络管理为主要目的的软件通常称之为“网管软件”,商用软件中以IBM Tivoli, HP OpenView,和CA Unicenter TNG为主流,开源社区则以OpenNMS和Nagios为代表,Flickr采用的是Nagios。传统上定义的网络管理有五大功能:故障管理、配置管理、性能管理、安全管理、计费管理。近些年随着”全面管理“概念的引入,这类软件一般更多的自称为“企业基础架构管理软件”。
   
  SNMP即“Simple Network Management Protocol”是目前最常用的网络管理协议,目前几乎所有的网络设备和主流操作系统都实现了对SNMP的支持。它提供了一种从网络上的设备中收集网络管理信息的方法,也为设备向网络管理工作站报告问题和错误提供了一种方法。
    SNMP的最初版本1.0只定义了5种报文:Get,GetNext,getResponse,Set,Trap(这也是它称之为“简单”的原因,而事实上SNMP报文的编解码较为复杂。),示意图如下:

    SNMP采用UDP作为传输协议,Get,GetNext,用于NMS Manager(一般即网管主机)以轮询(Polling)的方式向SNMP Agent(一般运行在网络设备或服务器上)发出获取MIB(Management Information Base)节点信息的请求,GetResponse则是SNMP Agent的回应,Set用于写操作(实际中很少使用),这4个报文都通过161端口传输;当SNMP Agent端发现所监控的对象(MIB节点)出现故障,则主动向NMS Manager的162端口发出Trap告警。(详细的SNMP协议介绍不在本文内容之内,有兴趣的话可以另贴讨论)
    SNMP的协议特点使得"网管软件"能够及时地被动接收网络上的各种告警(Trap)信息,并根据告警信息的内容和级别触发响应的程序进行处理。例如Nagios提供了超过50个各类插件(plugin)用于监控常用的网络设备,服务器系统资源,后台服务进程和软件等,也可以根据需要自行开发相应的客户化插件用于监控特定的资源和事件。
   Flickr将告警分为以下几类:
    1)Up/Down,例如定期检查Apache Server和一些后台服务的状态,发现响应失败则自动重起服务进程,并通知管理员(根据级别采用事后邮件或实时短信、寻呼等)。对于一些服务或应用,有必要检查每一个子进程或服务的Up/Down的状态;而对另一些服务和应用,则可能需要“整体”来看,比如,几十台Web Server中的一台故障,一般不需要马上处理,而MySQL Master Server故障,则要马上通知DBA。
    2)资源不足,例如硬盘空间或网络带宽不足时邮件提醒管理员考虑购买新的硬盘或增加网络带宽。
    3)超过或低于阀值,例如CPU利用率,DISK I/O长时间过高等;或者某台Web Server处理的交易请求突然大幅度降低等。
  
    一开始困难往往在于不知道哪些事件需要告警,哪些则可以忽略。Flickr的经验是先定义尽可能多的事件,然后随着运营的稳定和经验数据的积累再逐步减少。另外,定义完善的告警级别和上报流程也有助于减少不必要的人工和尽快排除故障。
   
    网管软件的功能当然不只是“告警”,性能管理和“趋势”管理也越来越成为“企业基础架构管理”的重要组成部分。不过尽管网管软件可以通过扩展来提供一定程度上的此类功能,但SNMP协议本身的效率并不高,而且当网络达到了一定规模时,主动轮询的方式也限制了它的扩展性和采集大量的数据,一般常见的轮询间隔往往只能限定为10-15分钟甚至更长,这样的采样颗粒度有时很难满足精度和精确分析的要求,因而在大型网络环境下,人们通常更倾向于专门的“数据采集和趋势分析“软件,而这其中的佼佼者就是大名鼎鼎的Ganglia

性能管理与容量规划
   Ganglia诞生于著名的伯克利大学,最早专用于高性能计算(HPC)环境中的cluster和Grid的监控,经过精心设计的数据结构和算法,使得Ganglia能够以较小的开销同时监视数千台节点。
   Ganglia的数据流如下图:

   gmond进程,运行在各个节点负责收集数据,并通过multicast或unicast向运行于后台服务端的gmetd进程发送数据,gmetd汇聚数据并存入RRDTool所管理的Round  Robin Database.
   Round Robin Database是一种特殊的数据库,它对于存储于其中的数据根据时间的远近而保存不同的数据“分辨率”,比如对于较近时间段数据保存每分钟一个采样点,而对于一年以前的数据则只保留每天一个采样点,超过用户指定的期限如几年则自动将过期数据删除。RRD的理论出发点是人们一般较关心近期数据的精确性,而对于长期数据,则只关心大的趋势。RRD的特点使得它能够将数据库的规模限制在一定大小,而不是随着时间的持续而无限增长,这就解决了在相对长的时间内保存大量趋势数据的关键问题,因而在数据采集和趋势分析软件中得到了广泛的应用。
   虽然gmond本身可以采集操作系统一级的大量系统信息,如CPU,内存,硬盘和网络I/O等,但光有这些信息是远远不够的,对于一个网站来说,更重要的是了解这些系统信息所代表或对应的业务信息的含义,比如CPU利用率40%时,网站的平均响应时间是多少,网站同时能够支持的并发用户是多少。这些信息更多的来自于应用或基础构件(如MySQL,Web Server,Memcache,Squid等),而Ganglia提供的另一个程序gmetric,则可以通过cron定期调用script的方式来采集这些客户化的应用层数据并发送到gmetd。例如下面的代码分别用于获取disk util和memcache的命中率,

#!/bin/sh
/usr/bin/iostat -x 4 2 sda | grep -v ^$ | tail -4 > /tmp/
disk-io.tmp
UTIL=`grep sda /tmp/disk-io.tmp | awk '{print $14}'`
/usr/bin/gmetric -t uint16 -n disk-util -v$UTIL -u '%'

。。。。。。
/usr/bin/gmetric -t uint32 -n memcachced_hitratio -v$HITRATIO -u ’%’

   Apache,MySQL,Memcache,Squid等一般都可以某种方式提供一定程度上的实时统计信息,通过对这些数据的定期采样(如每分钟),可以很清楚的显示整个系统近乎实时的运营情况,找出各个数据相互之间的关系及其规律,并以此作为容量规划的依据。下面是Flickr的运营主管在一次会议上展示的Flickr的Ganglia系统。

   除了这些子系统本身提供的一些实时的统计数据外,Web Server的Access Log是另一个重要的统计和性能数据来源。一行典型的Access Log包括:用户的IP地址,访问时间,执行时间(time-taken),网页的URL,HTTP的响应码,数据包大小等。通过对这些Log的分析,我们可以得到很多有用的信息。一个最常见的困难是,如何按照时间顺序汇总几十台甚至数百台Web Server的Log。传统的手工COPY和处理不仅费时,而且COPY的时候占用网络资源较大。所幸的是Spread Toolkit帮我们解决了这个问题,采用Spread toolkit进行开发,我们可以将多台Web Server的Log以multicast的方式实时传输到一台或几台(用于Fail-over)集中的Log Server。特别的对于Apache,可以直接下载和使用一个基于Spread的现成模块mod_log_spread,采用mod_log_spread,Apache避免了本地Log的读写,据说可以提高运行效率20-30%。 对于Log的分析,网上虽然有很多软件,如Report Magic等,但这些软件并不懂你的应用,只能做一些基本的分析,Flickr对于这些Log进行了基于应用的分析和处理,并将结果也导入Ganglia。
     Access Log的分析是基于Web页面的,Flickr的技术团队认为这还不足够,因而进一步对于页面的每个task进行计数,比如总共有多少访问是memcache的,多少是直接调用数据库的。最常见的想法是利用数据库,每次访问则更新数据库的记录,这对于每秒访问量成千上万的繁忙网站来说基本上是不可行的,因为连接和更新数据库的开销太大。Flickr使用的是UDP,PHP的相关代码如下:

   $fp = fsockopen("udp://$server", $port, $errno, $errstr);
   if ($fp){
      fwrite($fp, "$value\n");
      fclose($fp);
   }
     $value即是所要计数的counter的名称。一个后台的Perl进程每收到一个counter,就在相应的counter的计数上加1,并定期存入RRD。
     计数之外,Flickr还对一些任务计时。一些后台程序定期执行这些特定任务并记录这些任务所花费的时间。

    由于环境的影响,每次计时可能都会不同,获得标准偏差比简单的取平均值更要有意义。例如下图:

    此项示例任务平均时间为250ms,75%的任务时间大约340ms,25%的任务在 190ms以内,最下面的浅绿线则是剩下的25%。
    Flickr公开了这段代码,有兴趣的可以在这里下载。

    最后简单讲一下Flickr如何确定系统的“边界”,即在可接受的性能条件下的最大负载。Flickr并不太看重在测试系统所作的benchmark或者是压力测试。在大多数情况下,测试环境很难完全100%的模拟真实的投产环境,即使软硬件环境完全一致,并完全复制投产系统的数据,因为大量用户的行为是无法完全模拟的。虽然有一些Log replay软件(如HttperfSiege)可以根据Web Access Log作一定程度的模拟,但一般来说Access Log不包括HTTP POST的请求参数,无法replay,而且replay软件也无法模拟由于地理位置和网络不同而造成的网络迟延等问题。Flickr采用的是“controlled production load testing",即直接在投产系统测试来取得特定配置下Server的最大负载。这其中的关键是Load Balancer,通过调节Load Balancer的负载分配,逐渐增加指定Server的访问量,直到达到临界值为止。

    推荐一本书《The Art of Capacity Planning》,这是Flickr运营主管写的一本关于大型网站容量规划的书,书中没有什么高深的数学公式,完全从实战的角度出发,值得一看。
您需要登录后才可以回帖 登录 | 申请新用户

本版积分规则

小黑屋|手机版|Archiver|守望轩 ( 湘ICP备17013730号-2 )|网站地图

GMT+8, 2018-6-22 22:56 , Processed in 0.045938 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表