瓶颈是什么?
一条4车道的公路,运行非常顺畅,突然出了点事故,事故车导致某个地方只剩下1车道,然后就开始堵车,因为四辆车同时塞向一个车道里。把这个事故清除了,故障车拖走了,道路会开始恢复了通畅。
这个道理谁都懂,但偏偏有些傻瓜交警去把4车道变成8车道,但却不清理事故路段。
所以Web网站优化三部曲:应用程序优化、系统结构优化、网络优化。
一个Web应用,不管是何种语言开发,粗略的结构无非是三层:
1. 页面模板
可以是JSP、ASP、PHP等页面技术,根据数据生成最终的HTML页面,性能关键指标只有一个,页面的渲染速度。综合各种页面技术而言,渲染速度相差不会太大,10倍以内。
2. 业务逻辑
用于根据业务需要将数据库中的数据读取到内存中,以便通过页面模板渲染成HTML页面。这里面可能还包括缓存、连接池等技术。
3. 数据库
就是数据库,负责执行SQL查询并返回查询结果。
我们假设用户访问一个页面,也就是请求一个URL地址,然后得到内容,所需要的时间是3秒钟。其中大部分时间可能用在网络传输上,而真正页面执行并生成HTML内容所需的时间是很小的,这里假设需要100毫秒。
相当于用户花了两秒多钟在传输数据上,这部分时间如果能缩减,可以大大提升访问的速度,但是这部分一般也难以提升了,因为取决于用户本身的网络情况,服务器的网络情况以及中间整个路由的情况。对于一个网站来说,能做的就是尽可能的提升服务器的带宽,或者使用CDN来减少中间路由环节,很不幸的是,这个成本很高。
好吧,前面提到的更多是非技术因素,假设你已经耗费巨资解决了这个问题,然后突然发现网络太快了,可是服务器顶不住了,生成一个页面居然要100毫秒,才几十个并发用户就差点要把服务器搞崩溃了。
于是来到了本文的重点部分——找出应用的性能瓶颈。
前面我们提到的结构中的三层:页面模板,业务逻辑和数据库,根据经验值,在这100毫秒中,三个部分占用的时间差不多为:页面模板(5%)、业务逻辑+数据库(95%)。
几个准则:
1. 没必要去优化页面模板,这都是一些很成熟的技术,就算你好不容易提升了10%的性能,这10%在整个页面的执行过程中只占了0.5%的比例,微乎其微,等于是前面例子中的4车道变8车道的傻瓜,我们不要去充当傻瓜。
2. 一般瓶颈所在以及相应处理办法
简简单单的三条,里面却包含了很深的功夫,特别是在数据库查询优化上。
二、其次 系统结构优化
你必须在充分解决了这些应用程序所属的性能瓶颈之后,再去考虑系统级别的优化。
一些常用系统级别优化包括:
1. 静态文件和动态页面分开处理
2. 应用服务器的集群
3. 数据库的集群
不要本末倒置,一个性能很差的应用程序,你就算集群了100个节点,也不会有什么效果。
因为反向代理服务器带来的灵活性,它也成为了很多其它性能提升方法的先决条件,比如:
增加一个负载均衡器是一个相对简单的改动,而且会大幅度地改善网站的性能和安全性。你可以利用负载均衡器把业务分配给一些服务器,而不是建造一台更大更强的 Web 核心服务器。就算应用程序编写得很烂或者扩展性很差,负载均衡器都能提升用户体验而不需要任何其它的改动。
负载均衡器首先是一个反向代理服务器(查看建议一)—— 它接收网络流量,并把请求转交给另一个服务器。一个窍门就是让负载均衡器支持两台以上的应用服务器,利用一个选择算法在服务器间分配请求。最简单的方法就是轮询,每个新请求发送给列表中的下一台服务器。其它方法包括把请求发送给活动连接数量最少的服务器。NGINX Plus 可以在同一台服务器上维持一个给定的用户会话,这个功能被称为会话持久性。
缓存通过更快地向客户端提供内容来改善 Web 应用的性能。缓存包含一些策略:对内容预处理以便更快地发布、在更快的设备上保存内容、在更靠近客户端的地方保存内容,或者上述方法的组合。
有两种不同类型的缓存需要考虑:
比如一个网页每秒有十次访问,把它缓存 1 秒钟,这个网页 90% 的请求都可以用缓存满足。如果你单独缓存静态内容,甚至最新生成的网页也会大量包含这些缓存的内容。
Web 应用生成缓存内容主要有三种技术:
设置 Web 应用的缓存从 Web 应用服务器开始,是从内到外来实现的。首先,缓存动态内容,减轻了应用服务器的负担。接下来,缓存静态内容(包括原本是动态内容的临时副本),进一步分担应用服务器的负担。然后把缓存从应用服务器移到更快的、距离用户更近的电脑上,这样卸下了应用服务器的负担,减少了检索和传输的时间。
提高缓存可以大大加快应用程序。大多数网页中,一半以上的内容都是静态数据(比如大的图像文件)。在没有缓存的情况下,检索和传输数据可能要花费好几秒钟,但如果数据缓存在本地,只需要几分之一秒就可以。
压缩是一个有巨大潜能的性能加速器。已经有很多精心设计和高效的压缩标准,有针对图像的(JPEG 和 PNG)、视频的(MPEG-4)、音乐的(MP3)等等。这些标准都可以大幅减少文件的大小。
文本数据 —— 包含 HTML(包含了纯文本和 HTML 标签)、CSS 和类似 JavaScript 的代码,这些数据通常不经过压缩就进行传输了。压缩这些数据会大大改善对 Web 应用性能的体验,特别是那些连接缓慢或受限的移动客户端。
这是因为用户在和网页交互时,文本数据通常已经足够了,而多媒体数据就需要更多的支持。智能内容压缩可以减少 HTML、Javascript、CSS 和其它文本内容对带宽的要求,通常是 30% 或者更多,从而减少加载时间。
如果使用 SSL,压缩可以减少 SSL 加密的数据量,从而减少一些 CPU 时间。(译者注:SSL,Security Socket Layer,加密套接字层,一种加密的通讯协议,用在客户端与服务器之间。参考建议五。)
加密套接字层(SSL)协议及其后继者 —— 传输层安全(TLS)协议,被越来越多得的网站所采用。SSL/TLS 加密了服务器发送给用户的数据,提升了网站的安全性。影响这一趋势的部分原因是,Google 现在提升了启用 HTTPS 网站的搜索排名。
尽管 SSL/TLS 越来越普遍,它们却是影响许多网站性能的症结所在。SSL/TLS 降低网站性能有两个原因:
能用在每个 Web 应用上的性能提升方法千差万别,并且最后的效果也取决于预算、付出的时间和已有的实现等等。那么如何让你自己的应用达到性能提升 10 倍的目标呢?
虽然你们遇到的情况肯定会不一样,为了帮助理解每种优化方法的影响,这里列出一些上面详细讨论的要点:
我们希望你自己尝试下这些技术。我们希望听到你在某个应用程序上提升了性能。你可以在下面的评论中分享你的结果,或者把你的故事用 #NGINX 和 #webperf 标签推送到 twitter 上!
三、网络优化
增加服务器带宽,cup性能、内存等
各种性能瓶颈及其对策
前端性能问题
雅虎专门研究网页性能问题的工程师发现,一个页面从请求到加载完,80%的时间都花在前端上(http://developer.yahoo.com/blogs/ydn/posts/2007/03/high_performanc/)。事实也是如此,以图一为例,整个页面完全加载完花费了12s的时间,而服务器的响应时间只有391ms,其他时间都花在获取页面静态文件(如JS、CSS、图片等文件)上了。所以优化一个网站首先应从前端性能优化下手。
对策1:使用多域名增加最大并发数
仔细分析图一中的“域”,可以发现“tp1.sinaimg.cn”、“tp3.sinaimg.cn”这样的域名。这样做可以成倍的增加并发数。因为浏览器只对单个域名限制并发数,而不对单个IP限制并发数,所以可将一个IP地址映射到多个域名,然后使用这些域名访问网站资源,这样原本浏览器的并发数为6,使用两个域名并发数就可以达到12个了。但需要注意的是,域名并不是越多就越好的,因为域名解析也需要花费时间,而且并发数太多也会耗费客户端太多的CPU,域名数量到了一定程度,网页性能就会开始下降,所以在应用中需要根据实际情况寻找一个平衡点。如果不是特别需要,一般2到4个为佳。
对策2:通过文件压缩等方式降低单个文件的大小
对于JS文件,可以通过工具对其进行压缩,去除不必要的空格、换行符等。对于图片文件,优先考虑使用CSS来代替,实在不行可以考虑对图片进行裁剪。对于页面文件,尽可能使用Html标签而不使用服务器控件以减少ViewState长度,将内联的javascript和CSS放到单独的文件中,尽可能使用长度较小的字符串来作为控件ID值。
对策3:合并JS和CSS文件减少并发数
如果将两个JS文件合并不至于对项目维护造成影响,那么最好将它们合并。
对策4:使用图片延迟加载技术减少并发数
所谓图片延迟加载,就是每次只加载当前屏幕可见区域的图片,其余的图片在用户滚动页面到该位置后才开始加载。这是一项非常实用的技术,减少了并发数,不但减少了服务器的压力,也降低了页面的加载时间,目前很多门户网站都使用了该技术,如腾讯微博的“看看推荐的人”页面,在该页面上有几千个头像,如果一次性加载全部的图片,就要耗费比较多的客户端和服务器端的资源。该功能的实现原理很简单,就是将页面上的src替换成其他标记(如original),在页面滚动到相应位置后,再将original更改为src。目前有个Jquery插件Jquery.LazyLoad.js可以实现图片的延迟加载,使用方法如下所示:
不过该插件并没有真正的减少图片延迟加载,因为执行js是在页面的page_load之后,所以实际上打开页面后,图片已经全部下载到客户端,只是因为src属性被替换成original而没有显示起来。针对这种情况,网上有人提供了解决方案:将aspx页面上的src替换成original,这样保证page_load时绝对不会请求图片文件,然后Jquery.LazyLoad.js文件第62行的代码$(self).attr("original", $(self).attr("src"));修改为$(self).attr("original", $(self).attr("original"));
服务器端性能问题
影响服务器端的性能是多方面的,包括软件架构、服务器硬件配置等等各方面,如果编码的时候多注意一下,也能够给性能提升带来帮助。下面仅摘取几条记录如下:
1) 尽量使用List而不使用DataSet。
2) 使用<%# ((MyClass)Container.DataItem).field1 %>替代<%# Eval("field1")%>,因为Eval需通过反射识别field的类型,再进行数据转换,从而影响效率。
3) 涉及到字符串连接,使用StringBuilder类型,但是如果字符串连接操作发生在一个语句上,则不要使用StringBuilder。如:s = s1 + s2 + s3 + s4。