Web应用的性能优化思路——找到瓶颈

瓶颈是什么?

一条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个节点,也不会有什么效果。

4、利用反向代理服务器加速和保护应用

因为反向代理服务器带来的灵活性,它也成为了很多其它性能提升方法的先决条件,比如:

  • 负载均衡(查看 建议二)—— 反向代理服务器上运行一个负载均衡器,把流量平均分配给一堆应用服务器。由于负载均衡器的引入,在增加应用服务器时可以完全不用修改应用程序。
  • 缓存静态文件(查看 建议三)—— 直接请求的文件,比如图片或者代码文件,可以存在反向代理服务器上,并直接发送给客户端,这样可以更快地提供服务,分担了应用服务器的负载,可以让应用执行得更快。
  • 保护网站 —— 反向代理服务器可以设置较高的安全级别,通过监控进快速识别和响应攻击,这样就可以把应用服务器保护起来。
NGINX 软件是专门设计用做反向代理服务器的,具有上述这些附加功能。NGINX 利用事件驱动处理的方法,比其它传统的服务器更加高效。NGINX Plus 增加了更多反向代理的高级功能和支持,包含应用程序健康检查、特定请求路由和高级缓存等

5、增加一个负载均衡器

增加一个负载均衡器是一个相对简单的改动,而且会大幅度地改善网站的性能和安全性。你可以利用负载均衡器把业务分配给一些服务器,而不是建造一台更大更强的 Web 核心服务器。就算应用程序编写得很烂或者扩展性很差,负载均衡器都能提升用户体验而不需要任何其它的改动。

负载均衡器首先是一个反向代理服务器(查看建议一)—— 它接收网络流量,并把请求转交给另一个服务器。一个窍门就是让负载均衡器支持两台以上的应用服务器,利用一个选择算法在服务器间分配请求。最简单的方法就是轮询,每个新请求发送给列表中的下一台服务器。其它方法包括把请求发送给活动连接数量最少的服务器。NGINX Plus 可以在同一台服务器上维持一个给定的用户会话,这个功能被称为会话持久性。

6、缓存静态和动态内容

缓存通过更快地向客户端提供内容来改善 Web 应用的性能。缓存包含一些策略:对内容预处理以便更快地发布、在更快的设备上保存内容、在更靠近客户端的地方保存内容,或者上述方法的组合。

有两种不同类型的缓存需要考虑:

  • 静态内容的缓存。不经常变化的文件,比如图像文件(JPEG,PNG)和代码文件(CSS,JavaScript),可以存在一个边缘服务器上,以便在内存或磁盘上进行快速检索。
  • 动态内容的缓存。很多 Web 应用为每个页面请求生成新的 HTML。通过简单地将已经生成 HTML的副本保存一小段时间,就可以大幅度减少需要生成页面的总数,发布这些已经生成的 HTML 副本已经足够满足需求了。

比如一个网页每秒有十次访问,把它缓存 1 秒钟,这个网页 90% 的请求都可以用缓存满足。如果你单独缓存静态内容,甚至最新生成的网页也会大量包含这些缓存的内容。

Web 应用生成缓存内容主要有三种技术:

  • 让内容更靠近用户。内容副本靠近用户,可以减少传输时间。
  • 把内容存在更快的电脑上。内容可以保存在更快的电脑上以便加快检索。
  • 把内容移出过载的电脑。有时候电脑运行一个特定任务比基准性能要慢,这是因为它同时还在忙其他任务。把缓存设置在另一台电脑上,都能提升有缓存资源和没有缓存资源的性能,因为这台主机不再过载了。

设置 Web 应用的缓存从 Web 应用服务器开始,是从内到外来实现的。首先,缓存动态内容,减轻了应用服务器的负担。接下来,缓存静态内容(包括原本是动态内容的临时副本),进一步分担应用服务器的负担。然后把缓存从应用服务器移到更快的、距离用户更近的电脑上,这样卸下了应用服务器的负担,减少了检索和传输的时间。

提高缓存可以大大加快应用程序。大多数网页中,一半以上的内容都是静态数据(比如大的图像文件)。在没有缓存的情况下,检索和传输数据可能要花费好几秒钟,但如果数据缓存在本地,只需要几分之一秒就可以。

7、压缩数据

压缩是一个有巨大潜能的性能加速器。已经有很多精心设计和高效的压缩标准,有针对图像的(JPEG 和 PNG)、视频的(MPEG-4)、音乐的(MP3)等等。这些标准都可以大幅减少文件的大小。

文本数据 —— 包含 HTML(包含了纯文本和 HTML 标签)、CSS 和类似 JavaScript 的代码,这些数据通常不经过压缩就进行传输了。压缩这些数据会大大改善对 Web 应用性能的体验,特别是那些连接缓慢或受限的移动客户端。

这是因为用户在和网页交互时,文本数据通常已经足够了,而多媒体数据就需要更多的支持。智能内容压缩可以减少 HTML、Javascript、CSS 和其它文本内容对带宽的要求,通常是 30% 或者更多,从而减少加载时间。

如果使用 SSL,压缩可以减少 SSL 加密的数据量,从而减少一些 CPU 时间。(译者注:SSL,Security Socket Layer,加密套接字层,一种加密的通讯协议,用在客户端与服务器之间。参考建议五。)

8、优化 SSL/TLS

加密套接字层(SSL)协议及其后继者 —— 传输层安全(TLS)协议,被越来越多得的网站所采用。SSL/TLS 加密了服务器发送给用户的数据,提升了网站的安全性。影响这一趋势的部分原因是,Google 现在提升了启用 HTTPS 网站的搜索排名。

尽管 SSL/TLS 越来越普遍,它们却是影响许多网站性能的症结所在。SSL/TLS 降低网站性能有两个原因:

  1. 每当打开一个新的连接,最初的握手都需要建立加密密钥。浏览器使用 HTTP/1.x 和服务器建立多条连接,随着服务器的增多,连接会成倍增加。
  2. 服务器上加密数据,客户端解密数据,这些都是持续的开销。

总结:如何看到性能提升 10 倍

能用在每个 Web 应用上的性能提升方法千差万别,并且最后的效果也取决于预算、付出的时间和已有的实现等等。那么如何让你自己的应用达到性能提升 10 倍的目标呢?

虽然你们遇到的情况肯定会不一样,为了帮助理解每种优化方法的影响,这里列出一些上面详细讨论的要点:

  • 反向代理服务器和负载均衡。如果没有做负载均衡,或者负载均衡做得很烂,都会造成性能很差。增加一个反向代理服务器,比如 NGINX,就可以防止 Web 应用在内存和磁盘间往复切换。负载均衡可以将处理从过载的服务器移到其他可用的服务器上,并且很容易进行扩展。这些改动可以大幅度地提升性能,和现在实现的最差情况相比,很容易实现 10 倍的性能提升,但实质上整体性能的提升可能没有这么大。
  • 缓存动态和静态内容。如果有一个服务器已经过载了,它既是 Web 服务器又是应用服务器,通过缓存动态内容就可以在峰值时刻提升 10 倍的性能。缓存静态文件也能实现个位数字的性能提升。
  • 压缩数据。利用多媒体文件的压缩格式,比如图片采用 JPEG 格式、图像采用 PNG 格式、电影采用 MPEG-4 格式、音乐采用 MP3 格式,这样就能在很大程度上提升性能。一旦这些格式都用上,压缩文本数据(代码和 HTML)的页面加载速度可以提升 2 倍。
  • 优化 SSL/TLS。安全握手对性能有很大影响,所以优化它们可以带来 2 倍的改善,特别是文本很多的网站。在 SSL/TLS 条件下,优化多媒体文件改善很小。
  • 实现 HTTP/2 和 SPDY。当和 SSL/TLS 一起使用时,这些协议会让整个网站的性能大幅度地提升。
  • 优化 Linux 和 Web 服务器软件(例如 NGINX)。优化缓冲区、保持连接、将耗时的任务分散到一个独立的线程池上都能大幅提升性能。比如线程池运用在对磁盘操作频繁的任务上会带来指数级的提速。

我们希望你自己尝试下这些技术。我们希望听到你在某个应用程序上提升了性能。你可以在下面的评论中分享你的结果,或者把你的故事用 #NGINX 和  #webperf 标签推送到 twitter 上!



三、网络优化

增加服务器带宽,cup性能、内存等



 各种性能瓶颈及其对策

前端性能问题

雅虎专门研究网页性能问题的工程师发现,一个页面从请求到加载完,80%的时间都花在前端上(http://developer.yahoo.com/blogs/ydn/posts/2007/03/high_performanc/)。事实也是如此,以图一为例,整个页面完全加载完花费了12s的时间,而服务器的响应时间只有391ms,其他时间都花在获取页面静态文件(如JSCSS、图片等文件)上了。所以优化一个网站首先应从前端性能优化下手。

对策1:使用多域名增加最大并发数

仔细分析图一中的“域”,可以发现“tp1.sinaimg.cn”、“tp3.sinaimg.cn”这样的域名。这样做可以成倍的增加并发数。因为浏览器只对单个域名限制并发数,而不对单个IP限制并发数,所以可将一个IP地址映射到多个域名,然后使用这些域名访问网站资源,这样原本浏览器的并发数为6,使用两个域名并发数就可以达到12个了。但需要注意的是,域名并不是越多就越好的,因为域名解析也需要花费时间,而且并发数太多也会耗费客户端太多的CPU,域名数量到了一定程度,网页性能就会开始下降,所以在应用中需要根据实际情况寻找一个平衡点。如果不是特别需要,一般24个为佳。

对策2:通过文件压缩等方式降低单个文件的大小

对于JS文件,可以通过工具对其进行压缩,去除不必要的空格、换行符等。对于图片文件,优先考虑使用CSS来代替,实在不行可以考虑对图片进行裁剪。对于页面文件,尽可能使用Html标签而不使用服务器控件以减少ViewState长度,将内联的javascriptCSS放到单独的文件中,尽可能使用长度较小的字符串来作为控件ID值。

对策3:合并JSCSS文件减少并发数

如果将两个JS文件合并不至于对项目维护造成影响,那么最好将它们合并。

对策4:使用图片延迟加载技术减少并发数

所谓图片延迟加载,就是每次只加载当前屏幕可见区域的图片,其余的图片在用户滚动页面到该位置后才开始加载。这是一项非常实用的技术,减少了并发数,不但减少了服务器的压力,也降低了页面的加载时间,目前很多门户网站都使用了该技术,如腾讯微博的“看看推荐的人”页面,在该页面上有几千个头像,如果一次性加载全部的图片,就要耗费比较多的客户端和服务器端的资源。该功能的实现原理很简单,就是将页面上的src替换成其他标记(如original),在页面滚动到相应位置后,再将original更改为src。目前有个Jquery插件Jquery.LazyLoad.js可以实现图片的延迟加载,使用方法如下所示:

复制代码
1  jQuery(document).ready(
2  function ($){
3  $( " img " ).lazyload({
4       placeholder : grey.gif  // 代替原图片
5       effect      :  " fadeIn "
6  });
7  });
复制代码

 

不过该插件并没有真正的减少图片延迟加载,因为执行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








猜你喜欢

转载自blog.csdn.net/tianzongnihao/article/details/80290780
今日推荐