爱因万江斯坦 用文字记录思想

What is scalability ?

什么是scalablity? scalablity译成中文是:可扩展性,伸缩性。

很多人都在谈论系统性能,系统可用性,但我觉得scalablity(可扩展性)并不是这些,虽然我们仍然需要关注速度,性能,高可用性,程序平台,网络等等,但这些并不是scalablity的全部。
scalablity,简单的说,就是关于您如何把事情做的更大。扩展(scaling)一个web应用就是指怎么做才能使您的系统允许更多的人去访问或使用您的应用。
在目前的业内实践中,有两种扩展WEB应用的方法:

  • 垂直扩展Vertical Scalability” – 通过增加资源来增强系统的处理能力。如:给现有的系统升级CPU,或是为RAID/SAN扩充更多的硬盘。
  • 横向扩展Horizontal Scalability” – 增加多个应用处理单元的机器。一般是集群,分布式文件系统,负载均衡的形式来横向扩展系统。

 

每一个组件,无论是CPU,服务器,存储系统或是负载均衡,都还是会有一些运营上的开销。当您尝试扩展您的系统时,您一定要清楚到底哪些升级或扩展是对您系统影响比例最大的,这要经过一些测量。我们把比这些测量的结果叫做”可扩展性因子“。比如:您每为系统增加一个CPU,但因为一些开销要耗掉这个CPU 5%的性能,实际上您的可扩展性因子是0.95,也就意味着您只能使用到这个CPU 95%的资源。

  • 如果可扩展性因子是一个常量(=1),我们就称之为 “线性扩展linear scalability“.
  • 但实际上有很多组件的扩展性并不太好,如上面介绍的增加CPU时的有非功能性开销的例子. 如果扩展性因子小于 1.0,我们就称之为 “子线性扩展性-sub-linear scalability“.
  • 有一种情况比较少见,就是通过增加组件而获得性能(扩展性因子>1)的大幅度提升(如把在多个磁盘上的I/O操作移到一个RAID上),这种情况我们称之为 “超线性扩展supra-linear scalability“.
  • 还有一些设计的不好的应用,根本不适合扩展,扩展的努力只会让系统更糟 ,我们就称之为”负可扩展性 negative scalability“.

如果您需要扩展系统,垂直扩展是最简单的方式(只要您银行存款足够,这事就成了)。一般在不做代码优化的情况下,您可以把您的系统程序迁移到一个超级的,也更贵的64位CPU的服务器上;也可以把存储系统换成EMC,日立或Netapp的产品。但是垂直扩展只会让您的花费越来越大。

横向扩展,则不一定要相当数量的昂贵服务器。因为它可以通过普通的机器和存储硬件以达到规模效应来解决问题,像早期的Yahoo!,Google都是这样。横向扩展不仅仅是便宜,它是把应用程序构建在多个服务器组成的一个大server的基础上,这样就会随之有两个常见的问题: “Split brain“(功能,数据分割) 和 “hardware failure“(硬件故障,毕竟机器太多了)。

无限的线性横向扩展是很难实现的,无限的垂直扩展则更不可能了(硬件的性能都有极限)。如果您正在建设中的系统事先无法确定用户的数量,那么用垂直扩展比较明智。但如果您正在建立一个给百万网友使用的WEB应用,那垂直扩展是一个错误,它实在太昂贵了。

可扩展性不仅仅是关于CPU(处理能力)的,一个成功的可扩展的WEB应用,它的所有层都应该能够方便的扩展,如:存储层(文件集群系统),数据层(数据分区,数据联合),应用层(memcached,scaleout,terracota,tomcat集群等),WEB层,负载均衡层,防火墙。举个例子,如果您对您未来的WEB应用网络负载没有考虑负载均衡,那么花再多的钱在WEB层的横向扩展上都是浪费,因为您的WEB访问的将会被瓶颈限制,而这时只有负载均衡能解决问题。

选择一个正确的可扩展性方案取决于您的系统应用规模和您的银行帐户情况。如果有人说他有一个能解决所有问题的方案,那人肯定是个骗子或是外行。如果您再参加某人的”可扩展性”讨论,您一定要先问问他:What is scalability ?

More to say:

  • Always try to identify the worst case scenarios.
  • Always build for the worst case scenarios.
  • Always test the known worst case scenarios.

And , always have a back-up plan!

更多资料:http://www.royans.net/arch/

 

译:High Performance Web Sites(3)-Chapter 1

《High Performance Web Sites》 :Chapter 1

法则 1: 尽可能减少HTTP请求数 Make Fewer HTTP Requests

在Chapter A中介绍的性能黄金法则,揭示了一个现象:只有10~20%的用户响应时间是花在请求html源文件上,剩下的80~90%的时间则是在请求页面中的各个组件(图片,script,样式表,flash等)。因此,我们的第一个提速的方法就是尽可能的减少这些组件的数量,我们的目的就是要减少HTTP请求的数量。

减少页面组件的建议,通常会让人在性能和产品设计上产生冲突。毕竟我们的页面现在所蕴含的元素越来越多,更多的小图片,不同的样式表,script等,这一章节,我会介绍一些技术方法来帮助我们平衡性能与产品设计上的冲突。这些将要出场的技术包括:image maps, CSS sprites,inline images以及尽量使用独立的js和样式表文件。使用这些技术方法在我们的案例中能减少50%的响应时间。

Image Maps
将一个页面中所要引用的图片整合成一个单一的图片文件,按顺序排好,再分切出里面的链接区域。这样对整个图片群的需求样式没有变,但减少了对图片的http请求数。
图1-1显示的是一个有五个图片组成的导航栏,每个图片对应一个独立的超链接。我们常规的做法,当然就是做五个图片,然后为每个图片做一个链接;但为了更高效,我们把五张独立的图片做成一张image map,这样从五个HTTP请求,就变成了一个HTTP请求,相应的响应时间也就会变的更快了。

您可以试一下下面的两个链接,自己体会一下Image maps所带来的速度上的不同。

No Image Map
http://stevesouders.com/hpws/imagemap-no.php
Image Map
http://stevesouders.com/hpws/imagemap.php

如果使用IE6在DSL(~900Kbps)的网络环境下,用image map的方法组成的导航栏要比用单独图片文件的所组成的导航栏要快56%(354ms 比 799ms).这是因为image map会减少四个HTTP请求。

Image Map最常用的实现方式是使用HTML的map标签,把大图片分成一个一个的小块,并设置其不同的链接。如下:

<img usemap=”#map1″ border=0 src=”http://7career.org/images/imagemap.gif”>
<map name=”map1″>
<area shape=”rect” coords=”0,0,31,31″ href=”http://7career.org/home.html” title=”Home”>
<area shape=”rect” coords=”36,0,66,31″ href=”http://7career.org/gifts.html” title=”Gifts”>
<area shape=”rect” coords=”71,0,101,31″ href=”http://7career.org/cart.html” title=”Cart”>
<area shape=”rect” coords=”106,0,136,31″ href=”http://7career.org/settings.html” title=”Settings”>
<area shape=”rect” coords=”141,0,171,31″ href=”http://7career.org/help.html” title=”Help”>
</map>
但是它所带来的缺点就是你得手动确定图片的坐标,这会比较乏味和容易出错,并且它只适合把图片都组合在一个长方形的区域里。

CSS Sprites (您可以参考YouTube和iGoogle的首页,它们就是采用的这种优化方式)
与image maps类似,CSS Sprites也是把若干小图片合成一个大图片,但是CSS Sprites方式更灵活。为了实现CSS Sprites,是把各个小图片像组成一个棋盘一样地合成一个图片。如下图:

 

然后通过HTML中任何能支持背景图片的元素,如<span>或<div>,再通过CSS中的background-position属性来定位要显示的大图片中的某个小图片的位置。如下,就是要在上面给出的在图片中使用”My”这个图标来充当下面这个div的背景:
<div style=”background-image: url(’a_lot_of_sprites.gif’);
background-position: -260px -90px;
width: 26px; height: 24px;”>
</div>

我把前面我介绍image map的例子转成CSS Sprites的形式:把导航栏的五个链接都放到一个名为navbar的DIV中。每个链接都有一个SPAN元素,在#navbar的样式中为SPAN元素定义了背景图片spritebg.gif,但每个SPAN都有一个不同的class以指明其具体显示的背景图片的偏移位置,正是利用了CSS中的background-position属性。

<style>
#navbar span {
width:31px;
height:31px;
display:inline;
float:left;
background-image:url(/images/spritebg.gif);
}
.home { background-position:0 0; margin-right:4px; margin-left: 4px;}
.gifts { background-position:-32px 0; margin-right:4px;}
.cart { background-position:-64px 0; margin-right:4px;}
.settings { background-position:-96px 0; margin-right:4px;}
.help { background-position:-128px 0; margin-right:0px;}
</style>

<div id=”navbar” style=”background-color: #F4F5EB; border: 2px ridge #333;
width:180px; height: 32px; padding: 4px 0 4px 0;”>
<a href=”javascript:alert(’Home’)”><span></span></a>
<a href=”javascript:alert(’Gifts’)”><span></span></a>
<a href=”javascript:alert(’Cart’)”><span></span></a>
<a href=”javascript:alert(’Settings’)”><span></span></a>
<a href=”javascript:alert(’Help’)”><span></span></a>
</div>

这比image map的方式的例子要更快:342ms VS 354ms,但是他们之间的实现方式只有很小的不同。但重要的是,这可比用单独的五个图片的例子要快57%了。
CSS Sprites
http://stevesouders.com/hpws/sprites.php

我们看到,image map的方式要求所有的图片是连续的组合在一起的,而CSS Sprites没有这个限制。关于CSS Sprites的优缺点在Dave Shea的权威文章“CSS Sprites: Image Slicing’s Kiss of Death“中已经有详细的介绍,但我已经从CSS Sprites中体会到它的优点:减少了HTTP请求,比image maps灵活。另一个让我没想到的优点是它减少了下载的数据量。大多数人可能会认为一个拼合成的大图片肯定要比这此小图片的总量要大,因为它会有一些小图片的间隔区域。实际上正相反,大图片减少了图片中的color tables和格式信息等,而使得大图片比一堆小图片实际size要小一些。

如果你的网站中有很多背景图片,按钮图片,导航栏图片,那么你应该用CSS Sprites方式来优化你的页面了。(您可以参考YouTube和iGoogle的首页,它们就是采用的这种优化方式)

Inline images(注:IE暂不支持,您可以跳过这一部分;但说不定什么时候IE就支持了)
我们上面所做的都是为了减少HTTP请求,现在有一个更绝的方式,把所有的图片都以base64编码以源代码的形式写在HTML源码里:data:[<mediatype>][;base64],<data> 所有对图片的HTTP请求,都化在了对HTML源文件的第一次请求里。

我们在HTML中肯定都用过ftp:,file:,mailto:这样的标签,实际上像这样的标签还有很多,只是我们平常不太使用,像:smtp:,pop:,dns:,whois:,finger:,daytime:,news:,urn:等等.

data:URL标签是在1995年第一次提出,按RFC2397规范的描述:它是”allows inclusion of small data items as ‘immediate’ data.(允许在页面中包含一些小的即时数据)”。如一个内嵌的小红星的图片可以这样引用:(在firefox下可以出来)

<IMG ALT=”Red Star”
SRC=”data:image/gif;base64,R0lGODlhDAAMALMLAPN8ffBiYvWW
lvrKy/FvcPewsO9VVfajo+w6O/zl5estLv/8/AAAAAAAAAAAAAAAACH5BAEA
AAsALAAAAAAMAAwAAAQzcElZyryTEHyTUgknHd9xGV+qKsYirKkwDYiKDBia
tt2H1KBLQRFIJAIKywRgmhwAIlEEADs=”>
这样是挺方便吧,但它的不足之处也很明显:目前IE不支持;再就是FireFox1.5所能处理的line image有大小限制,不能超过100K;base64的编码会增加HTML的容量,总体下载量会增多。
Inline Images:http://stevesouders.com/hpws/inline-images.php

data:URL是内嵌在页面中的,所以它不会在页面之间缓存。所以您别用这种方法存储您公司的logo,因为它会增加您每个页面的容量。要解决这个问题,您可以把inline image写在CSS中,尽管date:URL不会被缓存,但CSS是可以被缓存的,Inline CSS Images:http://stevesouders.com/hpws/inline-css-images.php

下面就是接上面的例子,为每个SPAN加上inline image:

.home { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAAIxKA…);}
.gift { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAAABCp…);}
.cart { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAADlCr…);}
.settings { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAA…);}
.help { background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAALW1t…);}
PHP的file_get_content函数可以很轻松的实现inline image的那串base64的图片编码。我上面的例子就是用的这个函数:
.home { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents(”../images/home.gif”)) ?>);}
.gift { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents(”../images/gift.gif”)) ?>);}
Combined Scripts and Stylesheets | 15
.cart { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents(”../images/cart.gif”)) ?>);}
.settings { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents(”../images/settings.gif”)) ?>);}
.help { background-image: url(data:image/gif;base64,
<?php echo base64_encode(file_get_contents(”../images/help.gif”)) ?>);}
我们比较一下上面的几个例子,image maps与CSS Sprites的响应时间基本上相同,但比使用各自独立图片的方式要快50%以上。用inline image与CSS组合的方式虽然增加一个HTTP请求,但是它可以以样式表的形式被缓存。

Combined Scripts and Stylesheets
尽量使用独立的js和样式表文件

现在的前端开发少不了JavaScript和CSS,但我们还是建议:使用外部的js和css文件引用的方式,因为这要比直接写在页面中性能要更好一点(细节会在Chapter 8中讨论)。但是如果你滥用这条规则,把你的代码切割成了很多个小文件,那只会增加更多的HTTP请求而影响性能。
表1-1是10大网站首页中js和样式表的数量表,它们的样式表都不多,但js显然还有优化的余地。

下面的例子中,你会发现用独立的一个js比用多个js文件组成的页面载入要快38%.
Separate Scripts
http://stevesouders.com/hpws/combo-none.php
Combined Scripts
http://stevesouders.com/hpws/combo.php

有的人可能会习惯于按功能或模块来把js分成各种不同的文件,但以我的经验,如果一个网站的页面引用大量的js文件,那应该要分析一下这些分类是否真的便于管理。

结论
这一章节介绍了我们在Yahoo!用到的以减少HTTP请求数量的技术方法。也是对访问网站很重要的一条法则。它会提高用户第一次访问您网站时的体验速度。而更快的访问速度将会使用户更愿意常回头访问您杰作。

相关章节:


 

翻译:High Performance Web Sites(2)-Chapter B

《High Performance Web Sites》 :Chapter B

HTTP Overview

在开始介绍具体的优化法则之前,您务必要理解超文本传输协议:HyperText Transfer Protocol(HTTP)对性能的影响。HTTP是浏览器与服务器之间通过Internet通信的方式。HTTP的规范是由万维网联盟:W3C(World Wide Web Consortium)和互联网工程任务组:IETF(Internet Engineering Task Force)共同制定,而最终形成的RFC 2616规范。HTTP/1.1是目前流行通用的版本了,但还是有一些浏览器只支持HTTP/1.0。

HTTP就是描述请求request和响应response的c/s模型协议。一个浏览器发送HTTP请求(request)到一个指定的URL,这个URL所指定的服务器接到请求后返回一个HTTP响应(response)给浏览器,就这么简单。与很多互联网服务一样,这个协议用的是简单的明文格式。请求的种类有:GET,POST,HEAD,PUT,DELETE,OPTIONS和TRACE。下面我只重点介绍GET请求,这一最常用的。

一个GET请求把所请求的URL组装在头信息(headers)中,然后发送到这个指定的URL。服务器接收到请求后,经过响应处理,返回一个包含状态码,头信息(headers)和具体明文文本(一般是Html源码)所组装的数据包。如下图,就是请求yahoo_2.0.0-b2.js时的HTTP头信息(headers)的内容示例。

压缩
如果浏览器和服务器都支持,可以通过压缩来减少响应返回时的数据量。浏览器通过头信息中的Accept-Encoding元素来告诉服务器是否可以接受压缩数据,以及何种压缩方式。而服务器则通过头信息中的Content-Encoding元素来告诉浏览器,我目前支持哪种压缩方式。如下图

 

注意上图中返回的数据是已经被压缩的数据。Chapter 4会专门介绍HTTP压缩,以及在代理缓存下产生的一些问题,当然还会讨论一些其它的头信息(headers)。

有条件的GET请求  Conditional GET Request
如果浏览器已经缓存了一个组件,但是浏览器该怎么知道这个组件是否还有效呢,于是就产生了有条件的GET请求。如果缓存里的组件是有效的,浏览器就继续使用这个组件来加载页面,以减少响应的数据量从而加速了用户体验。

一般来说,判断缓存的组件是否有效是检查它的最后修时间。浏览器通过response响应数据中名为Last-Modified的头信息来获取这个时间值。再次有相同组件的GET请求时,浏览器会将名为If-Modified-Since头信息随GET请求一起发送到服务器,带着这一条件信息给远程的服务器,这就好像是浏览器对服务器呐喊:”我已经有一个版本的组件了,这里它的最后修改时间,这次我还可以继续使用它吗?”(只要网络不断,服务器总是会听见这呐喊声的)

 

如果这个组件在这个Last-Modified描述的时间之后没有被修改过,服务器就不会返回这个组件的具体数据了,而返回一个”304 Not Modified”状态码,这样可不就提高了响应速度了。在HTTP/1.1里,名为ETag和If-None-Match的头信息也可以起到类似的作用,这两个都会在Chapter 13中有讨论。

过期 Expires

有条件的GET请求和返回304状态码能让页面载入的更快,但它还是需要在客户端和服务器端进行一个往返的验证过程,HTTP请求仍然存在。而名为Expires的头信息会则会避免这种验证过程。

 

一旦浏览器发现响应返回的头信息中有Expires属性,它就会把这个属性所描述的日期与组件保存到浏览器缓存中,只要再次访问这个组件时的日期没有超过Expires属性里描述的日期,就会一直被浏览器使用而不会再次发送HTTP请求。Chapter 3会有关于Expires和Cache-Control头信息的更多介绍。

Keep-Alive

HTTP是建立在TCP协议的上层的实现,在早期的HTTP实现中,每个HTTP请求都会打开一个新的socket连接。实际上效率很低,因为大多数的HTTP请求都是针对同一个服务器发出的(想像一下我们浏览一个页面的情形,是不是该页面的组件大多数都是在这个页面所host的服务器上)。于是,长连接(persistent connections)的概念被引入了HTTP,以解决这种同一个服务器不停地打开关闭socket连接的情况。它能够让浏览器的多个请求只通过一个连接完成。浏览器和服务器就是通过相互传递Keep-Alive头信息来判断是否支持这一特性。

 

浏览器或服务器可以通过发送一个close头信息来关闭连接。从规范来说,keep-alive并不包含在HTTP/1.1中,但是绝大多数浏览器和服务器还是支持这一特性的。

Pipelining, 定义在HTTP/1.1中,它允许在一个socket连接中发送多个请求而不用等待响应返回。Pipelining比长连接(persistent connections)有着更好的性能。但是很不幸,IE不支持(IE7已经支持了),FireFox 2以后的版本虽然支持,但默认是关闭的。所以在Pipelining还没能普及以前,我们就还只能用Keep-Alive的方式来优化浏览器与服务器之间通过socket的通信。这个特性对于HTTPS很重要,因为HTTPS的连接都是长时间的连接.

更多

这个章节只是大致的介绍了HTTP,并把重点放在介绍影响性能的方面。如果要了解更多,您可以参考HTTP规范(http://www.w3.org/Protocols/rfc2616/rfc2616.html和由David Gourley 与 Brian Totty编写的《HTTP: The Definitive Guide》(O’Reilly; http://www.oreilly.com/catalog/httptdg)。上面所例举的资料将会您理解我们后台的章节有很大的帮助。

 

The New Yorker杂志封面欣赏续

今天是情人节,首选就从New Yorker的2000年情人节刊开始欣赏吧:

2000年情人节
没想到里面还有动物的亲吻,美国人很有幽默感

“Stairway to the Stars,” Philippe Petit-Roulet, October 1, 2007.
(
多读点书吧,书能让你走的更高)

“Autumn Tales,” Benoî;t van Innis, November 19, 2007.
(
秋天的传说,很有诗意,收获的季节)

(不见驴,只见一只被束缚的狂野大象-共和党,国会楼前草坪上的是什么人?看不太清….)

(纪念萨翁)

“Fill ‘Er Up”BY ANITA KUNZ / May 22, 2006

“Losing Face”BY FRANCOISE MOULY / May 29, 2006
丢脸了吧不知美国人的词汇里有没有丢脸这一说?)

“Back to Cool”BY BOB STAAKE / September 4, 2006
(
这孩子脑子里youtube,ipod,ps3,Aim,myspace…..)

翻译:High Performance Web Sites(1)-Chapter A

前端性能的重要性The Importance of Frontend Performance

我的大部分web生涯都是在扮演后台开发工程师的角色。所以,我很自然的把每个性能作业都作为是一个后台的优化练习罢了,像什么编译器参数,数据库索引,内存管理什么的。而且也有很多关于后台性能优化的书籍和资料让大家从中找到想要的东西。但实际上,对于绝大多数的web页面,只有不到10-20%的最终用户响应时间是花在了从服务器下载HTML源文件到用户浏览器上。如果你想达到不可思议般的用户快速浏览体验,那当然就应该去关注一下那其它的80-90%的用户体验时间花在哪儿呢?又该怎么去减少这个时间呢?后面的章节就会向您讲解与目前web优化所相关的一些基础知识,并提供14条优化性能的法则。

来关注一下web页面的性能

为了知道怎么去提高性能,我们应该先了解最终用户们都把等待的时间花在哪儿了?图A-1是在IE上访问Yahoo!的首页(http://www.yahoo.com)的HTTP响应时间图(John注:我们可以用HttpWatch(IE),Firebug(ff)工具来生成类似的图)。每一个横条都是一个HTTP请求。第一个横条,标识为html的那个,是HTML文档的初始请求(指请求HTML源文件)。然后浏览器解析这个HTML文档,并且开始下载这个页面的所有组件(John注:这里的组件是指页面所引用的图片,flash等非html文档的东西)。在我们的测试环境下,浏览器的缓存是空的,所以所有的组件都会被下载。在图上,我们可以看到,HTML源文件的下载只占整个响应时间的5%,最终用户花了近95%的时间用在等待下载组件上.还有一小部分是花在HTML,script和样式表(stylesheets)的解析上,在图中我们用每个下载时间条间的空白间隔来表示这些解析时间.

图A-2显示的是我们第二次用IE浏览这个网页时的情况.HTML文档的下载占到了整个响应时间的12%.大部分的组件都不用再下载了,因为它们已经被浏览器缓存了。

图A-1 在Internet Explorer上访问http://www.yahoo.com,空缓存

图A-2在Internet Explorer上访问http://www.yahoo.com,预先已有缓存

Ok,我们来看一下,有五个组件在第二次浏览页面时仍被下载了:

一个重定向(redirect)

这里的重定向虽然已在第一次访问被处理过了,但是浏览器还是再次请求了一次。因为它的HTTP响应状态码是302(表示”Found” 或 “move temporarily”),在响应header中没有缓存信息,所以浏览器不会去缓存它。关于这方面我们会在Chapter B中讨论HTTP时提到。

三个没有缓存的图片
接着的三个request请求是在上一次浏览时没有下载的三个图片。这些是经常变动的新闻图片和广告图片.

一个被缓存的图片
最后一个HTTP request请求是一个有条件的GET请求.这个图片是上次被缓存的,但是因为HTTP的响应头(response headers)的设置,浏览器要确认一下这个图片是不是最新的.有条件的GET请求我们也会在Chaptet B中讨论它.

 

时间都去哪儿了?

再回过来看看HTTP时间响应图,我们看到至少80%的最终用户响应时间是花在了下载页面里的组件上,如果我们来仔细分析一下这个图表的内容,我们会发现浏览器与HTTP之间的交互开始有点变的复杂了。除了上面我提到了HTTP的状态码和header会响应浏览器的缓存,另外,我们还能得到下面一些观察结果:

l 凡是被缓存的组件(图A-2)大多都不会有下载动作。相反,你看到的是紧跟在Html请求后的一个没有下载的空白时间间隔,这表示这段时间是在解析HTML,JavaScript和CSS,以及从缓存中获取相关的组件。

l 在同一时间里的HTTP请求数变化了。图A-2在同一个时间切面上最多只有三个HTTP请求,而图A-1却同时有多达六到七个HTTP请求。这是因为使用了多个不同的主机名(hostname),而无论它们是使用HTTP/1.0还是HTTP/1.1。Chapter 6将会解释这种多个同时下载的”平行下载(Parallel Downloads)”。(John注:是指同时下载)

l 这种平行下载不会发生在下载script上,因为在大多数情况下,浏览器会阻止其它下载script的HTTP请求。Chapter 6会解释为什么会这样,同时也会教你如何应用这个知识去优化你的页面载入时间。

要完完整整地指出时间都去哪儿了真是一个挑战。但至少还是很容易地能看到哪儿没花时间?它没有全花在下载HTML文档上,也没全花在后台的处理上。(几乎都花在了前端下载组件上)这就是为什么前端性能如此重要了。

 

性能黄金法则

像Yahoo!主页这样只有10%-20%的响应时间花在下载HTML源文件的现象并不是一个特例。据我分析,几乎所有与Yahoo!类似的网站都是这样(这可不包括Yahoo!Search,因为它的页面上组件的数量实在是太少了)。甚至可以进一步说,绝大多数的网站都是这样的情况。表A-1展示了由http://www.alexa.com所统计出的当今美国前10大网站。说明一下,下面提到的网站只有AOL不在前10,因为Craigslist.org本是排在前10,但是它的页面风格没有什么图片,script和样式表,不适合这里做为研究例子,所以我挑选了AOL。

图A-1 访问10大网站时的下载HTML源文件所占的时间比

所有的网站都只花了不到20%的总响应时间在获取HTML源文件上(John注:这一部分时间更多地取决于各网站的后端性能)。但只有Google在已有缓存的情况下是个例外。因为http://www.google.com只有6个组件,其中有5个被配置成被浏览器缓存。再次访问时,所有的组件被缓存,只有一个请求HTML源文件的HTTP请求和一个图片请求。

在做完所有的后台优化努力的情况下,你已经很难再有大的突破了。很显然,是时候关注前端的性能了。

首先,关注前端性能所能带来的整个性能提升到底有多少。如果我们能把后台的响应时间再优化,再砍掉一半响应时间,最终用户的响应时间也只是相应减少5% ~10%(John注:因为后台的性能只能是减少那个20%的响应HTML源文件请求)。相反的,如果我们把前端的性能提高一倍,我们可以把整个响应时间减少40~45%。

再说了,前端的改进一般不需要太多的时间和资源。而优化后台的性能时间基本上都得深入应用的构架和代码,找出优化的关健代码路径,增加或者改进硬件,分布化数据库等等,这些工作动辄就要以周和月为时间单位。在接下来的章节中介绍的前端性能优化法则都来自于最佳实践,比如修改web服务器的配置文件(Chapters3,4);将script和样式表放置到页面中相应的地方(Chapters 5 ,6);将图片,script,样式表组合到一起(Chapters 1)。这些实践只需要以小时和天为工作单位–远比提高后台性能需要的时间成本要低。

第三点,前端性能的这些实践都是经过实际考验的。在Yahoo!中有超过50个小组用这些实践减少了25%甚至更多的最终用户响应时间。在一些案例中,我们分析了运用这些要点的网站,一般都能达到25%或更多的性能提升。

在开始每个新的性能提升项目时,我都会画一张类似图A-1的图表来阐释一下性能的黄金法则:

只有10-20%的最终用户响应时间是花在了下载HTML源文件上。其它的80-90%是花在了下载页面中的所有组件上。

俺把这句浓缩一下,就是:80-90%的用户等待时间是来自于前端的页面加载。

这本书后面的部分将会提供详细的指导法则去减少这80-90%的最终用户响应时间。为了说明这些法则,我会陆续提到很多相关的技术:HTTP headers,JavaScript,CSS,Apache等等。

因为一些HTTP的基本概念对理解这本书非常有必要,所以我会在Chapter B中重点简介。

在这之后就是分章节的14条优化法则。我把这些法则按优先级列出。每条法则都有自己的适应范围。举个例子,法则2更适合商业网站而不适合个人站点。如果您应用所有适合您网站的的优化法则,您可以将页面速度提升25-50%从而增强用户体验。这本书的最后章节会告诉您如何以性能的眼光去分析美国的10大网站。

周星驰的下一个梦想是什么?

刚看了一个周星星同学的访谈节目。

回首周星星的历程,就像是在看一部少年经历到中年的奋斗史。他是一位在不断实现自己梦想的平凡人,只是他的工作太引人注目了。不要神化任何一个人,被神化的人,其实是在用平凡创造着不平凡。

从千禧年的《喜剧之王》,周星星已经在反思自己了,不再一味的追求无厘头和市俗的笑料,所以有人觉得这之后的周星星的作品”不如以前那么好笑了”。周星星这时总是试着触动大荧幕下黑暗坐位上的观众的心灵深处。其实拍《食神》时,周星星已经在偷偷转变了。

如果说《喜剧之王》是周星星的自我反思,《少林足球》的大成功则是周星星对自己少年时期的功夫爱好的一个圆梦之作,而《功夫》的出世则是周星星对偶像李小龙的一次完美的献礼,刚刚上映的《长江七号》,是周星星对自己幼时的梦想拥有一个玩具的情景再现,一些情节都来自于他自己的经历。

周星驰从一个跑龙套的,到万人所景仰的喜剧之王(导演)向我们证明了宇宙间一个永恒不变的定律:人因梦想而伟大。

不知周星驰的下一个梦想是什么,因为这将决定他的下一部电影是什么,我很期待。

The New Yorker杂志封面欣赏

“Bird’s Eye View,” Eric Drooker, January 15, 2007.

 

“While Rome Burns,” Anita Kunz, January 22, 2007. 

“Subway Connections,” David Heatley, February 12, 2007.

“Say Cheese,” Bruce McCall, March 5, 2007.

“Uphill Battle,” Barry Blitt, March 26, 2007.

“Style Sheet,” Ivan Brunetti, May 7, 2007.

“Big City Romance,” Lou Romano, June 25, 2007.

 ”Bright Idea,” Bob Staake, July 2, 2007.

Slide Show

“Summer Reading,” Jooste Swarte, August 20, 2007.

High Performance Web Sites

大概两个月前就在网上down下了这部书,并一口气读完。正值blog迁移,故现在才浓重推荐。此书是对于web前端的性能优化不可多得的一部好书,其可实践性极强,作者是原Yahoo!的首席性能官(Chief Performance Officer):Steve Souders,现已经跳至google。
在Yahoo,有一支性能小组,叫:Yahoo!’s Exceptional Performance team,翻译成中文就是是特别性能小组,他们主要是致力于以最佳的实践来提高Web性能。好像前段时间他们来过中国参加了CSDN的开发者大会,可惜俺未能参加,要是早知这一出演讲讨论,肯定要前往倾听了。Steve Souders就是这支小组的team leader。
书中,Steve Souders提出了一个performance golden rule,与其说是黄金法则,到不如说这是一个目前各网站所常见的一个表象:80-90 %的用户等待时间是来自于前端的网页加载
而《High Performance Web Sites》就是介绍如何去减少这80~90%的等待时间的一部优化手册。此书即适合前台开发者(html,js,css),也适合后台开发者。此书在yahoo的开发者网站有Html版本,具体可查阅http://developer.yahoo.com/performance/rules.html
如果您对后台的构架或性能更感兴趣,那在我前面介绍Flickr构架时,所提到的《Building Scalable Web Sites》就更值得一看了。
该书的中文版已经出来了,我就不翻译了,推荐大家买书学习。

Flickr构架

相关资料:
http://www.bytebot.net/blog/archives/2007/04/25/federation-at-flickr-a-tour-of-the-flickr-architecture
http://www.niallkennedy.com/blog/uploads/flickr_php.pdf
http://qcon.infoq.com/sanfrancisco/file?path=/QConSF2007/slides/public/IanFlint_YahooCommunitiesArchitecture.pdf
http://gocom.primeton.com/modules/newbb/item43643_43643.htm
还推荐一本fickr的资深构架师 Cal Henderson 的大作《Building Scalable Web Sites

截止到2007年11月13日,Flickr 用户上传的图片已经达到20亿,Flickr必须每天处理海量更新的图片,还有不断增加的用户,竟然还能不断发布新的功能,同时也保持着良好的应用性能。从Cal Henderson 的书中,我能感觉Flickr的成功是与他们坚实的技术和构架密不可分的。

flickr-4
平台
ok,先来看看他们的平台:
PHP
MySQL
Shards (这是个新名词,还是叫”碎片”吧,”指的是将应用的数据横向拆分,也就是说如果有几千万的用户信息,那么这些用户信息可以被分布在多个数据库服务器上“)
用Memcached做缓存layer
对Html和图片用squid反向代理
RedHat Linux
Java,Perl,用Pear处理xml和email
Apache
ImageMagick ,SystemImager ,Ganglia(分布式监控程序),Subcon(用于管理和发布集群机器的配置文件),Cvsup(用于在网络中分发和更新文件)

flickr-2

数据库  

dualtree中心数据库中存储像user这样的表,但只存储它们里的主键id和具体应该指向哪个分布数据库的指向信息。每个 Shard存储40W的用户数据,大多数数据会存储两次,比如,一个评论的发表,它是介于被评论对象和发起评论对象之间的数据,这种数据,flickr会存储两次,通过事务来同步执行:打开第一个事务(transaction),写数据,打开第二个事务,写数据;如果两个事务都正常执行了,则第一个事务提交(commit),如果第一个事务提交成功,则第二个事务提交。这个流程,如果遇到服务器down机,其实还是有可能出现只提交了一次的情况。

Flickr的搜索功能有两个结果返回来源,一个是Flickr自身的Shards机器,另一个是Yahoo的web搜索,用户自己的tag搜索走的是Flickr自己的Shards方式,其它的就是走的Yahoo的web搜索了,这个改进肯定是在被Yahoo收购之后的事了。

看资料,Flickr的服务器确实够强:EMT64 w/RHEL4, 16G的内存,6块15K RPM硬盘组成的RAID-10盘阵(羡慕…)。用户数据目前有12TB,当然这不包括图片,图片内容比这点数据要多的去了。都是2U的机器,每个数据库shard存有120G的数据,也就是说Flickr有10台数据库shard机器。

 

flickr-3

 

 

 数据备份

Flickr这种提供图片服务的web2.0网站,数据尤其重要,所以他的备份工作显的格外重要:
采用定时任务,从各个数据库shard机器中非并行的备数据(也就是避免同时开始备份任务),当然也有热备份。每天晚上会扫描数据库集群生成快照(Snapshots),Flickr给出的经验中告诫,备份数据时,不要在删除(写入)大文件的时候立即进行写(删除)操作,这会损害性能。

图片是以文件的形式存储。一旦文件上传,应用服务会生成几种不同的size文件,完成之后,这些图片信息和图片的指向信息就被存进数据库。汇集数据也相当快,因为是在各自的shard上并行执行,10台shard并行响应速度可想而知。每个shard的最大连接数的是400个每秒,或者是设置成每个服务器+其相应的shard共 800个连接。最大45线程,因为目前还不会出现有超过45个用户同时并发的情况。

Tag标签

Tag是web2.0的特征,但tag不适合用传统的关系数据库来描述,Denormalization(反向规格化)和缓存是解决生成大量tag以控制在毫秒级的唯一方法 (Cal Henderson 的结论),为了实现毫秒级的大量tag生成,flickr的数据增加了一倍;还要写额外的程序去产生这些资料,而且还有额外的程序来保证数据间的一致性。可见高性能是和离复杂度分不开的。

他们的一些数据视图是后台离线计算的,把结果存储到MySQL中。因为其中有一些关系很复杂的运算,他们采取了利用服务器空闲的cpu来执行这些耗资源的程序。这可又是一复杂的处理。

另外,Flickr还实现了业务连贯性计划(Business Continuity Planning),以保证业务的不间断,让所有的数据都及时能写到数据层(db,memcache等),不会有服务挂起的情况。这个涉及到具体的业务流程了,但应该也属于构架很重要的一部分,但关于这方面的资料Flickr透露的还太少,可以因为这个太涉及业务流程了吧。

总结
我在这里只是总结了Flickr构架的整体概念,他们本身的发展历程就是一个很好的学习素材。我相信任何一个做web2.0的有志人士,都不会是想只停留在一个原始的无序阶段,无论您的模式怎样,最终都得有一个好的构架来支撑。好的构架就好比肥沃的土壤,业内各种乱飞的模式也只是一个个的种子而以。我曾听一个朋友提到过当年联众怎么没有独霸国内的在线小游戏市场的事,我想和他当年土壤很有关系吧。

      (图片来源于前面提到的资源中ppt截图)

YouTube 的构架

youtubeYouTube增长的十分迅猛,它成立于2005年2月,一年后的2006年3月,每天已经有3000万的视频服务请求,到了2006年7月,已经达到每天近1亿的视频访问,但负责整个网站维护的人数却并没有我们想象中的那么多。比较可信的说法是:2个系统管理员,2个软件构架工程师,2个开发工程师,2个网络工程师,1个DBA。

目前YouTube所使用的平台:
Apache
Python
Linux(Suse)
MySQL
psyco,一个动态的 python->C 编译器,用于为python提速
lighttpd,用于视频内容

YouTube使用NetScaler 进行负载均衡和缓存静态内容,Apache 使用 mod_fast_cgi,请求是由一个Python应用服务器来单独处理. 应用服务器请求多个数据库和其它的资源来获取数据,再把数据组装成一个Html页面。其构造极具可扩展:可以通过增加机器来扩充大规模需要的Web层。

系统中Python不构成瓶颈,大部分的时间是花费在远程调用上 (RPC),并且Python 适合快速灵活的开发和部署,这一点于市场竞争很关健。通常每个页面的服务时间小于 100 ms 。它还使用 psyco, 一个动态的 python->C 编译器 ,使用JIT编译器的方法,以优化内部循环。 在对CPU占用率比较高的应用,如加密应用,他们使用C来进行处理。为一些处理代价比较高的请求预先生成并缓存HTML。 在数据库中实现Row级别的缓存。缓存Python对象等。

YouTube的视频存储:

每个视频都存储在一个小型集群中,所以每个视频都至少存储在一个以上的机器上,更多的磁盘来存储内容意味着更快的速度。如果一台机器down掉,其它的机器可以补上,还可以做到联机备份.
因为Apache会有多的开销,所有服务器使用lighttpd做为视频文件的web server,使用 epoll 处理 fds.
比较受欢迎的视频会移到CDN上,点击量很少的内容(一天1-20个 views的那种)会存储在相应标识的服务器上.


YouTube的缩略图处理

对每个视频都有四张缩略图与之对应,这些缩略图都是专门由单独的服务器来提供web访问,这里就会遇到存储大量小文件所遇到的问题,文件系统也像是一个数据库,对于大文件到无所谓,但对于大量的零碎小文件,磁盘的I/O可受不了,何况每个目录所能存储的文件数也有一个极限,为此YouTube使用了Linux里的页面缓存和索引节点缓存,为了突破目录的存储文件数限制,在Ext3下,将文件转移到了多层次的目录结构上,目前在2.6内核下可以将Ext3的大目录处理能力提高100倍。但是,将大量文件存储在一个文件系统下仍然不是一个太好的方法。

一个页面显示60个缩略图,在每秒高请求的情况下,这样的高负荷Apache不太适合:在前端的Apache使用反向代理squid,刚开始还算凑合,但随着负荷的增加性能下降的很明显,从能处理每秒300个请求下滑到每秒20个。于是YouTube尝试用lighttpd,但lighttpd的单线程性能实在说不过去,又通过安装多线程的mod来让lighttpd支持高并发,但每个线程又有各位独立的缓存,就这导致安装一个存储大量图片的机器要消耗24小时,重启一次也要消耗6~10个小时以填充缓存。

为了解决这个问题,他们使用了Google的BigTable,一个分布的数据存储:避免了小文件存储的问题,速度够快,容错也不错,还有较低的延迟,因为它使用了一个分布式多级高速缓存,并能跨越不同的站点。
YouTube数据库

       早年:
使用MySQL存储元数据,像用户信息,tags和视频描述。
用一个包含10块硬盘的RAID 10 Volume盘阵存储数据,由于当时经济拮据,不得不租用硬件和使用信用卡购买硬件。他们也经历了从单一服务器到一个主服务器和多个副服务器的转变经历,然后数据库分区,再解决共享问题。这时遇到的问题是:主服务器是多线程大机器,它可以处理大量的任务,但副服务器是配置不太好的机器(我怀疑可能就是普通PC)只是用来单线程的工作,所以导致副服务器的响应严重滞后,因为缓慢的I/O导致了更新数据时的缓存丢失。使用replicating architecture复制构架,也只是用大量的money换来一点点的写入性能。他们的一个解决办法就是考虑数据的优先级,把数据分成两大类:一个是视频数据池,一个是通用数据群,这样的好处是让观看视频的应用得到更多的资源,而社会网络化这样的相对不太重要的功能能被路由到小容量的集群中。

现在:
实现了数据分区。分布式的读写,更好的本地缓存策略以减少IO,并减少了30%的硬件,避免了各服务器间的滞后,并且能方便的扩展数据库。

ok,从YouTube的发展和他的构架中我们可以看到,在最初的开始就应该为将来长远的发展考虑,从应用的特点和服务重点方面去考虑整个构架和性能的扩展成本。并不断的找出系统中的瓶颈,每一次瓶颈的解决,对于用户的体验就是一次增强,对于瓶颈,要有可控的预见和分析:软件,缓存,操作系统,磁盘I/O,数据库,带宽… …
YouTube成功的另一个要素,是他有一支学科交叉的团队,我在前面提到过他们的人员构成,各有专攻,一个良好的团队,有什么事是不可能的!:Impossible is nothing.