`
leeight
  • 浏览: 23542 次
社区版块
存档分类
最新评论

[转载]Web性能优化

阅读更多
数据层的性能

  当调整某个应用程序的性能时,有一个简单的试金石,你可以用它按先后次序:检查代码是否存取数据库?如果是,多长时间存取一次?注意相同的测试也可以被应用于使用 Web 服务或远程调用的代码,但我们本文中不涉及这方面内容。
  如果在特定的代码流程中必须具有对数据库的请求以及要考察其它方面,如:想对字符串处理进行优先优化,那么暂且把它放一放,先按照上面定好的优先次序来做。除非你有异乎寻常的性能问题,否则你的时间应该用在尝试最优化与数据库的连接所花的时间,返回的数据量以及多长时间往返一次和数据库的通讯上。
  有了这些概括信息,下面就让我们来看看能帮助你改善应用程序性能的十个技巧。我将从能获得最显著效果的改变开始。

技巧 1 —— 返回多个结果集

  复审你的数据库代码,看看是否有多于一次的对数据库的访问请求。这样每次往返数据库都降低你的应用程序能处理的每秒请求数。通过在单个数据库请求中返回多结果集,你能降低与数据库通信的总体时间。同时你也将使系统更具伸缩性,因为你减少了数据库服务器处理请求的负担。
  虽然你可以用动态 SQL 返回多结果集,我更喜欢使用存储过过程。是否将业务逻辑驻留在存储过程当中是个有待争论的问题,但我认为,如果存储过程中的逻辑能约束返回的数据(降低数据集的尺寸,在网络上传输的时间以及逻辑层不必过虑数据),这是一件好事情。
  使用 SqlCommand 命令实例及其 ExecuteReader 方法来处理强类型的各个业务类,你通过调用 NextResult 可以向前移动结果集指针。Figure 1 示范了处理几个带类型的 ArrayLists 例子会话。从数据库只返回你需要的数据还会降低服务器上内存的分配。

技巧 2 —— 分页数据存取

  ASP.NET DataGrid 提供了非常好的能力:数据分页支持。当启用 DataGrid 中的分页功能,则每次只显示固定数量的记录。此外,分页用户界面也会显示在 DataGrid 底部用于导航记录。分页用户界面允许你向前向后导航所显示的记录,一次显示固定数量的记录。
  有一个美中不足的是用 DataGrid 分页需要将所有数据邦定到此栅格控件(gird)。例如,你的数据层必须返回所有数据,然后 DataGrid 将根据当前页过滤掉所有显示的记录。当你通过 DataGrid 进行分页时,如果有 100,000 条记录被返回,那么每个请求有 99,975 条记录将被废弃掉(假设页尺寸为 25)。当记录数不断增加,此应用程序的性能便会遭受痛苦,因为每次请求所要发送的数据会越来越多。
  编写较好的分页代码的一个好的方法是用存储过程。Figure 2 示范了一个用 Northwind 数据库中 Orders 表通过存储过程分页的例子。很简单,只要你在页面中传递索引以及页尺寸即可。相应的结果集先被计算然后被返回。
  在 Community Server 中,我们编写了几个分页控件来完成数据分页。你将会看到,我使用了技巧 1 中讨论的思路,从一个存储过程中返回连个结果集:总记录数和请求的数据。
  返回的总记录数依赖于所执行的查询不同而不同。例如,某个 WHERE 子句可被用于约束返回的数据。为了计算在分页用户界面显示的总页数,返回的总记录数必须是已知的。例如,如果有 1,000,000 条记录,用一个 WHERE 子句对之过滤后为 1,000 条记录,则分页逻辑必须要知道总记录数以便在分页用户界面中正确呈现。

技巧 3 —— 连接池

  建立 Web 应用程序与 SQL Server 之间的 TCP 连接是一项昂贵的操作。微软的开发人员利用连接池技术已经有好长一段时间了,这个技术使他们能重用到数据库的连接。而不是每次请求都建立新的 TCP 连接,新连接仅在连接池中得不到连接时才建立。当连接被关闭时,它被返回到连接池中,在那里它仍然保持与数据库的连接,与完全断开 TCP 连接相反。
  当然,你需要提防泄漏的连接。当你处理完毕,一定要关闭连接。重申一次:不管人们怎么吹嘘微软 .NET 框架中的垃圾收集特性,每当你处理完毕,一定要显式地调用连接对象的 Close 或 Dispose 方法。不要指望公共语言运行时(CLR)来为你定时清除和关闭连接。CLR 最终将销毁类并强行关闭连接,但你无法保证该对象的垃圾收集届时会起作用。
  为了充分用好连接池,有几条规则必须了然于心。首先,打开连接,进行处理,然后关闭连接。宁愿每个请求的连接打开和关闭多次,也不要保持连接打开状态以及在不同的方法间将它传来传去。其次,使用相同的连接串(如果你使用集成身份检查,那么也要用相同的线程身份)。如果你不用相同的连接串,例如,根据登录用户来定制连接串,你将无法得到连接池所提供的相同的最优化值。当模拟大用户量情形时,如果你使用集成身份检查,那么你的连接池将效力大减。.NET CLR 数据性能计数器在试图跟踪任何与连接池有关的性能问题时是非常有用的。
  不管什么时候,只要你的应用程序连接到运行在其它进程中的资源,比如某个数据库,你都应该针对连接到资源所耗时间,发送和接收数据所耗时间以及往返次数进行优化。为了实现较好的性能,应该首当其充优化应用程序中任何种类的忙碌进程。
  应用层包含到数据层的连接以及将数据转换成有意义的类实例和业务处理的逻辑。以 Community Server 为例,你要在其中处理 Forums 和 Threads 集合;以及应用许可这样的业务规则;尤其重要的是缓冲(Caching)逻辑也实现其中。

技巧 4 —— ASP.NET Cache API

  在编写代码之前要做的头等大事之一是最大限度地构建应用层并发掘 ASP.NET 的 Cache 特性。
  如果你的组件在 ASP.NET 应用程序内运行,那么你只需要在应用程序工程中引用 System.Web.dll 即可。当你需要访问 Cache 时,用 HttpRuntime.Cache 属性(相同的对象也可以通过 Page.Cache 和 HttpContext.Cache 访问)。
  缓冲数据有几个准则。首先,如果数据能被使用多次,缓冲是个好的后选方案。其次,如果数据对给定请求或用户是一般的数据而非专用数据,那么最好是选择缓冲。如果数据用户或请求专用,如果需要保存期很长但可能不被经常使用,那么仍然要用缓冲。第三,常常被忽略的一个准则是有时缓冲太多的东西。一般来说,在x86机器上,为了降低内存不足错误的几率,运行某个进程不要超过800MB私有字节。因此,缓冲应该有个上限。换句话说,你也许能重用某个计算的结果,但如果该计算有10个参数,你可能试图针对10个置换进行缓冲,这样做可能会给你带来麻烦。ASP.NET 提供的最常见的容错是由覆盖缓冲导致的内存不足错误,尤其是大型数据集。
  Cache 有几个重要特性是必须要了解的。第一个是 Cache 实现了最近最少使用(least-recently-used)算法,允许 ASP.NET 强制 Cache 清除操作 —— 如果可用内存下降到低水平 —— 则自动从 Cache 中删除不使用的项目。第二个是 Cache 支持依赖性到期特性,它能强制包括时间,键值,文件失效。时间常常被使用,但 ASP.NET 2.0 引入了具有更强大的失效类型:数据库缓冲失效。也就是当数据库中的数据改变时,缓冲中的条目会自动删除。有关数据库缓冲失效的更多信息参见 Dino Esposito 在 MSDN 杂志 2004 年七月刊的 Cutting Edge 专栏文章。该缓冲的体系结构,参见 Figure 3。

Figure 3 ASP.NET Cache

技巧 5 —— 预请求缓冲(Per-Request Caching)

  在本文前面,我曾提到对频繁执行的代码块所做的小小改动可能产生很大的,整体性能的提升。我把其中一个我特别中意的叫做预请求缓冲(per-request caching)。
  由于 Cache API 被设计用来缓冲长期数据或直到某个条件被满足,预请求缓冲意旨用于请求期间的缓冲该数据。特定的代码流程被每次请求频繁访问但是数据只需要被拾取,应用,修改或更新一次,这样说太理论化,还是让我们看一个具体的例子吧。
在 Community Server 的 Forums (论坛)应用中,某个页面上使用的每个服务器控件需要个性化数据以确定使用那个皮肤和式样页,以及其它的个性化数据,其中有些数据可以被长时间缓冲,但有些数据,比如用于控件的皮肤在单个请求中只被拾取一次并在该请求执行期间被重用多次。
  为了完成预请求缓冲,用 ASP.NET HttpContext。HttpContext 的实例是随每个请求创建的,并可以通过 HttpContext.Current 属性在那个请求执行期间的任何地方存取它。HttpContext 类具有一个特别的 Items 集合属性,被添加到该 Items 集合的对象和数据只是在该请求期间被缓存。就像你可以使用 Cache 来保存频繁使用的数据一样,你可以用 HttpContext.Items 来保存只在某个预请求中使用的数据。在此背景后的逻辑很简单:当数据不存在时被添加到 HttpContext.Items 集合,以及在随后的并发查找中简单地返回 HttpContext.Items 中发现的数据。

技巧 6——后台处理

  你的代码流程应该尽可能快,对吧?你自己可能多次发现要完成每个请求或每n个请求的任务代价很高。发出 e-mail 或解析并检查输入数据的有效性就是个例。
  在重新生成 ASP.NET Forums 1.0 并把它整合到 Community Server 时,我们发现添加新贴的代码流程非常慢。每次添加帖子,应用程序首先要确保没有重复贴,然后必须用“badword”过滤器解析该贴的表情图像,记号并索引,如果必要还要将帖子添加到相应的队列中,对附件进行有效性检查,最终完成发贴后,给预订者发出 e-mail 通知。显然,这里做的工作太多。
  我们发现大多数时间都花在了索引逻辑和发送e-mail上。索引帖子是一个很耗时的操作,此外,内建的 System.Web.Mail 功能要与 SMTP 服务器连接并顺序发送邮件。当特定帖子或主题预定者数量增加时,AddPost 函数的执行时间会越来越长。
  并不是每个请求都需要索引邮件,我们想最好是批量集中处理,并且一次只索引25个帖子或每隔五分钟发送一次邮件。我们决定使用的代码与我曾在原型数据库缓冲失效中所使用的代码相同,最终它也被纳入 Visual Studio 2005。
  名字空间 System.Threading 中的 Timer 类非常有用,但在.NET 框架中鲜为人知,至少对 Web 开发者来说是这样。一旦创建,Timer 将以可定制的间隔针对线程池中的某个线程调用指定的回调函数。这意味着你不用输入请求到 ASP.NET 应用程序便能让代码实行,这是一种最合适后台处理的情形。你也可以在这种后台处理模式中进行例如索引或发送电子邮件这样的工作。
  尽管如此,这个技术存在几个问题,如果你的应用程序域关闭,该定时器实例将停止触发其事件。另外,由于 CLR 有一个硬坎,即每个进程的线程数是固定的,你便可能陷入严重的服务器负荷当中,此时可能就没有线程来处理定时器,从而造成延时。为了让发生这种情况的几率最小化,ASP.NET 通过在进程中预留一定数量的空闲线程,并只使用部分线程来处理请求。然而,如果你有许多异步处理,这样做会有问题。
  由于篇幅所限,在此无法列出代码,但你可以从 http://www.rob-howard.net/ 下载可消化的例子。其中有 Blackbelt TechEd 2004 展示的幻灯和 Demo。

技巧 7——页面输出缓存和代理服务器

  ASP.NET 是你的表示层(或者说应该是);它由页面,用户控件,服务器控件(HttpHandlers and HttpModules)以及它们生成的内容组成。如果你有一个产生输出的 ASP.NET 页面,不管是输出 HTML,XML,图像还是任何其它数据,而且每个请求你都运行这个代码并产生相同的输出,此时最好选择使用页面输出缓存。
只要在页面顶部添加这一行代码即可:

<%@ Page OutputCache VaryByParams="none" Duration="60" %>
  你可以为此页面有效地产生一次输出并可以在60秒内多次重用它,一到这个时间点,该页面将重新执行并将再次将输出添加到 ASP.NET Cache。这个行为还能用某些低级编程 APIs 来完成。输出缓存有几个可以配置的设置,比如:VaryByParams 属性。VaryByParams 不是必须的,但允许你指定 HTTP GET 或 HTTP POST 参数来改变缓存入口。例如,default.aspx?Report=1 或 default.aspx?Report=2 可以简单地设置 VaryByParam="Report" 来对输出进行缓存。额外的参数被命名并用用分号分隔。
  在使用输出缓存机制时,许多人都不了解 ASP.NET 页还产生一组下游缓存服务器 HTTP 头,比如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的 HTTP 头。当设置 HTTP 缓存头,文档可以被缓存到这些网络资源,从而响应客户端请求不必返回原服务器。
  然而,使用页面输出缓存并不会使你的应用程序更有效率,但它能通过下游缓存技术缓存文档从而潜在地降低服务器的负载。当然,这只能是异步内容;一旦实施下游缓存,你将无法看到任何请求,也不能实现身份认证来防止对它的存取。

技巧 8——运行 IIS 6.0 (如果仅用于内核缓存)

  如果你不运行 IIS 6.O(Windows Server 2003),那么你将得不到微软 Web 服务器中一些重大的性能改进。在技巧 7 中,我谈到了输出缓存。在 IIS 5.0 中,请求到达 IIS,然后到达 ASP.NET。当使用缓存时,ASP.NET 中的 HttpModule 接受该请求,并从该缓存中返回内容。
  如果你用 IIS 6.0,有一些巧妙的特性叫内核缓存,它不需要将任何代码改成 ASP.NET。当 ASP.NET对请求进行缓存处理,IIS 内核缓存便接收一份缓存数据的拷贝。当请求来自网络驱动器,内核一级的驱动程序(没有到用户模式的上下文转换)接收该请求,如果缓存,则直接用缓存数据响应并完成执行。这意味着当你使用 IIS 内核模式缓存和 ASP.NET 缓存时,你将看到无法置信的性能结果。在开发 Visual Studio 2005 的 ASP.NET 期间,我是负责 ASP.NET 性能的程序经理。开发人员的工作做的真是棒极了,而我基本上每天都在看报告。内核模式缓存结果总是最有趣的。典型的情况是请求/响应往往使网络饱和,但 IIS 的运行仅占 CPU 的百分之五。真令人惊异!当然使用 IIS 6.O 有其它一些原因,但内核模式缓存是显而易见的理由。

技巧 9——使用 Gzip 压缩

  虽然使用 gzip 压缩不是一个必须的服务器性能技巧(因为你可能看到 CUP 的使用率上升了),但它能降低服务器发送字节的数量。从而感觉页面更快,而且减少带宽的占用。其压缩的效果好坏取决于所发送的数据以及客户端浏览器是否支持这种压缩(IIS 只会将数据发送到支持 gzip 的浏览器,比如:IE 6.0 和 Firefox),从而使服务器可以在每秒钟里处理更多的请求。事实上,只要你降低返回数据的数量,便能提高每秒所处理的请求数。
  有一个好消息是 gzip 压缩是 IIS 6.0 的内建特性,并且比它在 IIS 5.0 中使用的效果更好。但是,要想在 IIS 6.0 中启用 gzip 压缩可能没那么方便,IIS 的属性对话框里找不到设置它的地方。IIS 团队将卓越的 gzip 压缩能力内建在服务器中,但忽视了建立一个启用压缩特性的管理用户界面。要想启用 gzip 压缩机制,你必须深入到 IIS 的 XML 配置设置内部(必须对之相当熟悉才能配置)。顺便提一下,在此感谢 OrcsWeb 的 Scott Forsyth 帮我解决了在 OrcsWeb 数个 http://www.asp.net/ 服务器上的这个问题。
  与其在本文中包含整个过程,还不如阅读 Brad Wilson 在 IIS6 Compression 上的文章。微软知识库也有一篇关于为ASPX启用压缩特性的文章:Enable ASPX Compression in IIS。但是,还必须注意一点,动态压缩与内核缓存由于某些实现细节的原因,其在 IIS 6.0 中是相互排斥的。

技巧 10——服务器控件的可视状态

  可视状态(View State)对于 ASP.NET 来说是个奇特的名字,它在所产生的页面中隐藏输入域以存储某些状态数据。当页面被发回服务器,该服务器能解析,检查其有效性并将这个状态数据应用到页面的控件树中。可视状态是一种非常强大的能力,因为它允许状态被客户端持续化并且它不需要cookies 或 服务器内存来存储该状态。许多 ASP.NET 服务器控件使用可视状态来持续化与页面元素交互期间所作的设置,例如,对数据进行分页时保存当前页显示页。
  然而,使用可视状态有许多不利之处,首先,不论是在请求的时候还是提供服务的时候,它都增加造成整个页面的负担。当序列化或反序列化被返回服务器的可视状态数据时还产生一些附加的开销。最终可视状态会增加服务器的内存分配。
  最著名的服务器控件要数 DataGrid 了,使用可视状态有过之而无不及,即便是在不需要使用的时候也是如此。ViewState 属性默认是启用的,但如果你不需要它,可以在页面控件级或页面级关闭它。在某个控件中,只要将 EnableViewState 设置为 false,或者在页面里使用如下全局设置:


分享到:
评论

相关推荐

    使用open source产品组装你的web应用架构(转载)

    10. **监控与日志**:Prometheus、Grafana用于性能监控,ELK Stack(Elasticsearch、Logstash、Kibana)处理日志收集、分析和可视化,帮助开发者及时发现问题并优化系统。 11. **安全**:HTTPS证书管理工具如...

    高效前端:Web高效编程与优化实践_机械工业出版社; 第1版 (2018年1月1日) 完整版-未加密

    全书以问题为导向,精选了前端开发中的34个疑难问题,从分析问题的原因入手,逐步给出解决方案,并分析各种方案的优劣,最后针对每个问题总结出高效编程的最佳实践和各种性能优化的方法。全书共7章,内容从逻辑上...

    Linux一键安装web环境全攻略

    - 作为高性能的Web服务器和反向代理服务器,在处理静态内容方面表现出色。 - **Apache**: - 版本: 2.2.29、2.4.10 - 是广泛使用的Web服务器之一,提供强大的模块化特性以适应不同需求。 - **MySQL**: - 版本: ...

    三种Web开发主流技术ASP-PHP-JSP的评价[转载] --希望给大家作为参考.txt

    4. **运行效率高**:尽管在某些情况下需要优化以提高性能,但总体上ASP提供了较高的运行效率。 **局限性** 1. **依赖于Windows平台**:ASP主要针对Windows操作系统设计,这意味着非Windows平台的用户在部署ASP应用...

    转载类似iphono的加速度滑动效果的Flex容器

    总之,这个技术点涉及了CSS3 Flex布局、JavaScript触摸事件处理、速度和加速度计算,以及性能优化等多个方面,对于提升Web应用的用户体验具有重要意义。通过实践和学习,开发者可以将这种高级的交互效果应用到自己的...

    TOMCAT源代码,包括转载得别人的分析

    它的源代码是公开的,这使得开发者能够深入理解其内部工作原理,优化性能,或者为它添加自定义功能。以下是对Tomcat源码及其分析的一些关键知识点: 1. **Servlet与JSP**: Tomcat的核心在于Servlet容器,它负责...

    birt经验之谈转载

    BIRT(Business Intelligence and Reporting Tools)是一个基于Eclipse的开源报表系统,主要...通过深入理解和有效利用这些特性,开发者可以创建出满足业务需求的高效报表,并且在性能优化方面也有多种策略可以选择。

    js提示框-转载+++ js 乱码+++自动提交+滚动图片加载+箭头导航(转载)

    总的来说,这个资源包涵盖了JavaScript的基本交互设计、编码处理、表单操作、性能优化和导航设计等多个方面,对于学习和提升JavaScript编程技能是非常有价值的。在实际开发中,掌握这些知识点有助于创建更富交互性和...

    转载的供大家分享

    【标签】:Java调优,企业级应用,内存溢出,性能优化 【内容】: 企业级Java EE应用程序在不同部署环境下可能面临相似的问题。作为Java EE问题解决专家,作者通过分析和诊断发现,这些问题往往遵循一定的解决模式...

    脚本全自动完美搭建先电版openstack.docx

    偏研究的话,你可以针对某一个点展开,通常就是优化了,比较多的研究点包括调度算法优化、在线迁移优化、负载均衡优化、计算性能优化、虚拟化性能优化、能源损耗优化等,尤其是调度算法,你可以联想抽象出各种问题,...

    ogame中文源代码(网络转载)

    通过分析数据库模型,我们可以学习到如何设计符合游戏需求的关系型数据库,以及如何优化查询性能。 6. **安全性考虑** 在一个多人在线游戏中,防止作弊和保护用户数据安全是必不可少的。源代码中会包含验证机制、...

    淘宝客C#开源码(转载下载)

    8. **性能优化**:考虑到小程序的运行环境,性能优化是必须考虑的,包括资源加载优化、数据缓存策略、异步处理等。 9. **版本控制与协作**:使用Git进行版本控制和团队协作,确保代码的稳定性和团队间的有效沟通。 ...

    jsp论坛—别的网站转载的

    7. **性能优化**:如缓存策略(如Redis)、负载均衡、CDN服务等,以应对高并发访问。 开源的JSP论坛源码为初学者和经验丰富的开发者提供了一个实践平台,他们可以通过实际操作,深入学习和掌握上述知识点。同时,...

    转载的AJAX反转资料

    6. 安全和性能考虑:学习如何避免跨域问题,优化请求以减少延迟,以及处理错误和异常。 通过研究这份“反转AJAX”资料,开发者可以进一步提升在构建实时、动态Web应用方面的能力,更好地理解和运用AJAX及其相关技术...

    WebService+Android

    - **性能优化**:避免频繁的网络请求,可以使用加载更多、分页加载等策略,提高用户体验。 总的来说,"使用webservice为android提供数据"涉及的知识点包括:WebService的基本概念、Android中的网络请求方式、各种...

    【转载】浏览器的工作原理:新式网络浏览器幕后揭秘

    浏览器是我们日常上网的主要工具,它的工作原理复杂而精妙,涉及到网络通信、解析渲染、JavaScript执行等多个环节。这篇博文将深入探讨新式网络浏览器的...了解这些知识,有助于我们更好地理解和优化Web应用的性能。

    [转载] PV3D 全景的一个核心代码

    PV3D全景展示可能需要处理大量数据,所以性能优化是必要的。代码中可能包含了一些优化技巧,如使用`DisplayObject.cacheAsBitmap`属性来提高渲染效率,或者使用适当的缓存策略来减少不必要的计算。 6. **帧率控制*...

    JDBC连接DB2数据库详解.doc--转载

    JDBC (Java Database Connectivity) 是Java编程语言中用于与各种数据库进行交互的一种标准接口。...通过理解JDBC连接字符串、驱动注册以及性能优化策略,可以有效地在Java应用中集成和管理DB2数据库。

Global site tag (gtag.js) - Google Analytics