大型高并发高负载网站的系统架构

一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。


一、国外大型IT网站的成功之道

(一)MySpace

今天,MySpace已经成为全球众口皆碑的社区网站之王。尽管一流和营销和管理经验自然是每个IT企业取得成功的首要因素,但是本节中我们却抛弃这一点,而主要着眼于探讨在数次面临系统扩张的紧急关头MySpace是如何从技术方面采取应对策略的。

第一代架构—添置更多的Web服务器

MySpace最初的系统很小,只有两台Web服务器(分担处理用户请求的工作量)和一个数据库服务器(所有数据都存储在这一个地方)。那时使用的是Dell双CPU、4G内存的系统。在早期阶段,MySpace基本是通过添置更多Web服务器来对付用户暴增问题的。但到在2004年早期,在MySpace用户数增长到五十万后,其数据库服务器已经开始疲于奔命了。

第二代架构—增加数据库服务器

与增加Web服务器不同,增加数据库并没那么简单。如果一个站点由多个数据库支持,设计者必须考虑的是,如何在保证数据一致性的前提下让多个数据库分担压力。

MySpace运行在三个SQLServer数据库服务器上—一个为主,所有的新数据都向它提交,然后由它复制到其它两个;另两个数据库服务器全力向用户供给数据,用以在博客和个人资料栏显示。这种方式在一段时间内效果很好——只要增加数据库服务器,加大硬盘,就可以应对用户数和访问量的增加。

这一次的数据库架构按照垂直分割模式设计,不同的数据库服务于站点的不同功能,如登录、用户资料和博客。垂直分割策略利于多个数据库分担访问压力,当用户要求增加新功能时,MySpace只需要投入新的数据库加以支持。在账户到达二百万后,MySpace还从存储设备与数据库服务器直接交互的方式切换到SAN(存储区域网络)—用高带宽、专门设计的网络将大量磁盘存储设备连接在一起,而数据库连接到SAN。这项措施极大提升了系统性能、正常运行时间和可靠性。然而,当用户继续增加到三百万后,垂直分割策略也变得难以维持下去。

第三代架构—转到分布式计算架构

几经折腾,最终,MySpace将目光移到分布式计算架构——它在物理上分布的众多服务器,整体必须逻辑上等同于单台机器。拿数据库来说,就不能再像过去那样将应用拆分,再以不同数据库分别支持,而必须将整个站点看作一个应用。现在,数据库模型里只有一个用户表,支持博客、个人资料和其他核心功能的数据都存储在相同数据库。

既然所有的核心数据逻辑上都组织到一个数据库,那么MySpace必须找到新的办法以分担负荷——显然,运行在普通硬件上的单个数据库服务器是无能为力的。这次,不再按站点功能和应用分割数据库,MySpace开始将它的用户按每百万一组分割,然后将各组的全部数据分别存入独立的SQLServer实例。目前,MySpace的每台数据库服务器实际运行两个SQL Server实例,也就是说每台服务器服务大约二百万用户。据MySpace的技术人员说,以后还可以按照这种模式以更小粒度划分架构,从而优化负荷分担。

第四代架构—求助于微软方案

2005年早期,账户达到九百万,MySpace开始用微软的C#编写ASP.NET程序。在收到一定成效后,MySpace开始大规模迁移到ASP.NET。

账户达到一千万时,MySpace再次遭遇存储瓶颈问题。SAN的引入解决了早期一些性能问题,但站点目前的要求已经开始周期性超越SAN的I/O容量——即它从磁盘存储系统读写数据的极限速度。

第五代架构—增加数据缓存层并转到支持64位处理器的SQLServer 2005

2005年春天,MySpace账户达到一千七百万,MySpace又启用了新的策略以减轻存储系统压力,即增加数据缓存层——位于Web服务器和数据库服务器之间,其唯一职能是在内存中建立被频繁请求数据对象的副本,如此一来,不访问数据库也可以向Web应用供给数据。

2005年中期,服务账户数达到两千六百万时,MySpace因为我们对内存的渴求而切换到了还处于beta测试的支持64位处理器的SQLServer 2005。升级到SQL Server 2005和64位Windows Server2003后,MySpace每台服务器配备了32G内存,后于2006年再次将配置标准提升到64G。

事实上,MySpace的Web服务器和数据库仍然经常发生超负荷,其用户频繁遭遇“意外错误”和“站点离线维护”等告示,他们不得不在论坛抱怨不停……

MySpace正是在这样不断重构站点软件、数据库和存储系统中,才一步步走到今天。事实上,MySpace已经成功解决了很多系统扩展性问题,其中存在相当的经验值得我们借鉴。MySpace系统架构到目前为止保持了相对稳定,但其技术人员仍然在为SQLServer支持的同时连接数等方面继续攻坚,尽可能把事情做到最好。


二、搭建科学的系统架构

大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。

上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。

1、HTML静态化

其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。

除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。

同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

2、图片服务器分离

大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。

3、数据库集群和库表散列

大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。

上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。

4、缓存

缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。

架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。

网站程序开发方面的缓存,Linux上提供的MemoryCache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。

5、镜像

镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异。镜像网站是指将一个完全相同的站点放到几个服务器上,分别有自己的URL,这些服务器上的网站互相称为镜像网站。镜像网站和主站并没有太大差别,或者可以视为主站的拷贝。镜像网站的好处是:如果不能对主站作正常访问(如服务器故障,网络故障或者网速太慢等),仍能通过镜像服务器获得服务。不便之处是:更新网站内容的时候,需要同时更新多个服务器;需要用户记忆超过一个网址,或需要用户选择访问多个镜像网站中的一个,而用户选择的,不一定是最优的。在用户选择的过程中,缺乏必要的可控性。

在互联网发展的初期,互联网上的网站内容很少,而且大都是静态内容,更新频率底。但因为服务器运算能力低,带宽小,网速慢,热门网站的访问压力还是很大。镜像网站技术在这种情况下作为一种有效解决方案,被广泛采用。随着互联网的发展,越来越多的网站使用服务器端脚本动态生成内容,同步更新越来越困难,对可控性要求越来越高,镜像技术因为不能满足这类网站的需要,渐渐的淡出了人们的视线。

6CDN

CDN的全称是Content DeliveryNetwork,即内容分发网络。其目的是通过在现有的互联网中增加一层新的网络架构,将网站的内容发布到最接近用户的网络“边缘”,使用户可以就近取得所需的内容,分散服务器的压力,解决互联网拥挤的状况,提高用户访问网站的响应速度。从而解决由于网络带宽小、用户访问量大、网点分布不均等原因所造成的用户访问网站响应速度慢的问题。

CDN与镜像网站技术的不同之处在于网站代替用户去选择最优的内容服务器,增强了可控制性。CDN其实是夹在网页浏览者和被访问的服务器中间的一层镜像或者说缓存,浏览者访问时点击的还是服务器原来的URL地址,但是看到的内容其实是对浏览者来说最优的一台镜像服务器上的页面缓存内容。这是通过调整服务器的域名解析来实现的。使用CDN技术的域名解析服务器需要维护一个镜像服务器列表和一份来访IP到镜像服务器的对应表。当一个用户的请求到来的时候,根据用户的IP,查询对应表,得到最优的镜像服务器的IP地址,返回给用户。这里的最优,需要综合考虑服务器的处理能力,带宽,离访问者的距离远近等因素。当某个地方的镜像网站流量过大,带宽消耗过快,或者出现服务器,网络等故障的时候,可以很方便的设置将用户的访问转到另外一个地方(图2)。这样就增强了可控制性。

CDN网络加速技术也有它的局限性。首先,因为内容更新的时候,需要同步更新多台镜像服务器,所以它也只适用于内容更新不太频繁,或者对实时性要求不是很高的网站;其次,DNS解析有缓存,当某一个镜像网站的访问需要转移时,主DNS服务器更改了IP解析结果,但各地的DNS服务器缓存更新会滞后一段时间,这段时间内用户的访问仍然会指向该服务器,可控制性依然有不足。

目前,国内访问量较高的大型网站如新浪、网易等的资讯频道,均使用CDN网络加速技术(图3),虽然网站的访问量巨大,但无论在什么地方访问,速度都会很快。但论坛,邮箱等更新频繁,实时性要求高的频道,则不适合使用这种技术。

7、 交换层架构

7.1 第四层交换简介

按照OSI七层模型,第四层是传输层。传输层负责端到端通信,在IP协议栈中是TCP和UDP所在的协议层。TCP和UDP数据包中包含端口号(portnumber),它们可以唯一区分每个数据包所属的协议和应用程序。接收端计算机的操作系统根据端口号确定所收到的IP包类型,并把它交给合适的高层程序。IP地址和端口号的组合通常称作“插口(Socket)”。

第四层交换的一个简单定义是:它是一种传输功能,它决定传输不仅仅依据MAC地址(第二层网桥)或源/目标IP地址(第三层路由),而且依据IP地址与TCP/UDP(第四层)应用端口号的组合(Socket)。第四层交换功能就像是虚拟IP,指向实际的服务器。它传输的数据支持多种协议,有HTTP、FTP、NFS、Telnet等。

以HTTP协议为例,在第四层交换中为每个服务器组设立一个虚拟IP(VirtueIP,VIP),每组服务器支持某一个或几个域名。在域名服务器(DNS)中存储服务器组的VIP,而不是某一台服务器的真实地址。

当用户请求页面时,一个带有目标服务器组的VIP连接请求发送给第四层交换机。第四层交换机使用某种选择策略,在组中选取最优的服务器,将数据包中的目标VIP地址用实际服务器的IP地址取代,并将连接请求传给该服务器。第四层交换一般都实现了会话保持功能,即同一会话的所有的包由第四层交换机进行映射后,在用户和同一服务器间进行传输。

第四层交换按实现分类,分为硬件实现和软件实现

7.2 硬件实现

第四层交换的硬件实现一般都由专业的硬件厂商作为商业解决方案提供。常见的有Alteon,F5等。这些产品非常昂贵,但是能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了。鉴于条件关系,这里不展开讨论

7.3 软件实现

第四层交换也可以通过软件实现,不过性能比专业硬件稍差,但是满足一定量的压力还是可以达到的,而且软件实现配置起来更灵活。软件四层交换常用的有Linux上的LVS(Linux Virtual Server),它提供了基于心跳(heartbeat)的实时灾难应对解决方案,提高了系统的鲁棒性,同时提供了灵活的VIP配置和管理功能,可以同时满足多种应用需求。

8、 服务器优化

8.1 服务器整体性能考虑

对于价值昂贵的服务器来说,怎样配置才能发挥它的最大功效,又不至于影响正常的服务,这是在设计网站架构的时候必须要考虑的。常见的影响服务器的处理速度的因素有:网络连接,硬盘读写,内存空间,CPU速度。如果服务器的某一个部件满负荷运转仍然低于需要,而其他部件仍有能力剩余,我们将之称为性能瓶颈。服务器想要发挥最大的功效,关键的是消除瓶颈,让所有的部件都被充分的利用起来。

8.2 Socket优化

以标准的 GNU/Linux 为例。GNU/Linux发行版试图对各种部署情况都进行优化,这意味着对具体服务器的执行环境来说,标准的发行版可能并不是最优化的[23]。GNU/Linux提供了很多可调节的内核参数,可以使用这些参数为服务器进行动态配置,包括影响 Socket 性能的一些重要的选项。这些选项包含在 /proc虚拟文件系统中。这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 cat 工具进行读取,或使用 echo命令进行修改。这里仅列出一些影响TCP/IP 栈性能的可调节内核参数:

/proc/sys/net/ipv4/tcp_window_scaling“1”(1表示启用该选项,0表示关闭,下同) 启用 RFC 1323 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值。

/proc/sys/net/ipv4/tcp_sack“1”启用有选择的应答(SelectiveAcknowledgment),通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);对于广域网通信来说,这个选项应该启用,但是这也会增加对CPU 的占用。

/proc/sys/net/ipv4/tcp_timestamps“1” 以一种比重发超时更精确的方法(参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。

/proc/sys/net/ipv4/tcp_mem“24576 32768 49152” 确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限。第二个值是内存压力模式开始对缓冲区使用应用压力的上限。第三个值是内存上限。超过这个上限时可以将报文丢弃,从而减少对内存的使用。

/proc/sys/net/ipv4/tcp_wmem“4096 16384 131072” 为自动调优定义每个 socket 使用的内存。第一个值是为 socket的发送缓冲区分配的最少字节数。第二个值是默认值(该值会被 wmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。

/proc/sys/net/ipv4/tcp_westwood“1” 启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。

与其他调优努力一样,最好的方法实际上就是不断进行实验。具体应用程序的行为、处理器的速度以及可用内存的多少都会影响到这些参数对性能作用的效果。在某些情况中,一些认为有益的操作可能恰恰是有害的(反之亦然)。因此,需要逐一试验各个选项,然后检查每个选项的结果,最后得出最适合具体机器的一套参数。

如果重启了 GNU/Linux系统,设置的内核参数都会恢复成默认值。为了将所设置的值作为这些参数的默认值,可以使用 /etc/rc.local文件,在系统每次启动时自动将这些参数配置成所需要的值。

在检测每个选项的更改带来的效果的时候,GNU/Linux上有一些非常强大的工具可以使用:

ping这是用于检查主机的可用性的最常用的工具,也可以用于计算网络带宽延时。

traceroute打印连接到特定网络主机所经过的一系列路由器和网关的路径(路由),从而确定每个 hop 之间的延时。

netstat确定有关网络子系统、协议和连接的各种统计信息。

tcpdump显示一个或多个连接的协议级的报文跟踪信息,其中包括时间信息,可以使用这些信息来研究不同协议的报文时间。

Ethereal 以一个易于使用的图形化界面提供tcpump (报文跟踪)的信息,支持报文过滤功能。

iperf 测量 TCP 和 UDP的网络性能;测量最大带宽,并汇报延时和数据报的丢失情况。

8.3 硬盘级缓存

硬盘级别的缓存是指将需要动态生成的内容暂时缓存在硬盘上,在一个可接受的延迟时间范围内,同样的请求不再动态生成,以达到节约系统资源,提高网站承受能力的目的。Linux环境下硬盘级缓存一般使用Squid。

Squid是一个高性能的代理缓存服务器。和一般的代理缓存软件不同,Squid用一个单独的、非模块化的、I/O驱动的进程来处理所有的客户端请求。它接受来自客户端对目标对象的请求并适当地处理这些请求。比如说,用户通过浏览器想下载(即浏览)一个web页面,浏览器请求Squid为它取得这个页面。Squid随之连接到页面所在的原始服务器并向服务器发出取得该页面的请求。取得页面后,Squid再将页面返回给用户端浏览器,并且同时在Squid本地缓存目录里保存一份副本。当下一次有用户需要同一页面时,Squid可以简单地从缓存中读取它的副本,直接返回给用户,而不用再次请求原始服务器。当前的Squid可以处理HTTP,FTP, GOPHER, SSL和WAIS等协议。

Squid默认通过检测HTTP协议头的Expires和Cache-Control字段来决定缓存的时间。在实际应用中,可以显式的在服务器端脚本中输出HTTP头,也可以通过配置apache的mod_expires模块,让apache自动的给每一个网页加上过期时间。对于静态内容,如图片,视频文件,供下载的软件等,还可以针对文件类型(扩展名),用Squid 的 refresh_pattern 来指定缓存时间。

Squid运行的时候,默认会在硬盘上建两层hash目录,用来存储缓存的Object。它还会在内存中建立一个HashTable,用来记录硬盘中Object分布的情况。如果Squid配置成为一个Squid集群中的一个的话,它还会建立一个 DigestTable(摘要表),用来存储其它 Squid上的Object摘要。当用户端想要的资料本地硬盘上没有时,可以很快的知道应该去集群中的哪一台机器获得。在硬盘空间快要达到配置限额的时候,可以配置使用某种策略(默认使用LRU:LeastRecently Used-最近最少用)删除一些Object,从而腾出空间。

集群中的Squid Server 之间可以有两种关系:第一种关系是:Child 和Parent。当 Child Squid Server 没有资料时,会直接向 Parent Squid Server 要资料,然后一直等,直到 Parent给它资料为止。 第二种关系是:Sibling 和 Sibling。当 Squid Server 没有资料时,会先向 Sibling 的 SquidServer 要资料,如果 Sibling 没资料,就跳过它向 Parent 要或直接上原始网站去拿。

默认配置的Squid,没有经过任何优化的时候,一般可以达到 50%的命中率[30](图4)。如果需要,还可以通过参数优化,拆分业务,优化文件系统等办法,使得Squid达到 90% 以上的缓存命中率。Squid处理TCP连接消耗的服务器资源比真正的HTTP服务器要小的多,当Squid分担了大部分连接,网站的承压能力就大大增强了。

8.4 内存级缓存

内存级别的缓存是指将需要动态生成的内容暂时缓存在内存里,在一个可接受的延迟时间范围内,同样的请求不再动态生成,而是直接从内存中读取。Linux环境下内存级缓存Memcached是一个不错的选择。

Memcached是danga.com(运营LiveJournal[32]的技术团队)开发的一套非常优秀的分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。和 Squid的前端缓存加速不同,它是通过基于内存的对象缓存来减少数据库查询的方式改善网站的性能,而其中最吸引人的一个特性就是支持分布式部署;也就是说可以在一群机器上建立一堆Memcached 服务,每个服务可以根据具体服务器的硬件配置使用不同大小的内存块,这样,理论上可以建立一个无限大的基于内存的缓存系统。

Memcached是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端 API 包括Perl/PHP/Python/Ruby/Java/C#/C 等等[附录1]。客户端首先与 Memcached服务建立连接,然后存取对象。每个被存取的对象都有一个唯一的标识符 key,存取操作均通过这个 key 进行,保存的时候还可以设置有效期。保存在Memcached 中的对象实际上是放置在内存中的,而不是在硬盘上。Memcached 进程运行之后,会预申请一块较大的内存空间,自己进行管理,用完之后再申请一块,而不是每次需要的时候去向操作系统申请。Memcached将对象保存在一个巨大的Hash表中,它还使用NewHash算法来管理Hash表,从而获得进一步的性能提升。所以当分配给Memcached的内存足够大的时候,Memcached的时间消耗基本上只是网络Socket连接了。

Memcached也有它的不足。首先它的数据是保存在内存当中的,一旦服务进程重启(进程意外被关掉,机器重启等),数据会全部丢失。其次Memcached以root权限运行,而且Memcached本身没有任何权限管理和认证功能,安全性不足。第一条是Memcached作为内存缓存服务使用无法避免的,当然,如果内存中的数据需要保存,可以采取更改Memcached的源代码,增加定期写入硬盘的功能。对于第二条,我们可以将Memcached服务绑定在内网IP上,通过Linux防火墙进行防护。

8.5 CPU与IO均衡

在一个网站提供的所有功能中,有的功能可能需要消耗大量的服务器端IO资源,像下载,视频播放等,而有的功能则可能需要消耗大量的服务器CPU资源,像视频格式转换,LOG统计等。在一个服务器集群中,当我们发现某些机器上CPU和IO的利用率相差很大的时候,例如CPU负载很高而IO负责很低,我们可以考虑将该服务器上的某些耗CPU资源的进程换成耗IO的进程,以达到均衡的目的。均衡每一台机器的CPU和IO消耗,不仅可以获得更充分的服务器资源利用,而且还能够支持暂时的过载,遇到突发事件,访问流量剧增的时候,实现得体的性能下降(Graceful performance degradation),而不是立即崩溃。

8.6 读写分离

如果网站的硬盘读写性能是整个网站性能提升的一个瓶颈的话,可以考虑将硬盘的读,写功能分开,分别进行优化。在专门用来写的硬盘上,我们可以在Linux下使用软件RAID-0(磁盘冗余阵列0级)。RAID-0在获得硬盘IO提升的同时,也会增加整个文件系统的故障率——它等于RAID中所有驱动器的故障率之和。如果需要保持或提高硬盘的容错能力,就需要实现软件RAID-1,4或5,它们能在某一个(甚至几个)磁盘驱动器故障之后仍然保持整个文件系统的正常运行,但文件读写效率不如RAID-0。而专门用来读的硬盘,则不用如此麻烦,可以使用普通的服务器硬盘,以降低开销。

一般的文件系统,会综合考虑各种大小和格式的文件的读,写效率,因而对特定的文件读或写的效率不是最优。如果有必要,可以通过选择文件系统,以及修改文件系统的配置参数来达到对特定文件的读或写的效率最大化。比如说,如果文件系统中需要存储大量的小文件,则可以使用ReiserFS[37]来替代Linux操作系统默认的ext3系统,因为ReiserFS是基于平衡树的文件系统结构,尤其对于大量文件的巨型文件系统,搜索速度要比使用局部的二分查找法的ext3快。ReiserFS里的目录是完全动态分配的,因此不存在ext3中常见的无法回收巨型目录占用的磁盘空间的情况。ReiserFS里小文件(<4K)可以直接存储进树,小文件读取和写入的速度更快,树内节点是按字节对齐的,多个小文件可共享同一个硬盘块,节约大量空间。ext3使用固定大小的块分配策略,也就是说,不到4K的小文件也要占据4K的空间,导致的空间浪费比较严重。但ReiserFS对很多Linux内核支持的不是很好,包括2.4.3、2.4.9 甚至相对较新的2.4.16,如果网站想要使用它,就必须要安装与它配合的较好的2.4.18内核——一般管理员都不是很乐意使用太新的内核,因为在它上面运行的软件,都还没有经过大量的实践测试,也许有一些小的bug还没有被发现,但对于服务器来说,再小的bug也是不能接受的。ReiserFS还是一个较为年轻的,发展迅速的文件系统,它相对于ext3来说有一个很大的缺陷就是,每次ReiserFS文件系统升级的时候,必须完全重新格式化整个磁盘分区。所以在选择使用的时候,需要权衡取舍。

9、应用程序层优化

9.1 网站服务器程序的选择

经统计,当前互联网上有超过50%的网站主机使用Apache服务器程序。Apache是开源界的首选Web服务器,因为它的强大和可靠,而且适用于绝大部分的应用场合。但是它的强大有时候却显得笨重,配置文件复杂得让人望而生畏,高并发情况下效率不太高。而轻量级的Web服务器Lighttpd却是后起之秀,基于单进程多路复用技术,其静态文件的响应能力远高于Apache。Lighttpd对PHP的支持也很好,还可以通过Fastcgi方式支持其他的语言,比如Python等。虽然Lighttpd是轻量级的服务器,功能上不能跟Apache比,某些复杂应用无法胜任,但即使是大部分内容动态生成的网站,仍免不了会有一些静态元素,比如图片、JS脚本、CSS等等,可以考虑将Lighttpd放在Squid的前面,构成Lighttpd->Squid->Apache的一条处理链,Lighttpd在最前面,专门处理静态内容的请求,把动态内容请求通过Proxy模块转发给Squid,如果Squid中有该请求的内容且没有过期,则直接返回给Lighttpd。新请求或者过期的页面请求交由Apache中的脚本程序来处理。经过Lighttpd和Squid的两级过滤,Apache需要处理的请求大大减少,减少了Web应用程序的压力。同时这样的构架,便于把不同的处理分散到多台计算机上进行,由Lighttpd在前面统一分发。

在这种架构下,每一级都是可以进行单独优化的,比如Lighttpd可以采用异步IO方式,Squid可以启用内存来缓存,Apache可以启用MPM(Multi-Processing Modules,多道处理模块)等,并且每一级都可以使用多台机器来均衡负载,伸缩性好。

著名视频分享网站YouTube就是选择使用Lighttpd作为网站的前台服务器程序。

9.2 数据库选择

MySQL是一个快速的、多线程、多用户和健壮的SQL数据库服务器,支持关键任务、重负载系统的使用,是最受欢迎的开源数据库管理系统,是Linux下网站开发的首选。它由MySQLAB开发、发布和提供支持。

MySQL数据库能为网站提供:

高性能。MySQL支持海量,快速的数据库存储和读取。还可以通过使用64位处理器来获取额外的一些性能,因为MySQL在内部里很多时候都使用64位的整数处理。

易用性。MySQL的核心是一个小而快速的数据库。它的快速连接,快速存取和安全可靠的特性使MySQL非常适合在互联网站上使用。

开放性。MySQL提供多种后台存储引擎的选择,如MyISAM,Heap, InnoDB,Berkeley Db等。缺省格式为MyISAM。 MyISAM 存储引擎与磁盘兼容的非常好。

支持企业级应用。MySQL有一个用于记录数据改变的二进制日志。因为它是二进制的,这一日志能够快速地将数据的更改从一台机器复制(replication)到另一台机器上。即使服务器崩溃,这一二进制日志也能够保持完整。这一特性通常被用来搭建数据库集群,以支持更大的流量访问要求。

MySQL也有一些它自身的缺陷,如缺乏图形界面,缺乏存储过程,还不支持触发器,参照完整性,子查询和数据表视图等,但这些功能都在开发者的TO-DO列表当中。这就是开源的力量:你永远可以期待更好。

国外的Yahoo!,国内的新浪,搜狐等很多大型商业网站都使用MySQL作为后台数据库。对于一般的网站系统,无论从成本还是性能上考虑,MySQL应该是最佳的选择。

9.3 服务器端脚本解析器的选择

目前最常见的服务器端脚本有三种:ASP(Active ServerPages),JSP(Java Server Pages),PHP (Hypertext Preprocessor)。

ASP全名Active ServerPages,以及它的升级ASP.NET,是微软公司出品的一个WEB服务器端的开发环境,利用它可以产生和运行动态的、交互的、高性能的WEB服务应用程序。ASP采用脚本语言VBScript(C#)作为自己的开发语言。但因为只能运行在Windows环境下,这里我们不讨论它。

PHP是一种跨平台的服务器端的嵌入式脚本语言。它大量地借用C,Java和Perl语言的语法,并耦合PHP自己的特性,使WEB开发者能够快速地写出动态生成页面。它支持目前绝大多数数据库。PHP也是开源的,它的发行遵从GPL开源协议,你可以从PHP官方站点(http://www.php.net)自由下载到它的二进制安装文件及全部的源代码。如果在Linux平台上与MySQL搭配使用,PHP是最佳的选择。

JSP是Sun公司推出的新一代站点开发语言,是Java语言除Java应用程序和JavaApplet之外的第三个应用。Jsp可以在Serverlet和JavaBean的支持下,完成功能强大的站点程序。作为采用Java技术家族的一部分,以及Java2(企业版体系结构)的一个组成部分,JSP技术拥有Java技术带来的所有优点,包括优秀的跨平台性,高度可重用的组件设计,健壮性和安全性等,能够支持高度复杂的基于Web的应用。

除了这三种常见的脚本之外,在Linux下我们其实还有很多其他的选择:Python(Google使用),Perl等,如果作为CGI调用,那么可选择范围就更广了。使用这些不太常见的脚本语言的好处是,它们对于某些特殊的应用有别的脚本所不具有的优势;不好的地方是,这些脚本语言在国内使用的人比较少,当碰到技术上的问题的时候,能找到的资料也较少。

9.4 可配置性

在大型网站开发过程中,不管使用什么技术,网站的可配置性是必须的。在网站的后期运营过程中,肯定会有很多的需求变更。如果每一次的需求变更都会导致修改源代码,那么,这个网站的开发可以说是失败的。

首先,也是最重要的一点,功能和展示必须分开。PHP和JSP都支持模板技术,如PHP的Smarty,Phplib,JSP的JSTL(JSPStandard Tag Library)等。核心功能使用脚本语言编写,前台展示使用带特殊标签的HTML,不仅加快了开发速度,而且方便以后的维护和升级。

其次,对于前台模板,一般还需要将页面的头,尾单独提取出来,页面的主体部分也按模块或者功能拆分。对CSS,JS等辅助性的代码,也建议以单独的文件形式存放。这样不仅方便管理,修改,而且还可以在用户访问的时候进行缓存,减少网络流量,减轻服务器压力。

再次,对于核心功能脚本,必须将与服务器相关的配置内容,如数据库连接配置,脚本头文件路径等,与代码分离开。尤其当网站使用集群技术,CDN加速等技术的时候,每一台服务器上的配置可能都会不一样。如果不使用配置文件,则需要同时维护几份不同的代码,很容易出错。

最后,应该尽量做到修改配置文件后能实时生效,避免修改配置文件之后需要重启服务程序的情况。

9.5 封装和中间层思想

在功能块层次,如果使用JSP,基于纯面向对象语言Java的面向对象思想,类似数据库连接,会话管理等基本功能都已经封装成类了。如果使用PHP,则需要在脚本代码中显式的封装,将每一个功能块封装成一个函数,一个文件或者一个类。

在更高的层次,可以将网站分为表示层,逻辑层,持久层,分别进行封装,做到当某一层架构发生变化时,不会影响到其他层。比如新浪播客在一次升级的时候,将持久层的数据库由原来的集中式改为分布式架构,因为封装了数据库连接及所有操作,做到了不修改任何上层代码,平稳的实现了过渡。近来流行的MVC架构,将整个网站拆分成Model(模型/逻辑)、View(视图/界面)、Controller(控制/流程)三个部分,而且有很多优秀的代码框架可供选择使用,像JSP的Structs,Spring,PHP的php.MVC, Studs 等。使用现成的代码框架,可以使网站开发事半功倍。

10、扩容、容错处理

10.1 扩容

一个大型网站,在设计架构的时候,必须考虑到以后可能的容量扩充。新浪播客在设计时充分地考虑了这一点。对于视频分享类网站来说,视频存储空间消耗是巨大的。新浪播客在主存储服务器上,采用配置文件形式指定每一个存储盘柜上存储的视频文件的ID范围。当前台服务器需要读取一个视频的时候,首先通过询问主存储服务器上的接口获得该视频所在的盘柜及目录地址,然后再去该盘柜读取实际的视频文件。这样如果需要增加存储用的盘柜,只需要修改配置文件即可,前台程序丝毫不受影响。

新浪播客采用MySQL数据库集群,在逻辑层封装了所有的数据库连接及操作。当数据库存储架构发生改变的时候,如增加一台主库,将某些数据表独立成库,增加读取数据用的从库等,都只需要修改封装了的数据库操作类,上层代码不用修改。

新浪播客的前台页面服务器使用F5公司的硬件第四层交换机,网通,电信分别导向不同的虚拟IP,每一个虚拟IP后面又有多个服务器提供服务。当访问流量增大的时候,可以很方便往虚拟IP后面增加服务器,分担压力。

10.2 容错

对于商业性网站来说,可用性是非常重要的。7*24的访问要求网站具有很强的容错能力。错误包括网络错误,服务器错误以及应用程序错误。

2006年12月27日台湾东部外海发生里氏7.6级地震,造成途径台湾海峡的多条海底电缆中断,导致许多国外网站,像MSN,NBA,Yahoo!(英文主站)等国内无法访问,但也有例外,以Google为代表的在国内建设有分布式数据节点的很多网站却仍然可以访问。虽然说地震造成断网是不可抗原因,但如果在这种情况下网站仍然可以访问,无疑能给网站用户留下深刻的印象。这件事情给大型商业网站留下的教训是:网站需要在用户主要分布区域保持数据存在,以防止可能的网络故障。

对于服务器错误,一般采取冗余设计的方法来避免。对于存储服务器(主要是负责写入的服务器),可以使用RAID(冗余磁盘阵列);对于数据库(主要是负责写入的主库),可以采用双主库设计[30];对于提供服务的前台,则可以使用第四层交换的集群,由多台服务器同时提供服务,不仅分担了流量压力,同时还可以互相作为备份。

在应用层程序中,也要考虑“用户友好”的出错设计。典型例子如HTTP 404出错页面,程序内部错误处理,错误返回提示等,尽可能的做到人性化。


三、总结

对于一个高并发高流量的网站来说,任何一个环节的瓶颈都会造成网站性能的下降,影响用户体验,进而造成巨大的经济损失。在全互联网层面,应该使用分布式设计,缩短网站与用户的网络距离,减少主干网上的流量,以及防止在网络意外情况下网站无法访问的问题。在局域网层面,应该使用服务器集群,一方面可以支撑更大的访问量,另一方面也作为冗余备份,防止服务器故障导致的网站无法访问。在单服务器层面,应该配置操作系统,文件系统及应用层软件,均衡各种资源的消耗,消除系统性能瓶颈,充分发挥服务器的潜能。在应用层,可以通过各种缓存来提升程序的效率,减少服务器资源消耗。另外,还需要合理设计应用层程序,为以后的需求变更,扩容做好准备。

中国电子商务真正理性发展的一个标志,是大量的传统企业实实在在地开始用互联网来处理商务、做生意,而现在这样的浪潮已经开始。北京发行集团,联合SINA、6688.com等单位共同推出的网上虚拟书店—新新书店就是这样的一个标志。

随着网络带宽日渐提升,随着网络理念和WEB2.0技术的断深入人心,各种B2B、B2C、C2C等电子商务模式很可能以立体交叉方式整合到各种大型商务网站中来。因此,作为公司的技术人员,作为临危救驾的“白衣骑士”,如何应对海量存储、海量访问问题,海量信息检索的问题,日益严峻的安全问题,等等,已经刻不容缓。

你可能感兴趣的:(大型高并发高负载网站的系统架构)