Pages

Sunday, 29 September 2013

如何构建高性能web站点

《构建高性能web站点》-负载均衡

由于单机性能是有限的,不管机器多么牛逼,最后都会受限于一些硬件和一些软件,在这个时候web的水平扩展变得非常重要。

常见的负载均衡的方式:

1.程序重定向

2.dns的A记录或者CNAME进行负载均衡

3.七层http反向代理负载均衡,也就是常见的nginx、HAProxy、varnish

4.四层负载均衡,在IP层实现的负载均衡

总结1:http重定向负载均衡

这个很好处理啊,当一个请求来的时候,通过一定的策略,把请求放到后端服务器。比如。策略一般用随机转发,或者通过监控后端机器的流量、负载等数据,发送到中转机进行一些简单的策略调度。

总结2:DNS负载均衡

这个应该所有的大型互联网公司都在用,首先是因为不能把所有的鸡蛋放在一个篮子里面,如果把所有的数据、程序放在一个地方,要是地震了不就全挂了。

但是dns不能实时的,有一定的缓存区,也就是你的机器挂掉了,dns才不管这个呢,依旧指向这个ip,一般公网上的dns提供商指定的最短dns的过期时间为3600s(一个小时),这是无法接受的。现在发现的dnspod 可以设置的最短的dns过期时间是600s,这个对于中小型网站来说,还是一个不错的选择。

dns负载均衡还有一个用处就是智能解析,可以把电信的解析到电信的机器,网通的解析到网通的机器,同样有dnspod可以做这个事情,这个比较适合中型网站。

动态dns:大型互联网公司一般都自己管理自己的dns,首先是业务台复杂,其次是cdn太多,最后是需要迅速切换流量和处理宕机事故。据我所知,新浪的dns过期时间是120s,也就是可以接受2分钟来切换故障(不知道我理解对与否).

总结3:反向代理负载均衡

nginx、HAProxy、varnish、squid都是目前最好的反向代理服务器。反向代理服务器需要的几个特性:

    自身效率高,别弄到最后瓶颈在反向代理服务器
    能对后端服务器有权重设置
    对后端服务器有健康检查、异常服务器自动删除、自动恢复的功能
    尽量只转发动态耗时的处理程序,静态的最好本地直接向浏览器传输。

总结4:IP层——四层的负载均衡(NAT)

核心思想:由linux内核来实现,在网络数据包从内核缓冲区到达用户程序地址空间前,尽早转发到其他服务器上。

首先,网络七层是分开设计的,IP层工作在应用层下,可以透明地进行转发,而应用层根本不知道下面的事情。

比如LVS,F5都是四层的负载均衡软件和设备

LVS-NAT,核心,修改包头

总结5:直接路由,也是修改数据包头,让真实服务器不经过负载均衡设备,直接传输数据给客户端。

LVS-DR

请求:浏览器————————>负载均衡设备——————–>真实服务器

响应:真实服务器——————————–>浏览器

写完了,大概总结了一下,没太好的思路,因为在大公司里面,这些都是系统工程师、运维做好了的,开发工程师一般不用考虑这个,但是我依旧感觉这个东西就是互联网的设置之一,毕竟一个服务器也干不了啥.
------------------------------------------------------
《构建高性能web站点》-动态脚本加速

不可否认,动态脚本语言在互联网中占有举足轻重的地位,其最大的好处就是开发成本低、开发速度极快,这极大符合互联网的发展情况。3p系列(php、python、perl)和java(暂且把java的web开发也算到脚本语言中,其实差不多),成为目前web开发最强大的中间层(处于数据存储和前端界面的逻辑层)。

主要使用java的网站:淘宝(现在也部分使用php了),搜狐,人人

主要使用php的网站:facebook,sina,baidu,qq,kaixin001,各大团购网站

主要使用python的网站:google,douban

perl好像现在很少人用来写web中间逻辑层了,更偏向于系统去了。

有人说脚本语言执行效率太低,最好用c、c++来写网页。这个不是不可以,但是目前硬件如此便宜,开发时间如此之短,产品更改如此频繁,中国特色如此之多,如果用c、c++来做这中间的逻辑层,程序员的工资就会翻上好几倍,开发时间也会长上好几倍,c、c++的程序员都是完美主义者,他们不希望自己设计的软件被被人摆布,说怎么改就怎么改(主要是改起来太麻烦了,牵一发而动全身)),最后上线,发现用c、c++程序安装到线上的时候运维搞不定,悲剧,只能自己维护自己的代码。

总的来说,用脚本语言来处理web的中间逻辑层目前还没有遇到大的性能问题,不然facebook早变成myspace了。

总结1:php parsekit扩展 ,主要是用来看php的opcode
安装php的扩展

wget http://pecl.php.net/get/parsekit-1.3.0.tgz
tar xzvf parsekit-1.3.0.tgz
cd parsekit-1.3.0
/usr/local/php-fcgi/bin/phpize
./configure --with-php-config=/usr/local/php-fcgi/bin/php-config
make;make install
vim /usr/local/php-fcgi/etc/php.ini
//加入 extension=parsekit.so
php -m|grep parsekit

运行
php -r "var_dump(parsekit_compile_string('print 1+1;'));"
就可以打印出opcode的数组

php 的opcode和汇编类似的,是一种二元运算的赋值语法,比如

res1 = 1+1

res2 = res1*3

总结2:几种php的opcode(APC,eAccelerator,xcache)

其实都应该是差不多的,网上有一些比较的教程,可以参考一下

http://apps.hi.baidu.com/share/detail/15405216

我常用的是eAccelerator,这篇文章很全面,值得一看http://www.toplee.com/blog/100.html#pp1

eAccelerator 可以开启debug模式,

eaccelerator.debug=”1″

eaccelerator.log_file = “/var/log/httpd/eaccelerator_logg”

这样就可以看到那些文件被缓存了

总结3:xdebug 和wincachegrind 在windows 下看php程序运行效率

xdebug 是目前开发的时候最好的php代码调试工具和php效率检测工具,刻意和wincachegrind一起在windows下查看php程序的运行效率。
这里有比较详细的xdebug的教程 http://bbs.phpchina.com/thread-209365-1-1.html

1.下载地址:http://www.xdebug.org/download.php
版本选择: xdebug有Non-thread-safe(非线程安全)、thread-safe(线程安全)
写一个test.php,内容为,搜索"Thread Safety" enable为线程安全版、disable为非线程安全版
选择VC6还是VC9?

标明 MSVC9 (Visual C++ 2008) 的是VC9
如果你在apache1或者apache2下使用PHP,你应该选择VC6的版本
如果你在IIS下使用PHP应该选择VC9的版本
VC6的版本使用visual studio6编译
VC9使用Visual Studio 2008编译,并且改进了性能和稳定性。VC9版本的PHP需要你安装Microsoft 2008 C++ Runtime
不要在apache下使用VC9的版本

2.Xdebug安装:
将下载的php_xdebug-2.1.0-5.2-vc6.dll放到php的ext目录,重命名为php_xdebug.dll;

3.编辑php.ini,加入下面几行:
[Xdebug]
extension=php_xdebug.dll
xdebug.profiler_enable=on
xdebug.trace_output_dir="X:\Projects\xdebug"
xdebug.profiler_output_dir="X:\Projects\xdebug"
xdebug.profiler_output_name = "cachegrind.out.%p"
最后一行主要是为了和wincachegrind.out整合
后面的目录“I:\Projects\xdebug”为你想要放置Xdebug输出的数据文件的目录,可自由设置。
4. 重启Apache;
5. 写一个test.php,内容为,如果输出的内容中有看到xdebug,说明安装配置成功。

下载wincachegrid http://sourceforge.net/projects/wincachegrind/

打开在那个目录中生成的 cachegrind.out.* 就可以看到每一个过程调用执行了多长时间.

总结3:xhprof  在linux 下看php程序运行效率

xhprof 是facebook开发出来的查看php效率的工具,非常牛逼,可以生成时间消耗图片.

具体安装 http://hi.baidu.com/thinkinginlamp/blog/item/f4bd08fa1a03ba9e59ee90fd.html

快速体验可以到新浪的云计算平台 http://sae.sina.com.cn 体验
----------------------------------------------------------------------

《构建高性能WEB站点》-动态内容缓存(页面缓存)


动态内容缓存就是把动态语言(目前一般是动态脚本php、python、perl、java、ruby等)生成的结果保存起来,下次来取的时候直接 返回html的结果。缓存这个东西,就是为了避免重复计算,把结果保留下来,这个比较适合读多写少的页面和网站,但是像微博的页面、人人的个人首页之类的 就绝对不能用页面缓存。
在新浪这种大型的互联网公司,而且以读为主的新闻媒体,页面缓存一般分两种。一种是静态页面,就是编辑直接敲html生成一个页面,然后发到线上的前端机(比如首页,经过人工处理过的排行榜,推荐视频等,也就是运营页面)。另外一些就是存放在数据库中的数据,一般是php调用计算出页面之后放到前端cdn缓存,一段时间之后自动过期重新取数据,比如用squid、varnish等实现前端缓存。
总结1:几个时间数据
1.5ms的时间=数据通过光缆从北京传输到西安的时间。
2.数据传输的速度从cpu向外逐渐变慢 cpu>>高速缓存>>内存>>硬盘
3.html的速度是php等动态脚本速度的10倍以上
总结2:cache目录的设置
一般的linux的文件系统一个目录下的文件上万之后,CPU遍历目录效率很低,解决方法:
1.使用支持目录hash等加速目录遍历的文件系统缓解这些情况,比如XFSreiserfs
2.在自己缓存的时候hash一下目录,也就是缓存成y/m/d/id.html 的类型,一般cache程序都支持这样
总结3:smarty 文件cache 的开启方式

$smarty->caching = true;
$smarty->template_page="index.tpl";
$smarty->cache_lifetime = 60;

4.几种php级别的全页面缓存的比较
smarty 以文件的方式缓存在磁盘,可以用于小规模的页面缓存
file cache 这个适合不常更新的网站,生成简单,速度非常快。网站内容多了重建内容麻烦。
apc cache 等 修改程序,用本机内存,不具有通用性
memcache 走tcp,需要网络传输,但是用来缓存页面有点浪费
总结4:带宽对吞吐率的影响
比如租用了一个10m的带宽,全部是静态页,每个页面10k
(10Mbit/s*8)/10kB = 8ooreqs/s
也就是极限只能是800reqs/s
总结5:页面页面缓存更新策略(比如cms)
1.在新建、更新文件的时候更新缓存,但是如果有排行榜之类的就很麻烦,所有的都得修改
2.异步crontab或者一个daemon的程序修改
3.用局部缓存的方式处理排行榜、推荐之类的,比如用SSI的方式来包含其他的需要半实时更新的排行榜之类的数据
如 http://news.sina.com.cn/c/2011-04-23/021522342946.shtml 这种shtml格式的文件,这个可以满足大多数的需求。
总结6:SSI
SSI就是服务器端包含的意思,就是这个请求不用到达php,在apache,nginx,lighttpd就处理的一种技术,不到动态脚本,速度自然快很多。
apache下的配置(安装mode_include模块)
AddType text/html .html
AddOutputFilter INCLUDES .shtml

----------------------------------------------------------------

《构建高性能web站点》-浏览器缓存


浏览器缓存,其实这个在浏览器出来的时候就有了,ie6有名的未知缓存让很多前端js开发人员叫苦不迭,常常看见一个ajax请求后面莫名其妙地跟了一个随机数,这个就是解决缓存的问题。

但是浏览器缓存总的来说,是利大于弊,因为网速有限,如果一个页面有10张1m的图片,用户的带宽是2m的,那也得花一分钟才能下载完成,如果他很不幸地点击了一次刷新,如果不用浏览器缓存的话,他将再等一分钟。

而且程序的需要,以前常见的js、image、css之类的静态文件缓存的需求已经远远达不到要求了,能和动态程序交互的就只有cookie了(当然还是有flash的flashcookie),但是容量太小,google曾今开发出一个gears的东西用来存更多的东西,但是这个东西还是无法推广起来,毕竟是一家的标准。最后在html5中,一个叫local storage的东西有望成为下一代的浏览器缓存的标准,这将解决目前浏览器缓存的一些瓶颈。

从某种意义上说,浏览器缓存是很环保的,合适的设计可以减少很多不必要的流量,节约不少的电能也更环保。

总结1:各种浏览器的缓存方式

1.ie 在缓存目录的一些独立的文件 xp的ie6 的缓存目录 C:\Documents and Settings\kebebrar\Local Settings\Temporary Internet Files

2.firefox 是一种类似innodb的方式存储的key value 的模式,在地址栏中输入 about:cache 可以看见缓存的文件

3.chrome 默认在 C:\Application Data\Google\Chrome\User Data

总结2:缓存协商

浏览器和web服务器的交流的只能通过html,而且主要是通过html的头来实现的。

协商的方式,浏览器向webserver发出请求后,web server要告诉浏览器哪些是可以缓存的,浏览器下次请求的时候,先判断这个文件是不是在有效期内,如果在有效期内,直接用,不在的话,请求web server,web server迅速告诉浏览器是否可用,如果过期了就重传一份。

总结3:Last-modified 的过程

一般静态文件在服务器上都有一个最后修改时间,web服务器一般通过这个时间来判断这个资源是否被修改过。

整个请求过程在最重要的3个http头

Last-Modified: Fri, 20 Mar 2009 07:53:02 GMT //web服务器告诉浏览器这个的最后修改时间
If-Modified-Since: Fri, 20 Mar 2009 07:53:02 GMT //浏览器下次请求问这个文件在这个时间后是否修改
HTTP/1.1 304 Not Modified //如果这个文件没有被修改,web服务器回答304 没有被修改

总结4:Etag 过程

Etag是web服务器给每个资源编的一个号,这个编号是通过一算法来生成的不会重复的号,和浏览器交互过程如下。

Etag: "111111-b-222222" //web服务器告诉浏览器这个资源的Etag编号
If-None-Math: " 111111-b-222222" //浏览器下次请求的时候问这个Etag还能否用
HTTP/1.1 304 Not Modified //web服务器回答没有变化,可以用

但是大型的网站都不会用Etag,因为大网站的前端机有很多台,前端proxy之后不一定请求到上次访问的那一台,所以这个就导致了Etag这个标签无效,还得重传。

http://baike.baidu.com/view/3039264.htm#sub3039264

总结5:Expires 直接不让浏览器再次请求,直接用已经下载的资源

一般情况下,图片都很少修改,为什么还要问一次浏览器这个可否用呢,这个算是多余的请求了,所以web服务器可以直接告诉浏览器,在某个时间段,你就自己做主把这拿来用就行了(这个和某些部门机关差不多,老大睁一只眼闭一只眼,你们看着办,在进监狱之前想怎么用怎么用)。

但是这个是基于服务器时间的, 如果你的服务器时间不对,比客户的机器慢了一个小时,你设置过期时间<60min,那你就悲剧吧,百思不得其解吧。
操作     last-modified     expires
F5     有效     无效
ctrl+F5强制刷新     无效     无效
点击转到或者在url上回车     无效     有效

last-modified 有效表示浏览器会请求,但是web服务器返回304
expires 有效表示浏览器不请求了,直接用本地文件

总结6:Cache-Control

这个主要是上面的Expires过期时间不统一的问题,web服务器和用户的时间误差可能导致缓存永远无效,Cache-Control用浏览器端的时间来判断过期。

浏览器会优先考虑 Cache-Control,如果在Cache-Control时间没有的情况下,才会符合expires.
------------------------------------------------

《构建高性能web站点》-反向代理缓存


反向代理缓存是目前互联网公司使用最常见的一种方式,像新浪这样的新闻网站,大多数的页面都是读的页面,而且要求速度,所以会用反向代理的方式来构建cdn,已达到最快的访问速度。
反向代理缓存依旧是基于HTTP的一种缓存方式,反向代理服务器和web服务器以及浏览器通过HTTP头来协商,哪些东西可以缓存,哪些应该缓存过期。
因为反向代理缓存是基于HTTP的,所以其工作在七层,常用的软件是古老的squid和新兴的varnish、nginx。
总结1:集中反向代理缓存服务器的比较
1.squid,很古老的反向代理软件,拥有传统代理、身份验证、流量管理等高级功能,但是配置太复杂。它算是目前互联网应用得最多的反向缓存代理服务器,工作于各大古老的cdn上。
2.varnish是新兴的一个软件,设计简单,更符合目前互联网的应用,很多公司都采用了varnish这个软件,比如微博的openapi  http://open.t.sina.com.cn
3.nginx nginx的反向代理缓存是通过一个模块实proxy_cache来实现的,目前这个模块还在完善中,但是很多公司已经开始使用了。
总结2:varnish的一些简单总结
小总结一些书上的东西,以后更加全面地实施一遍
1.varnish的换成为一个文件,数据结构和mysql的innodb类似
2.varnish的配置文件很容易理解,是一种类编程语言实现的控制逻辑,主要是对HTTP的头的判断
3.nginx配置的主要的两种方式,一种是浏览器请求的时候判断哪些该从缓存文件输出,哪些应该求求后端;第二种是从服务器端出来的数据,varnish判断哪些可以缓存
从浏览器得到请求后的判断:

sub vcl_recv{
if(req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE"){
return (pipe);
}
if(req.request != "GET" && req.request != "HEAD"){
return (pass);
}
if(req.http.Authorization || req.http.Cookie){
return(pass)
}
return(lookup);
}

pass 表示不检查缓存直接发给后端
lookup 表示先从缓存中找信息
http://hi.baidu.com/leolance/blog/item/a4bb9d823e95603066096e6c.html
从后端服务器获得数据后的判断:
sub vcl_fetch{
if(!obj.cacheable){
return pass;
}
if(!obj.http.Set-Cookie){
return pass;
}
set obj.prefetch = -30s;
return (deliver);
}

pass 表示直接返回给浏览器
deliver 表示将内容写入缓存区
总结3:缓存命中率
由于缓存一般都以LRU的模式进行淘汰,有可能缓存空间太小,内容又很多,导致了很多需要的文件被踢出去了。
缓存命中率是一个需要预算和监控分析的,需要数据上进行各种分析后不断调整.
-------------------------------------------------------------------------------------

《构建高性能web站点》-web组件分离


首先还是感叹一下http设计的牛逼之处,http是天然的分布式,它造就了互联网应用的扩展如此的容易,一个很小规模的应用可以很容易就扩展开来。
web组建分离就是把页面上的每个资源按照自己特有的属性分开,由不同的服务器来处理。分开的原因分服务器的原因和浏览器的原因,浏览器的原因是浏 览器在统一个域名下并发请求数的限制,比如ie6是2,这个确实很2的,10年前设计自然跟不上现在的需求,服务器端主要资源的属性不一样,有的需要很多 cpu计算,有的基本不用cpu,所以需要分开处理。
总结1:web组建的需要考虑的不同的属性
  • 文件大小
  • 文件数量
  • 内容更新频率
  • 预计并发用户
  • 是否需要脚本解释
  • 是否有大量cpu计算
  • 是否需要连数据库
  • 对数据库的操作是读还是写
对应的需要在服务器端考虑的一些策略
  • 是否需要epoll模型
  • 是否使用sendfile发送文件
  • 是否使用异步IO
  • 是否使用HTTP长连接
  • 是否需要oopcode
  • 缓存有效期多长
  • 是否使用浏览器缓存,时间多长
  • 是否使用反响代理缓存,时间多长
  • 是否负载均衡
总结2:组件分离的域名处理
如果没有多余的域名,最好也做一些组件分离的工作。独立域名的好处,不用传送cookie,减少流量,其实这个很重要,又的图片资源很少,但是浏览器必须把硕大的cookie发过去,太浪费了。
总结3:浏览器并发连接数
可以在这个网站看各种浏览器的请求的瀑布图 http://site-perf.com/
浏览器默认最大并发连接数
浏览器 HTTP 1.1 HTTP 1.0
IE 6,7 2 4
IE 8 6 6
Firefox 2 2 8
Firefox 3 6 6
Safari 3, 4 4 4
Chrome 1,2 6 ?
Chrome 3 4 4
Opera 9.63,10.00alpha 4 4
总结3:各种不同资源的不同的处理方法
1.动态页面
  • 开启opcode
  • 提供足够快的CPU、足够大的内存,尽量不要用到swap
  • 多进程,但是不要太多,这个需要精确计算或者从日志中分析,以免上下文切换的时间浪费过多
  • 与数据库、cache保存高速连接
2.静态页面(IO密集型)
  • 支持epoll
  • 非阻塞IO
  • 异步IO
  • 使用sendfile
  • 但进程(避免进程切换的开销)
  • 使用高速磁盘
  • 用raid分区,这样可以提高读取数据的速度
  • 购买足够的带宽,因为是IO密集型,如果带宽太小,会导致在交换机路由器那里等待
3.图片、css、js
  • 长连接,因为web的图片很小,而且一般需要同时传输很多张
  • 设置较长的expires过期时间.
------------------------------------------------------------------------------

《构建高性能web站点》-分布式缓存


分布式缓存这个应该算是目前互联网技术上最热的地方,因为现在的网站,越来越偏向于全动态话,但是数据库不可能能抵抗住如此强悍的查询,所以不得不在db前面挡一层cache,这也便是目前互联网从业者最主要的设计点之一。
memory is another disk,这个是分布式缓存的最核心的地方,就是把数据放在内存里面。为什么放在内存里面,首先这个数据不能放在cpu以及及寄存器吧,其次磁盘太慢了, 无法接受,就只能在这个中间的就是内存了,但是内存又一个天然的问题,就是宕机之后数据丢失,这个算是互联网架构师们最头疼的设计点之二。
当前最火web2.0 比如facebook、weibo,都采用了这种简单的架构,就是存储+cache+前端脚本的模式,而且,他们都不约而同地用了同一种技术memcached这种key-value的缓存方式。
key-value这种缓存方式最主要的好处就是它简单且高效,水平sharding非常容易,只要控制地好,啥问题都不会是大问题。
当前最主要的nosql 缓存开源软件,我的理解,nosql的东西,分两种,一种算数据库,一种算缓存,最大的区别就是是否把数据放到硬盘中,放到硬盘中就是数据库,否则就只能 是cache。当然目前最流行的设计是数据库+cache,比如redis,就是自己维护一些内存的缓存,又写入的时候可选择定时写入硬盘或则事实写入硬 盘。
设计分布式缓存最主要也是最重要的两点:
  • 要预算缓存的大小以及水平sharding的复杂度
  • 不要丢失数据
如果涉及到跨IDC那得让程序读取本地的缓存而不是千里迢迢去遥远的地方要一个数据。
先写到这里,以后补充具体的。
-----------------------------------------

《构建高性能web站点》-网络传输


网络传输,算是互联网最原始的形态,从最初的smtp、ftp、telnet形成的相关的协议到最后的http一统天下,不得不说,互联网的基础,就是传输数据。只是,http这种高层的协议越来越符合这个世界的消息传递的需求,从而有了现在互联网的高速发展。

总结1: 网卡和操作系统交换数据的过程
接受数据:
网卡把接收到的数据转换成二进制的数据,写入操作系统的在内存中的一个内核缓冲区,然后用中断的方式切换到相 关的程序来读取内核缓冲区的数据。如果流量过大,会导致内核缓冲区写满而丢失数据。解决方案:在数据链路层加入控制流量的机制,一般采用的是滑动窗口的原 理(简单说就是发送方连续发几个分组,不用停下等待接收方每一个分组都确认,可以提高速度…..)
数据发送:

影响发送速度的几个方面:
1.接收方速度受限,
2.数据传输的并行度,也就是和计算机系统总线类似,光纤横切面能同时传32位还是64位这种概念。
总结2:传输速度(传输速度只与介质以及介质的温度等相关)
铜线中电信号传输速度:2.3*10的8次方 ,只能传100m
光缆中电信号传输速度:2*10的8次方,可以传上千m
光速:3*10的8次方
由于信号在光缆中采用全反射的方式传播,实际距离比较远,所以速度降低了。
总结3:带宽
最直接的理解,如果在市场上租用了10M的带宽,实际的传输速度(不计算传输中间的过程限制,也不计算一些头信息的带宽),极限时10/8=1.25Mbyte/s,除了8之后发现带宽好小啊。
独立带宽和共享带宽,最直接的理解就是你的流量在出IDC的时候排队不,共享带宽需要排队,独立带宽,不排队,直接出到下一级的路由器。
总结4:
一些简单网速的计算(概念上的)
相应时间 = 服务端发送时间 +传输时间+用户处理时间
= (数据量比特数/带宽)+(传输距离/传播速度)+处理时间
下载速度 = 数据量字节数/带宽
-----------------------------------------------------------------

《构建高性能web站点》——服务器并发处理能力(1)


这一章,很难懂,是因为整个互联网都在研究这个东西,大到google、facebook等顶级网站,小到xxxorg等几乎没有流量的网站都在研 究者这个东西。当然,这个东西就像买一个笔记本一样,当然希望最高的性价比,用最低的工资招到最好的人才(不过这个在中国互联网好像行不通,一般nb的人 都会跑掉)。
这一章和我的工作隔得比较远,因为在大型的互联网公司,都会有既定的一套线上的架构放在那里,一般不会让我们作为开发人员碰到那些东西,比如在新浪 有一个动态应用平台的东东(如果不懂就去看看SAE,SAE就是这个动态应用平台派生出来的),一般纯动态的程序很难出现大的问题,强大的前端集群配置、 带宽保证、数据库保证、缓存保证,这个让一般的开发者去优化仅限于php逻辑、数据库的sql、缓存的设计,很难接触到服务器级以及web服务软件级别的 优化。
这一章,有很多具体数据的概念,对我理解高并发的web开发有很大的帮助,最直接的感受,原来最原始的html才是最高效的。
名词:
1.吞吐率:直接理解就是一秒钟能响应多少次请求 reqs/s
2.最大并发用户数:就是在用户等待限度以内,以及服务器处理能力范围内最大的处理数量。就像10086的客 服,有100个客服mm,但是同时有1000个人打电话,那这1000个人得等待很久;如果只有10个人打电话给客服,又浪费了工资;当然,如果有200 个人同时打,等待1分钟,这个是可以接受的,然后最大并发数就是200。这个是协商性质的。
3.浏览器并发连接数:现在的浏览器一般对于在同一个域名下的文件的请求都会是并发的,具体的在http1.1下,IE7—-2, IE8——6,firefox3——–4….所以这个导致了后面的要组建分离,就是把js、image、css之类的独立域名出去。
4.用户平均等待时间:这个就是上面的那个只有100个客服然后100+的用户打电话过来的时候需要等待的时间,前提是>100这样才会有一定的压力,没有压力,就不用等待了。
5.服务器平均请求处理时间:这个很好理解,就是平均处理一个请求需要多少时间,这个应该是吞吐率的倒数。
总结1:常用的压力测试软件
1.apache ab 这个最强大,返回的信息最多,应该是最实用的一个测试工具,到处udou是。
2.linux webbench 这个简单,返回结果也简单,可以看个大概
3.pc loadRunner  这个是模拟客户浏览器的行为,能带来比较真实的压力测试数据。
总结2:DMA在网络流量中的过程
总结3:进程,轻量级进程,线程的区别
1.进程由系统调用fork()产生,维护自己独立的地址空间、和上下文信息,可以和其他进程通信,但是不相互依赖。比如apache 的prefork模式,有点:稳定;缺点:由于不共享数据,内存占用很大。
2.轻量级的进程由系统调用clone()产生,可以共享一些地址空间、打开的文件等,但是上下文切换很多。
3.线程:这个目前在内核上没有统一,还很少有web服务器使用这个。
---------------------------------------------------------------------

《构建高性能web站点》——服务器并发处理能力2–服务器进程


很多时候,web服务器的性能是我们最关注的地方,如果是纯静态资源的网站,web服务器的性能将是考虑的最主要的地方。
对于互联网这种高并发的情况,各种服务器都采用多进程或者多线程的方式来处理并发请求,这也就意味着要争夺资源,争夺资源就需要一定的规则,比如先 来后到、VIP优先等。但是举个现在最remen和naocan的例子,北京的摇号买车,xx部门最后定下了一个八国联军进入中国时候的一个标语——中国 人和狗不得入内 的规定,具体的我不多说了,这个比喻很形象。
继续技术上研讨,做技术的人,经常发牢骚,是因为从古到今,中国的技术都是从属与社会,中国发明了火药,只是一个历史,却重来没有一个当官的想到把它拿来强国,现在的中国也是这样,大家都自娱自乐,天天唱红歌,喝红酒,扯红蛋…..
总结1:系统级别的进程切换的过程——硬上下文切换
首先,进程有自己的独立内存地址空间,别人不能侵犯,但是CPU和寄存器资源有限,得共享。
被挂起的意思就是原来在CPU和寄存器中的数据被拿到内核维护的一段内存空间——内核态对战中。
进程被恢复的过程就是重新装入CPU和寄存器进行计算。
这个过程就向去政府银行取钱一样,经常存进去,这个得排队等待,还的取出来,这也得排队等待。
总结2:nmon这个东西,很强大,能看见很多不容易看见的东西
nmon 然后键盘输入k 可以看见一下信息

Kernel Stats ------------------------------------------------------|
| RunQueue 1 Load Average CPU use since boot time |
| ContextSwitch 91.9 1 mins 0.00 Uptime Days= 18 Hours= 4 Mins= 2 |
| Forks 0.0 5 mins 0.00 Idle Days= 17 Hours=22 Mins=40 |
| Interrupts 36.0 15 mins 0.00 Average CPU use= 1.23%

加粗的地方就是上下文切换的数据. 引用《高性能web站点》的一个数据,操作系统的维护最基本的工作的进程切换需要28.4次切换,当然在这个数量级别而已。
apache 用prefork 模式的时候进程切换远大于lighhttpd和nginx,好像并不在一个数量级。
总结3:top命令中几个很重要的数
RES 使用的物理空间
SWAP 使用的虚拟内存空间
VIRTual= RES+SWAP
通过这个计算你可以大概算出你的4G内存的服务器可以开启多少个httpd的进程了。
总结4:IOwait 这个东西(本来不算在进程这一块,一并总结了)

Cpu(s): 1.3%us, 0.7%sy, 0.0%ni, 95.3%id, 0.0%wa, 0.0%hi, 0.0%si, 2.7%st

加粗的地方就是IOwait,这个最初是衡量CPU的执行效率的,IOwait > 50%表示在这次执行过程中,IO占了50%以上的时间,CPU使用的时间较少。这个IOwait应该包含磁盘IO和网络传输的IO(具体的还不定)。
参照书上的解释:这个数据得根据实际情况来合理解释,IOwait高并不一定代表IO性能出现瓶颈,IOwait=0可能也很繁忙(这个具体的参照书上的demo解释)。 -----------------------------------------------------------------------------------------

《构建高性能web站点》–服务器并发处理能力(3)


总结一:系统调用
进程的运行态分为用户态和内核态两种运行模式,进程可以在这两种运行模式下切换,当然会有一定的开销。一般情况下,程序进行数据的运算,以及内存中 数据的管理都是在用户态的,但是一旦要写磁盘、发送网络数据,就得切换到内核态进行系统调用,因为系统调用有更多的权利控制计算机,内核态完成后之后切换 回用户态,这个比较好理解。
系统调用在linux下的查看方式,strace -p pid 就可以查看到一个进程的运行的系统调用了,这是nginx的子进程空闲的时候的strace过程。

1
2
3
4
5
6
7
8
9
[root@centos www]# strace -p 6991
Process 6991 attached - interrupt to quit
gettimeofday({1301575497, 166608}, NULL) = 0
epoll_wait(14, {}, 512, 500) = 0
gettimeofday({1301575497, 667346}, NULL) = 0
epoll_wait(14, {}, 512, 500) = 0
gettimeofday({1301575498, 167645}, NULL) = 0
epoll_wait(14, {}, 512, 500) = 0
gettimeofday({1301575498, 667913}, NULL) = 0
总结2:nginx的内存管理概况
nginx可以用多线程的方式来处理请求,线程之间可以共享内存资源,及时申请,及时释放,这让nginx的内存占用很低。官方介绍,nginx维护10000个非活跃的http连结只需要2.5m的内存,太bt了,2.5m的内存能做什么。
总结3:长连接的利弊
长连接是TCP层的一个概念,就是一次连结多次发送数据,因为建立连接的代价太大,需要3次握手确认,如果能重用连结,将减少不少的开销。
好处:提高了连结的重用,提高传输速度。如果是用户批量下载小图标、小图片之类的操作,用长连接可以大大提高速度。
弊端:比如apache 的进程数有限,但是它也必须在没有传输数据的时候继续维护这个长连接,就是占着茅房不xx,这个在上茅房的人很多的时候将严重影响别人的身体健康。
总结4:长连接协商
如果客户端浏览器支持长连接,它会在请求的时候发送一个
connection: Keep-Alive 的头(对于IE7,这个时间默认60s)
如果服务器设置了支持长连接,就会返回一个头(比如apache)
Keep-Alive: timeout=5,max=100(表示长连接持续5s,最多传输100次)
长连接时间是以短的那个为准。
-------------------------------------------------------

《构建高性能web站点》–服务器的并发处理能力–I/O


网络流量作为互联网最重要的部分,广义上说最重要的是PV,UV等参数,流量和这个网站的价值成正比。微观来说具体到web服务器,就是一个一个资源从服务器出去、进来的过程。这个过程,是互联网的核心,互联网也就是提供了一种比较方便快捷的数据传输的方法而已。
一般在互联网上考虑的I/O分内存I/O,网络I/O,磁盘I/O,但是在互联网这个基于HTTP和TCP这类上层协议的应用,我们很少考虑内存的 I/O,因为这个I/O远比网络和磁盘快多了,如果一定要用一个比喻来形容,就是北四环的堵车速度和贪污无数修建起来的高铁之间的区别….
总结1:关于I/O等待

1
2
3
4
5
6
在没有人访问的时候,服务器只能等待,比如这个是一个nginx的等待的过程
[root@centos ~]# strace -p 1248
Process 1248 attached - interrupt to quit
gettimeofday({1302358935, 445379}, NULL) = 0
epoll_wait(15, {}, 512, 500)            = 0
gettimeofday({1302358935, 445379}, NULL) = 0
其次是读磁盘的数据的时候,磁头移动速度有限,所以就有读取队列,也便有了阻塞。
向网卡发送数据的时候,数据需要复制到程序的内存空间、网卡缓冲区等操作,必然有一些等待时间。
总结2:几种I/O的模型
  1. 同步阻塞I/O:
  2. 书上最直接的比喻:就是去中关村的大食代吃饭,点了饭之后,得等厨师做完吧,这段时间,你只能在那里等待。
  3. 同步非阻塞I/O:
  4. 一样是等吃饭,但是等饭这段时间,你可以去中关村的地下广场走一圈,但是又怕饭做好了被人领走或者冷掉,你需要2分钟回去看一下,累不累啊。
    和阻塞I/O不同的是,它的调用不会等待数据就绪,如果数据不可读或者不可写,将直接返回给程序状态。比如用非阻塞的recv()接收网络数据的时 候,如果网卡缓冲区莫有可以接收的数据,函数直接返回告诉进程没有可读数据了。好处在于可以防止进程阻塞,可以在一个进程中同时进行多个I/O操作,弊 端,轮询,浪费CPU。
  5. 多路I/O就绪通知:
  6. 等饭的时候出去逛逛,但是中关村的地下商场电子化很先进,有一个大屏幕,你可以在上面看见你的饭好了没有,好了就回来。不用像同步I/O那样去轮询查看。
    提供了对于大量文件描述符就绪检查的方案,允许进程不用通过轮询的方式来获取可用的文件描述符,可以快速通知进程就绪的文件描述符,进程可以只针对这些文件描述符访问数据。
  7. 直接I/O:
  8. 最直接的理解,就是绕过内核缓冲区,在用户态空间实现并管理I/O缓冲区,提高效率,因为以前的数据需要复制两次(从磁盘到内核缓冲区,然后再重内核缓冲区到用户态内存),直接I/O只需要一次。如果是数据库,用直接I/O可以更加合理地的策略来提高歘徐缓存命中率。
  9. sendfile:
  10. 这个的出现有一个问题,就是js,css,image之类的静态文件,他们的传输流程:
    磁盘——>内核缓冲区——>用户内存空间(不需要任何计算)——>复制回内核缓冲区——>拷贝到网卡缓冲区
    数据无缘无故在内核缓冲区复制了两次。
    sendfile的作用就是 磁盘——>内核缓冲区——>网卡缓冲区
    Apache 2.x 用sendfile 来发送大的静态文件
  11. MMap
  12. MMap是linux提供的一种磁盘文件访问方式,可以将内存中的某块地址映射到磁盘的某个文件,从而达到我们对这块内存的访问转换为对磁盘文件的 访问。当然由于这个不进行read(),write()系统调用,效率很高。apache 2.x中使用了MMap来读取小静态文件。
总结3:sellect poll epoll 等实现
select :通过一个系统调用select()来监视包含多个文件描述符在数组,当select()返回后,该数组就绪的文件描述符便会被修改标志位,进程可以获得 这些文件描述从而进行后续的的读写操作。不好的地方:通常存在监视文件描述符不能超过1024这个限制,而且每次用select()对socket进行线 性扫描,增加了开销。
epoll:这个是2.6内核下公认的最好的多路I/O就绪通知的实现方法,分为边缘处罚和水平触发。
一个形象的比喻,就是在你先去商场注册一个手机号码,等饭的时候你出去逛逛,一旦饭好了,商场自动短信提醒…这个是不是很高效啊.
----------------------------------------------------------------------------------
《构建高性能Web站点》

围绕如何构建高性能Web站点,从多个方面、多个角度进行了全面的阐述,涵盖了Web站点性能优化的几乎所有内容,包括数 据的网络传输、服务器并发处理能力、动态网页缓存、动态网页静态化、应用层数据缓存、分布式缓存、Web服务器缓存、反向代理缓存、脚本解释速度、页面组 件分离、浏览器本地缓存、浏览器并发请求、文件的分发、数据库I/O优化、数据库访问、数据库分布式设计、负载均衡、分布式文件系统、性能监控等。在这些 内容中充分抓住本质并结合实践,通过通俗易懂的文字和生动有趣的配图,让读者充分并深入理解高性能架构的真相。同时,《构建高性能Web站点》充分应用跨 学科知识和科学分析方法,通过宽泛的视野和独特的角度,将《构建高性能Web站点》的内容展现得更加透彻和富有趣味。

前言

从我写出第一个HTML网页到现在,已经过去10年多的时间了,回顾过去的Web开发经历,我曾经尝试过各种不同的技术,与此同时,我和我的团队也 犯了很多的错误,但我们为此感到自豪。是的,成长是需要不断付出代价的,每次的挫折都会让我更加深刻地看到隐藏在深处的本质,为什么不把这些内容分享出来 呢?于是便有了《构建高性能Web站点》这本书。
10年来,我们见证了互联网有史以来最快速的发展,商业应用层出不穷,业务逻辑不断复杂,对用户体验的要求也不断提升,随之而来的是应用技术和开发 语言的日新月异,开发者永不停息地学习新技术。同样,在Web站点性能方面,我们一直在跟时间赛跑,社交网站和微博客成为大众的主流应用,带来了更加快 速、实时的信息传递,更多的站点意识到开放的重要性,数据访问和计算无处不在,每秒数以万次的数据传递和读写正在我们身边进行。
但是,构建Web站点的基础技术几乎多年来从未改变,比如诞生于20世纪80年代的TCP,如今依旧是网络数据传输的主宰者,而HTTP则更与我们 息息相关,可是你真的认真学习过它们吗?人们始终在做的事情就是在这些基础技术之上一层一层地封装概念,不断地诞生新的技术。加上商业化产品的市场竞争和 炒作,.NET和Java阵营中的概念让我眼花缭乱却又无可奈何。它们已经成为营销用语,有时候过度会让事情变得更加复杂,让开发者迷失方向。
不论你是一名从事Web开发的工程师,还是一名关心Web性能的架构师,都应该更多地关注各种技术和架构的本质。
从哲学意义上讲,对本质的研究属于形而上学的范畴,但是在自然科学中,我们从来不缺乏对本质的探索,因为只有认识事物的本质才能做出正确的决策,并且真正地驾驭它们,这是毫无争议的。
也许你曾经被商家的促销活动所打动。是的,我们往往只看到事物的表面现象,而经济学家却看到了事物的本质,这正是他们的高明之处。技术和架构同样如 此,你要明白任何收获都是有代价的,天下没有免费的午餐,很多时候,你完全可以用成本经济学的知识来思考技术的合理性,你甚至可以像经济学家一样思考技术 问题。
当然,仅仅理解本质是远远不够的,因为在庞大的架构体系中,涉及太多的部件,而影响整体性能的因素究竟有哪些呢?你也许会感到扑朔迷离,但你必须知 道瓶颈所在,并且能够意识到何时需要优化性能或者扩展规模。与此同时,系统化的分析方法至关重要,中医理论对人体的系统思辨能力体现了先哲们的智慧,在站 点性能不尽如人意的时候,我们能否“对症下药”?这与你对整个系统能否全面把握有着密切的关系。
另一方面,绝对与相对、变化与平衡,是永恒的大道,在很多时候你实际上需要考虑的是如何做出权衡,同时,我们也要铭记变化的道理,系统瓶颈不是一成不变的,久经考验的架构师深知这一点。
道可道,非常道。要将所有的架构之道讲出来实属不易,架构就像艺术品一样,往往无法完全复制,但是独立的技术以及分析的思路是可以学习的,作为优秀的开发者或者架构师,心中的架构才是最有价值的。
如果你希望寻找心中的架构,那么,从本书的绪论开始吧!

目录

第1章 绪论
1.1 等待的真相
1.2 瓶颈在哪里
1.3 增加带宽
1.4 减少网页中的HTTP请求
1.5 加快服务器脚本计算速度
1.6 使用动态内容缓存
1.7 使用数据缓存
1.8 将动态内容静态化
1.9 更换Web服务器软件
1.10 页面组件分离
1.11 合理部署服务器
1.12 使用负载均衡
1.13 优化数据库
1.14 考虑可扩展性
1.15 减少视觉等待
第2章 数据的网络传输
2.1 分层网络模型
2.2 带宽
2.3 响应时间
2.4 互联互通
第3章 服务器并发处理能力
3.1 吞吐率
3.2 CPU并发计算
3.3 系统调用
3.4 内存分配
3.5 持久连接
3.6 I/O模型
3.7 服务器并发策略
第4章 动态内容缓存
4.1 重复的开销
4.2 缓存与速度
4.3 页面缓存
4.4 局部无缓存
4.5 静态化内容
第5章 动态脚本加速
5.1 opcode缓存
5.2 解释器扩展模块
5.3 脚本跟踪与分析
第6章 浏览器缓存
6.1 别忘了浏览器
6.2 缓存协商
6.3 彻底消灭请求
第7章 Web服务器缓存
7.1 URL映射
7.2 缓存响应内容
7.3 缓存文件描述符
第8章 反向代理缓存
8.1 传统代理
8.2 何为反向
8.3 在反向代理上创建缓存
8.4 小心穿过代理
8.5 流量分配
第9章 Web组件分离
9.1 备受争议的分离
9.2 因材施教
9.3 拥有不同的域名
9.4 浏览器并发数
9.5 发挥各自的潜力
第10章 分布式缓存
10.1 数据库的前端缓存区
10.2 使用memcached
10.3 读操作缓存
10.4 写操作缓存
10.5 监控状态
10.6 缓存扩展
第11章 数据库性能优化
11.1 友好的状态报告
11.2 正确使用索引
11.3 锁定与等待
11.4 事务性表的性能
11.5 使用查询缓存
11.6 临时表
11.7 线程池
11.8 反范式化设计
11.9 放弃关系型数据库
第12章 Web负载均衡
12.1 一些思考
12.2 HTTP重定向
12.3 DNS负载均衡
12.4 反向代理负载均衡
12.5 IP负载均衡
12.6 直接路由
12.7 IP隧道
12.8 考虑可用性
第13章 共享文件系统
13.1 网络共享
13.2 NFS
13.3 局限性
第14章 内容分发和同步
14.1 复制
14.2 SSH
14.3 WebDAV
14.4 rsync
14.5 Hashtree
14.6 分发还是同步
14.7 反向代理
第15章 分布式文件系统
15.1 文件系统
15.2 存储节点和追踪器
15.3 MogileFS
第16章 数据库扩展
16.1 复制和分离
16.2 垂直分区
16.3 水平分区
第17章 分布式计算
17.1 异步计算
17.2 并行计算
第18章 性能监控
18.1 实时监控
18.2 监控代理
18.3 系统监控
18.4 服务监控
18.5 响应时间监控