26种提升ASP.NET网站访问性能的优化方法 .

1. 数据库访问性能优化html


数据库的链接和关闭程序员

  访问数据库资源须要建立链接、打开链接和关闭链接几个操做。这些过程须要屡次与数据库交换信息以经过身份验证,比较耗费服务器资源。 ASP.NET中提供了链接池(Connection Pool)改善打开和关闭数据库对性能的影响。系统将用户的数据库链接放在链接池中,须要时取出,关闭时收回链接,等待下一次的链接请求。web

  链接池的大小是有限的,若是在链接池达到最大限度后仍要求建立链接,必然大大影响性能。所以,在创建数据库链接后只有在真正须要操做时才打开链接,使用完毕后立刻关闭,从而尽可能减小数据库链接打开的时间,避免出现超出链接限制的状况。算法

使用存储过程数据库

  存储过程是存储在服务器上的一组预编译的SQL语句,相似于DOS系统中的批处理文件。存储过程具备对数据库当即访问的功能,信息处理极为迅 速。使用存储过程能够避免对命令的屡次编译,在执行一次后其执行规划就驻留在高速缓存中,之后须要时只需直接调用缓存中的二进制代码便可。编程

  另外,存储过程在服务器端运行,独立于ASP.NET程序,便于修改,最重要的是它能够减小数据库操做语句在网络中的传输。windows

优化查询语句浏览器

  ASP.NET中ADO链接消耗的资源至关大,SQL语句运行的时间越长,占用系统资源的时间也越长。所以,尽可能使用优化过的SQL语句以减小执行时间。好比,不在查询语句中包含子查询语句,充分利用索引等。缓存


2. 字符串操做性能优化安全

  使用值类型的ToString方法。在链接字符串时,常用"+"号直接将数字添加到字符串中。这种方法虽然简单,也能够获得正确结果,可是因为涉及到不一样的数据类型, 数字须要经过装箱操做转化为引用类型才能够添加到字符串中。可是装箱操做对性能影响较大,由于在进行这类处理时,将在托管堆中分配一个新的对象,原有的值 复制到新建立的对象中。

使用值类型的ToString方法能够避免装箱操做,从而提升应用程序性能。

运用StringBuilder类

  String类对象是不可改变的,对于String对象的从新赋值在本质上是从新建立了一个String对象并将新值赋予该对象,其方法ToString对性能的提升并不是很显著。

  在处理字符串时,最好使用StringBuilder类,其.NET 命名空间是System.Text。该类并不是建立新的对象,而是经过Append,Remove,Insert等方法直接对字符串进行操做,经过ToString方法返回操做结果。

其定义及操做语句以下所示:

int num;

System.Text.StringBuilder str = new System.Text.StringBuilder(); //建立字符串

str.Append(num.ToString()); //添加数值num

Response.Write(str.ToString); //显示操做结果

3. 优化 Web 服务器计算机和特定应用程序的配置文件以符合您的特定须要

  默认状况下,ASP.NET 配置被设置成启用最普遍的功能并尽可能适应最多见的方案。所以,应用程序开发人员能够根据应用程序所使用的功能,优化和更改其中的某些配置,以提升应用程序的性能。下面的列表是您应该考虑的一些选项。


  仅对须要的应用程序启用身份验证。默认状况下,身份验证模式为 Windows,或集成 NTLM。大多数状况下,对于须要身份验证的应用程序,最好在 Machine.config 文件中禁用身份验证,并在 Web.config 文件中启用身份验证。


  根据适当的请求和响应编码设置来配置应用程序。ASP.NET 默认编码格式为 UTF-8。若是您的应用程序为严格的 ASCII,请配置应用程序使用 ASCII 以得到稍许的性能提升。


  考虑对应用程序禁用 AutoEventWireup。在 Machine.config 文件中将 AutoEventWireup 属性设置为 false,意味着页面不将方法名与事件进行匹配和将二者挂钩(例如 Page_Load)。若是页面开发人员要使用这些事件,须要在基类中重写这些方法(例如,须要为页面加载事件重写 Page.OnLoad,而不是使用 Page_Load 方法)。若是禁用 AutoEventWireup,页面将经过将事件链接留给页面做者而不是自动执行它,得到稍许的性能提高。


  从请求处理管线中移除不用的模块。默认状况下,服务器计算机的 Machine.config 文件中 <httpModules> 节点的全部功能均保留为激活。根据应用程序所使用的功能,您能够从请求管线中移除不用的模块以得到稍许的性能提高。检查每一个模块及其功能,并按您的须要自 定义它。


  例如,若是您在应用程序中不使用会话状态和输出缓存,则能够从 <httpModules> 列表中移除它们,以便请求在不执行其余有意义的处理时,没必要执行每一个模块的进入和离开代码。


4. 必定要禁用调试模式

  在部署生产应用程序或进行任何性能测量以前,始终记住禁用调试模式。若是启用了调试模式,应用程序的性能可能受到很是大的影响。


5. 对于普遍依赖外部资源的应用程序,请考虑在多处理器计算机上启用网络园艺

  ASP.NET 进程模型帮助启用多处理器计算机上的可缩放性,将工做分发给多个进程(每一个 CPU 一个),而且每一个进程都将处理器关系设置为其 CPU。此技术称为网络园艺。若是应用程序使用较慢的数据库服务器或调用具备外部依赖项的 COM 对象(这里只是说起两种可能性),则为您的应用程序启用网络园艺是有益的。可是,在决定启用网络园艺以前,您应该测试应用程序在网络园中的执行状况。


6. 只要可能,就缓存数据和页输出

  ASP.NET 提供了一些简单的机制,它们会在不须要为每一个页请求动态计算页输出或数据时缓存这些页输出或数据。另外,经过设计要进行缓存的页和数据请求(特别是在站点 中预期将有较大通信量的区域),能够优化这些页的性能。与 .NET Framework 的任何 Web 窗体功能相比,适当地使用缓存能够更好的提升站点的性能,有时这种提升是超数量级的。

  使用 ASP.NET 缓存机制有两点须要注意。首先,不要缓存太多项。缓存每一个项均有开销,特别是在内存使用方面。不要缓存容易从新计算和不多使用的项。其次,给缓存的项分配 的有效期不要过短。很快到期的项会致使缓存中没必要要的周转,而且常常致使更多的代码清除和垃圾回收工做。若关心此问题,请监视与 ASP.NET Applications 性能对象关联的 Cache Total Turnover Rate 性能计数器。高周转率可能说明存在问题,特别是当项在到期前被移除时。这也称做内存压力。

7. 选择适合页面或应用程序的数据查看机制

  根据您选择在 Web 窗体页显示数据的方式,在便利和性能之间经常存在着重要的权衡。例如,DataGrid Web 服务器控件多是一种显示数据的方便快捷的方法,但就性能而言它的开销经常是最大的。在某些简单的状况下,您经过生成适当的 HTML 本身呈现数据可能颇有效,可是自定义和浏览器定向会很快抵销所得到的额外功效。Repeater Web 服务器控件是便利和性能的折衷。它高效、可自定义且可编程。

8. 将 SqlDataReader 类用于快速只进数据游标

  SqlDataReader 类提供了一种读取从 SQL Server 数据库检索的只进数据流的方法。若是当建立 ASP.NET 应用程序时出现容许您使用它的状况,则 SqlDataReader 类提供比 DataSet 类更高的性能。状况之因此这样,是由于 SqlDataReader 使用 SQL Server 的本机网络数据传输格式从数据库链接直接读取数据。另外,SqlDataReader 类实现 IEnumerable 接口,该接口也容许您将数据绑定到服务器控件。有关更多信息,请参见 SqlDataReader 类。有关 ASP.NET 如何访问数据的信息,请参见经过 ASP.NET 访问数据。

9. 将 SQL Server 存储过程用于数据访问

  在 .NET Framework 提供的全部数据访问方法中,基于 SQL Server 的数据访问是生成高性能、可缩放 Web 应用程序的推荐选择。使用托管 SQL Server 提供程序时,可经过使用编译的存储过程而不是特殊查询得到额外的性能提升。


10.   避免单线程单元 (STA) COM 组件

  默认状况下,ASP.NET 不容许任何 STA COM 组件在页面内运行。若要运行它们,必须在 .aspx 文件内将 ASPCompat=true 属性包含在 @ Page 指令中。这样就将执行用的线程池切换到 STA 线程池,并且使 HttpContext 和其余内置对象可用于 COM 对象。前者也是一种性能优化,由于它避免了将多线程单元 (MTA) 封送到 STA 线程的任何调用。


  使用 STA COM 组件可能大大损害性能,应尽可能避免。若必须使用 STA COM 组件,如在任何 interop 方案中,则应在执行期间进行大量调用并在每次调用期间发送尽量多的信息。另外,当心不要在构造页面期间建立任何 STA COM 组件。例以下面的代码中,在页面构造时将实例化由某个线程建立的 MySTAComponent,而该线程并非将运行页面的 STA 线程。这可能对性能有不利影响,由于要构造页面就必须完成 MTA 和 STA 线程之间的封送处理。

<%@ Page Language="VB" ASPCompat="true" %>
< script runat=server>
Dim myComp as new MySTAComponent()
Public Sub Page_Load()
myComp.Name = "Bob"
End Sub
< /script>
< html>
< %
Response.Write(myComp.SayHello)
%>
< /html>

首选机制是推迟对象的建立,直到之后在 STA 线程下执行上述代码,以下面的例子所示。

<%@ Page Language="VB" ASPCompat="true" %>
< script runat=server>
Dim myComp
Public Sub Page_Load()
myComp = new MySTAComponent()
myComp.Name = "Bob"
End Sub
< /script>
< html>
< %
Response.Write(myComp.SayHello)
%>
< /html>

  推荐的作法是在须要时或者在 Page_Load 方法中构造任何 COM 组件和外部资源。

  永远不要将任何 STA COM 组件存储在能够由构造它的线程之外的其余线程访问的共享资源里。这类资源包括像缓存和会话状态这样的资源。即便 STA 线程调用 STA COM 组件,也只有构造此 STA COM 组件的线程可以实际为该调用服务,而这要求封送处理对建立者线程的调用。此封送处理可能产生重大的性能损失和可伸缩性问题。在这种状况下,请研究一下使 COM 组件成为 MTA COM 组件的可能性,或者更好的办法是迁移代码以使对象成为托管对象。

11.   将调用密集型的 COM 组件迁移到托管代码

  .NET Framework 提供了一个简单的方法与传统的 COM 组件进行交互。其优势是能够在保留现有投资的同时利用新的平台。可是在某些状况下,保留旧组件的性能开销使得将组件迁移到托管代码是值得的。每一状况都是 不同的,决定是否须要迁移组件的最好方法是对 Web 站点运行性能测量。建议您研究一下如何将须要大量调用以进行交互的任何 COM 组件迁移到托管代码。

  许多状况下不可能将旧式组件迁移到托管代码,特别是在最初迁移 Web 应用程序时。在这种状况下,最大的性能障碍之一是将数据从非托管环境封送到托管环境。所以,在交互操做中,请在任何一端执行尽量多的任务,而后进行一个 大调用而不是一系列小调用。例如,公共语言运行库中的全部字符串都是 Unicode 的,因此应在调用托管代码以前将组件中的全部字符串转换成 Unicode 格式。

  另外,一处理完任何 COM 对象或本机资源就释放它们。这样,其余请求就可以使用它们,而且最大限度地减小了因稍后请求垃圾回收器释放它们所引发的性能问题。


12.   在 Visual Basic .NET 或 JScript 代码中使用早期绑定

  以往,开发人员喜欢使用 Visual Basic、VBScript 和 JScript 的缘由之一就是它们所谓“无类型”的性质。变量不须要显式类型声明,并可以简单地经过使用来建立它们。当从一个类型到另外一个类型进行分配时,转换将自动执 行。不过,这种便利会大大损害应用程序的性能。

  Visual Basic 如今经过使用 Option Strict 编译器指令来支持类型安全编程。为了向后兼容,默认状况下,ASP.NET 不启用该选项。可是,为了获得最佳性能,强烈建议在页中启用该选项。若要启用 Option Strict,请将 Strict 属性包括在 @ Page 指令中,或者,对于用户控件,请将该属性包括在 @ Control 指令中。下面的示例演示了如何设置该属性,并进行了四个变量调用以显示使用该属性是如何致使编译器错误的。

<%@ Page Language="VB" Strict="true" %>
< %
Dim B
Dim C As String
' This will cause a compiler error.
A = "Hello"
' This will cause a compiler error.
B = "World"
' This will not cause a compiler error.
C = "!!!!!!"
' But this will cause a compiler error.
C = 0
%>

  JScript .NET 也支持无类型编程,但它不提供强制早期绑定的编译器指令。若发生下面任何一种状况,则变量是晚期绑定的:

  被显式声明为 Object。

  是无类型声明的类的字段。

  是无显式类型声明的专用函数或方法成员,而且没法从其使用推断出类型。

  最后一个差异比较复杂,由于若是 JScript .NET 编译器能够根据变量的使用状况推断出类型,它就会进行优化。在下面的示例中,变量 A 是早期绑定的,但变量 B 是晚期绑定的。

var A;

var B;

A = "Hello";


B = "World";


B = 0;

  为了得到最佳的性能,当声明 JScript .NET 变量时,请为其分配一个类型。例如,var A : String。

13.   使请求管线内的全部模块尽量高效

  请求管线内的全部模块在每次请求中都有机会被运行。所以,当请求进入和离开模块时快速地触发代码相当重要,特别是在不使用模块功能的代码路径里。分别在使用及不使用模块和配置文件时执行吞吐量测试,对肯定这些方法的执行速度很是有用。

14.   使用 HttpServerUtility.Transfer 方法在同一应用程序的页面间重定向

  采用 Server.Transfer 语法,在页面中使用该方法可避免没必要要的客户端重定向。

15.   必要时调整应用程序每一个辅助进程的线程数

  ASP.NET 的请求结构试图在执行请求的线程数和可用资源之间达到一种平衡。已知一个使用足够 CPU 功率的应用程序,该结构将根据可用于请求的 CPU 功率,来决定容许同时执行的请求数。这项技术称做线程门控。可是在某些条件下,线程门控算法不是颇有效。经过使用与 ASP.NET Applications 性能对象关联的 Pipeline Instance Count 性能计数器,能够在 PerfMon 中监视线程门控。

  当页面调用外部资源,如数据库访问或 XML Web services 请求时,页面请求一般中止并释放 CPU。若是某个请求正在等待被处理,而且线程池中有一个线程是自由的,那么这个正在等待的请求将开始被处理。遗憾的是,有时这可能致使 Web 服务器上存在大量同时处理的请求和许多正在等待的线程,而它们对服务器性能有不利影响。一般,若是门控因子是外部资源的响应时间,则让过多请求等待资源, 对 Web 服务器的吞吐量并没有帮助。

  为缓和这种状况,能够经过更改 Machine.config 配置文件 <processModel> 节点的 maxWorkerThreads 和 maxIOThreads 属性,手动设置进程中的线程数限制。

  注意 辅助线程是用来处理 ASP.NET 请求的,而 IO 线程则是用于为来自文件、数据库或 XML Web services 的数据提供服务的。


 分配给这些属性的值是进程中每一个 CPU 每类线程的最大数目。对于双处理器计算机,最大数是设置值的两倍。对于四处理器计算机,最大值是设置值的四倍。不管如何,对于有四个或八个 CPU 的计算机,最好更改默认值。对于有一个或两个处理器的计算机,默认值就能够,但对于有更多处理器的计算机的性能,进程中有一百或两百个线程则弊大于利。

  注意 进程中有太多线程每每会下降服务器的速度,由于额外的上下文交换致使操做系统将 CPU 周期花在维护线程而不是处理请求上。

16.   适当地使用公共语言运行库的垃圾回收器和自动内存管理

  当心不要给每一个请求分配过多内存,由于这样垃圾回收器将必须更频繁地进行更多的工做。另外,不要让没必要要的指针指向对象,由于它们将使对象保持 活动状态,而且应尽可能避免含 Finalize 方法的对象,由于它们在后面会致使更多的工做。特别是在 Finalize 调用中永远不要释放资源,由于资源在被垃圾回收器回收以前可能一直消耗着内存。最后这个问题常常会对 Web 服务器环境的性能形成毁灭性的打击,由于在等待 Finalize 运行时,很容易耗尽某个特定的资源。

17.   若是有大型 Web 应用程序,可考虑执行预批编译

  每当发生对目录的第一次请求时都会执行批编译。若是目录中的页面没有被分析并编译,此功能会成批分析并编译目录中的全部页面,以便更好地利用磁 盘和内存。若是这须要很长时间,则将快速分析并编译单个页面,以便请求能被处理。此功能带给 ASP.NET 性能上的好处,由于它将许多页面编译为单个程序集。从已加载的程序集访问一页比每页加载新的程序集要快。


  批编译的缺点在于:若是服务器接收到许多对还没有编译的页面的请求,那么当 Web 服务器分析并编译它们时,性能可能较差。为解决这个问题,能够执行预批编译。为此,只需在应用程序激活以前向它请求一个页面,不管哪页都可。而后,当用户 首次访问您的站点时,页面及其程序集将已被编译。

  没有简单的机制能够知道批编译什么时候发生。需一直等到 CPU 空闲或者没有更多的编译器进程(例如 csc.exe(C# 编译器)或 vbc.exe(Visual Basic 编译器))启动。

  还应尽可能避免更改应用程序的 /bin 目录中的程序集。更改页面会致使从新分析和编译该页,而替换 /bin 目录中的程序集则会致使彻底从新批编译该目录。

  在包含许多页面的大规模站点上,更好的办法多是根据计划替换页面或程序集的频繁程度来设计不一样的目录结构。不常更改的页面能够存储在同一目录中并在特定的时间进行预批编译。常常更改的页面应在它们本身的目录中(每一个目录最多几百页)以便快速编译。

  Web 应用程序能够包含许多子目录。批编译发生在目录级,而不是应用程序级。


18.   不要依赖代码中的异常

  由于异常大大地下降性能,因此您不该该将它们用做控制正常程序流程的方式。若是有可能检测到代码中可能致使异常的状态,请执行这种操做。不要在 处理该状态以前捕获异常自己。常见的方案包括:检查 null,分配给将分析为数字值的 String 一个值,或在应用数学运算前检查特定值。下面的示例演示可能致使异常的代码以及测试是否存在某种状态的代码。二者产生相同的结果。

try

{

       result = 100 / num;

}

catch (Exception e)

{

       result = 0;

}

// ...to this.

if (num != 0)

       result = 100 / num;

else

       result = 0;

19.   使用 HttpResponse.Write 方法进行字符串串联

  该方法提供很是有效的缓冲和链接服务。可是,若是您正在执行普遍的链接,请使用多个 Response.Write 调用。下面示例中显示的技术比用对 Response.Write 方法的单个调用链接字符串更快。

Response.Write("a");
Response.Write(myString);
Response.Write("b");
Response.Write(myObj.ToString());
Response.Write("c");
Response.Write(myString2);
Response.Write("d");

20.   除非有特殊的缘由要关闭缓冲,不然使其保持打开

禁用 Web 窗体页的缓冲会致使大量的性能开销。

21.   只在必要时保存服务器控件视图状态

  自动视图状态管理是服务器控件的功能,该功能使服务器控件能够在往返过程上从新填充它们的属性值(您不须要编写任何代码)。可是,由于服务器控 件的视图状态在隐藏的窗体字段中往返于服务器,因此该功能确实会对性能产生影响。您应该知道在哪些状况下视图状态会有所帮助,在哪些状况下它影响页的性 能。例如,若是您将服务器控件绑定到每一个往返过程上的数据,则将用从数据绑定操做得到的新值替换保存的视图状态。在这种状况下,禁用视图状态能够节省处理 时间。

  默认状况下,为全部服务器控件启用视图状态。若要禁用视图状态,请将控件的EnableViewState 属性设置为 false,以下面的 DataGrid 服务器控件示例所示。

<asp:datagrid EnableViewState="false" datasource="..." runat="server"/>

  您还能够使用 @ Page 指令禁用整个页的视图状态。当您不从页回发到服务器时,这将十分有用:

<%@ Page EnableViewState="false" %>

  注意 @ Control 指令中也支持 EnableViewState 属性,该指令容许您控制是否为用户控件启用视图状态。

  若要分析页上服务器控件使用的视图状态的数量,请(经过将 trace="true" 属性包括在 @ Page 指令中)启用该页的跟踪并查看 Control Hierarchy 表的 Viewstate 列。有关跟踪和如何启用它的信息,请参见 ASP.NET 跟踪。

22.   避免到服务器的没必要要的往返过程

  虽然您极可能但愿尽可能多地使用 Web 窗体页框架的那些节省时间和代码的功能,但在某些状况下却不宜使用 ASP.NET 服务器控件和回发事件处理。

  一般,只有在检索或存储数据时,您才须要启动到服务器的往返过程。多数数据操做可在这些往返过程间的客户端上进行。例如,从 HTML 窗体验证用户输入常常可在数据提交到服务器以前在客户端进行。一般,若是不须要将信息传递到服务器以将其存储在数据库中,那么您不该该编写致使往返过程的 代码。

  若是您开发自定义服务器控件,请考虑让它们为支持 ECMAScript 的浏览器呈现客户端代码。经过以这种方式使用服务器控件,您能够显著地减小信息被没必要要的发送到 Web 服务器的次数。

  使用 Page.IsPostBack 避免对往返过程执行没必要要的处理

  若是您编写处理服务器控件回发处理的代码,有时可能须要在首次请求页时执行其余代码,而不是当用户发送包含在该页中的 HTML 窗体时执行的代码。根据该页是不是响应服务器控件事件生成的,使用 Page.IsPostBack 属性有条件地执行代码。例如,下面的代码演示如何建立数据库链接和命令,该命令在首次请求该页时将数据绑定到 DataGrid 服务器控件。

void Page_Load(Object sender, EventArgs e)

{

       // Set up a connection and command here.

       if (!Page.IsPostBack)

       {

              String query = "select * from Authors where FirstName like '%JUSTIN%'";

              myCommand.Fill(ds, "Authors");

              myDataGrid.DataBind();

       }

}

  因为每次请求时都执行 Page_Load 事件,上述代码检查 IsPostBack 属性是否设置为 false。若是是,则执行代码。若是该属性设置为 true,则不执行代码。

  注意 若是不运行这种检查,回发页的行为将不更改。Page_Load 事件的代码在执行服务器控件事件以前执行,但只有服务器控件事件的结果才可能在输出页上呈现。若是不运行该检查,仍将为 Page_Load 事件和该页上的任何服务器控件事件执行处理。

23.   当不使用会话状态时禁用它

  并非全部的应用程序或页都须要针对于具体用户的会话状态,您应该对任何不须要会话状态的应用程序或页禁用会话状态。

  若要禁用页的会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false。例如,<%@ Page EnableSessionState="false" %>。

  注意 若是页须要访问会话变量,但不打算建立或修改它们,则将 @ Page 指令中的 EnableSessionState 属性设置为 ReadOnly。

  还能够禁用 XML Web services 方法的会话状态。有关更多信息,请参见使用 ASP.NET 和 XML Web services 客户端建立的 XML Web services。

  若要禁用应用程序的会话状态,请在应用程序 Web.config 文件的 sessionstate 配置节中将 mode 属性设置为 off。例如,<sessionstate mode="off" />。

24.   仔细选择会话状态提供程序

  ASP.NET 为存储应用程序的会话数据提供了三种不一样的方法:进程内会话状态、做为 Windows 服务的进程外会话状态和 SQL Server 数据库中的进程外会话状态。每种方法都有本身的优势,但进程内会话状态是迄今为止速度最快的解决方案。若是只在会话状态中存储少许易失数据,则建议您使用 进程内提供程序。进程外解决方案主要用于跨多个处理器或多个计算机缩放应用程序,或者用于服务器或进程从新启动时不能丢失数据的状况。有关更多信息,请参 见 ASP.NET 状态管理。

25.   不使用没必要要的Server Control

  ASP.net中,大量的服务器端控件方便了程序开发,但也可能带来性能的损失,由于用户每操做一次服务器端控件,就产生一次与服务器端的往返过程。所以,非必要,应当少使用Server Control。

26.   ASP.NET应用程序性能测试

  在对ASP.NET应用程序进行性能测试以前,应确保应用程序没有错误,并且功能正确。具体的性能测试能够采用如下工具进行:Web Application Strees Tool (WAS)是Microsoft发布的一个免费测试工具,能够从http://webtool.rte.microsoft.com/上下载。它能够模拟成百上千个用户同时对web应用程序进行访问请求,在服务器上造成流量负载,从而达到测试的目的,能够生成平均TTFB、平均TTLB等性能汇总报告。

  Application Center Test (ACT) 是一个测试工具,附带于Visual Studio.NET的企业版中,是Microsoft正式支持的web应用程序测试工具。它可以直观地生成图表结果,功能比WAS多,但不具有多个客户 机同时测试的能力。

  如今写一个asp.net的web应用程序变得很是的简单,许多的程序员都不肯花时间去构建一个性能良好的应用程序。本文将要讨论提升web应 用程序性能的十大方法。我将不限于只讨论asp.net应用程序的内容,由于它们只是web应用程序的一个子集。本文也不能提供一个完整提升web应用程 序性能的指南,由于这须要一本书的篇幅。本文只提供一个提升web应用程序性能的良好的开端。(剩下的只有咱们本身慢慢研究了)。

  在工做这外,我常常去攀岩,在每次攀岩以前,我都会重温一下攀岩线路图及看一下前面的成功的攀岩者的建议。由于咱们须要它们的成功经验。一样的,当你须要修改某个有性能问题的程序或者是要开发一个高性能的站点时,你也须要学习怎么样写一个高性能的web应用程序。

  我我的的经验主要来源于在微软的ASP.NET组担任程序经理,运行和管理www.asp.net网站,和协助开发Community Server(它是ASP.NET Forums,.Text, 和 nGallery的集成升级版本软件)。我想这些经验能我让来帮助你们。

  你也许会想到把你的应用程序划分红不一样的逻辑层。你也可能听过三层物理架构或N层架构,这是最经常使用的架构模式,它把不一样的程序功能物理的分配给 各个硬件来执行。这样,若是咱们想提升应用程序的性能的话,加一些硬件就能够达到目的了。按理说这种方法能提升应用程序的性能,可是咱们应该避免使用这种 方法。因此,只要有可能,咱们都应该把ASP.NET页面和它用到的组件放到一个应用程序中运行。

  由于分布式的布署,要用到Web Services或者Remoting,它将使应用程序的性能降低20%或者更多。

  对于数据层有点不一样,最好仍是把它独立出来布署,用一个单独的硬件来运行它。虽然这样,可是数据库仍然是应用程序性能的瓶颈。所以,当你想优化你的程序的时候,首先想到的地方就应该是优化数据层了。

  在修改应用程序的出现性能问题的地方以前,你要先确认出问题的地方的程序看起来很严密,性能分析器对于查找应用程序哪些地方花费了多长时间很是有用。这些地方是咱们用直觉感受不到的。

  本文讨论两种类型的性能优化:一种是大的性能优化(big optimizations),如用ASP.NET的Cache;另外一种是小的性能优化(tiny optimizations)。小幅的性能优化有时候很是有用。你只对你的代码做一个小的改到,而后一次调用它一千或一万次。做一次大的性能优化,你会发 生你的应用程序的速度会有一个很大的提高。做一次小的性能优化,也许每次请求只能提升一微秒,可是若是天天的请求量很大的话,那么应用程序就有很显著的性能提高。

  数据层的性能

  当你要优化一个应用程序的性能的时候,你能够按下面的顺序工做:你的代码要访问数据库?若是要,访问数据库频率怎么样?一样,这种测试方法也可 以用在用Web Service或.NET Remoting的程序代码中。本文将不讨论用Web Services和Remoting的程序优化的问题。

  若是在你的代码中有一段必须访问数据库的请求,而你在其它的地方又看到实现一样的功能 的代码,那么你首先要优化它。修改和完善继续测试,除非你有一个很是大的性能问题,你的时间最好花在优化查询,链接数据库,返回数据集的大小,以及一次查询往返回的时间上。

  根据经验的总结,让咱们来看看十个能帮助你提高你的应用程序性能的经验,我将按将它们提高效率的多少从大到小小依次说明。

  1、返回多个数据集

  检查你的访问数据库的代码,看是否存在着要返回屡次的请求。每次往返下降了你的应用程序的每秒可以响应请求的次数。经过在单个数据库请求中返回多个结果集,能够减小与数据库通讯的时间,使你的系统具备扩展性,也能够减小数据库服务器响应请求的工做量。

  若是你是用动态的SQL语句来返回多个数据集,那我建议你用存储过程来替代动态的SQL语句。是否把业务逻辑写到存储过程当中,这个有点争议。但 是我认为,把业务逻辑写到存储过程里面能够限制返回结果集的大小,减少网络数据的流量,在逻辑层也不用在过滤数据,这是一个好事情。

  用SqlCommand对象的ExecuteReader方法返回一个强类型的业务对象,再调用NextResult方法来移动数据集指针来定 位数据集。示例一演示了一个返回多个ArrayList强类型对象的例子。只从数据库中返回你须要的数据能够大大的减少你的服务器所耗用的内存。

  2、对数据进行分页

  ASP.NET的DataGrid有一个很是有用的功能:分页。若是DataGrid容许分页,在某一时刻它只下载某一页的数据,另外,它有一个数据分页的浏览导航栏,它让你能够选择浏览某一页,并且每次只下载一页的数据。

  可是它有一个小小的缺点,就是你必须把全部的数据都绑定到DataGrid中。也就是说,你的数据层必须返回全部的数据,而后DataGrid 再根据当前页过滤出当前页所须要的数据显示出来。若是有一个一万条记录的结果集要用DataGrid进行分页,假设DataGrid每页只显示25条数 据,那就意味着每次请求都有9975条数据都是要丢弃的。每次请求都要返回这么大的数据集,对应用程序的性能影响是很是大的。

  一个好的解决方案是写一个分页的存储过程,例子2是一个用于对Northwind数据库orders表的分页存储过程。你只须要传当前页码,每页显示的条数两个参数进来,存储过程会返回相应的结果。

  在服务器端,我专门写了一个分页的控件来处理数据的分页,在这里,我用了第一个方法,在一个存储过程里面返回了两个结果集:数据记录总数和要求的结果集。

  返回的记录总数取决于要执行查询,例如,一个where条件能够限制返回的结果集的大小。由于在分页界面中必需要根据数据集记录的大小来计算总 的页数,因此必需要返回结果集的记录数。例如,若是一共有1000000条记录,若是用where条件就能够过滤成只返回1000条记录,存储过程的分页 逻辑应该知道返回那些须要显示的数据。

  3、链接池

  用TCP来链接你的应用程序与数据库是一件昂贵的事情(很费时的事情),微软的开发者能够经过用链接池来反复的使用数据库的链接。比起每次请求 都用TCP来连一次数据库,链接池只有在不存在有效的链接时才新建一个TCP链接。当关闭一个链接的时候,它会被放到池中,它仍然会保持与数据库的链接, 这样就能够减小与数据库的TCP链接次数。

  固然,你要注意那些忘记关的链接,你应在每次用完链接后立刻关闭它。我要强调的是:不管什么人说.NET Framework中的GC(垃圾收集器)总会在你用完链接对象后调用链接对象的Close或者Dispose方法显式的关闭你的链接。不要指望CLR会 在你想象的时间内关掉链接,虽然CLR最终都要销毁对象和关闭边接,可是咱们并不能肯定它到底会在何时作这些事情。

  要用链接池优化,有两条规则,第一,打开链接,处理数据,而后关闭链接。若是你必须在每次请求中屡次打开或关闭链接,这好过一直打开一个边接, 而后把它传到各个方法中。第二,用相同的链接字符串(或者用相同的用户标识,当你用集成认证的时候)。若是你没有用相同的链接字符串,如你用基于登陆用户 的链接字符串,这将不能利用链接池的优化功能。若是你用的是集成的论证,由于用户不少,因此你也不能充分利用链接池的优化功能。.NET CLR提供了一个数据性能计数器,它在咱们须要跟踪程序性能特性的时候很是有用,固然也包括链接池的跟踪了。

  不管你的应用程序何时要连在另外一台机子的资源,如数据库,你都应该重点优化你连资源所花的时间,接收和发送数据的时间,以及往返回之间的次数。优化你的应用程序中的每个处理点(process hop),它是提升你的应用的性能的出发点。

  应用程序层包含与数据层链接,传送数据到相应的类的实例以及业务处理的逻辑。例如,在Community Server中,要组装一个Forums或者Threads集合,而后应用业务逻辑,如受权,更重要的,这里要完成缓存逻辑。

  4、 ASP.NET缓存API

  在写应用程序以前,你要作的第一件事是让应用程序最大化的利用ASP.NET的缓存功能。

  若是你的组件是要在Asp.net应用程序中运行,你只要把System.Web.dll引用到你的项目中就能够了。而后用HttpRuntime.Cache属性就可访问Cache了(也能够经过Page.Cache或HttpContext.Cache访问)。

  有如下几条缓存数据的规则。第一,数据可能会被频繁的被使用,这种数据能够缓存。第二,数据的访问频率很是高,或者一个数据的访问频率不高,但 是它的生存周期很长,这样的数据最好也缓存起来。第三是一个经常被忽略的问题,有时候咱们缓存了太多数据,一般在一台X86的机子上,若是你要缓存的数据 超过800M的话,就会出现内存溢出的错误。因此说缓存是有限的。换名话说,你应该估计缓存集的大小,把缓存集的大小限制在10之内,不然它可能会出问 题。在Asp.net中,若是缓存过大的话也会报内存溢出错误,特别是若是缓存大的DataSet对象的时候。

  这里有几个你必须了解的重要的缓存机制。首先是缓存实现了“最近使用”原则( a least-recently-used algorithm),当缓存少的时候,它会自动的强制清除那些无用的缓存。其次 “条件依赖”强制清除原则(expiration dependencies),条件能够是时间,关键字和文件。以时间做为条件是最经常使用的。在asp.net2.0中增长一更强的条件,就是数据库条件。当 数据库中的数据发生变化时,就会强制清除缓存。要更深刻的了解数据库条件依赖请看Dino Esposito 在MSDN杂志2004年七月刊的Cutting Edge专栏文章。

  5、 预请求缓存

  在前面,我提到过即便咱们只对某些地方做了一个小小的性能改进也能够得到大的性能提高,我很是喜欢用预请求缓存来提高程序的性能。

  虽然Cache API设计成用来保存某段时间的数据,而预请求缓存只是保存某个时期的某个请求的内容。若是某个请求的访问频率高,并且这个请求只须要提取,应用,修改或者更新数据一次。那么就能够预缓存该请求。咱们举个例子来讲明。

  在CS的论坛应用程序中,每个页面的服务器控件都要求获得用于决定它的皮肤(skin)的自定义的数据,以决定用哪一个样式表及其它的一些个性 化的东西。这里面的某些数据可能要长时间的保存,有些时间则否则,如控件的skin数据,它只须要应用一次,然后就能够一直使用。

  要实现预请求缓存,用Asp.net 的HttpContext类,HttpContext类的实例在每个请求中建立,在请求期间的任何地方均可以经过 HttpContext.Current属性访问。HttpContext类有一个Items集合属性,在请求期间全部的对象和数据都被添加到这个集合中 缓存起来。和你用Cache缓存访问频率高数据同样,你能够用HttpContext.Items缓存那些每一个请求都要用到的基础数据。它背后的逻辑很简 单:咱们向HttpContext.Items中添加一个数据,而后再从它里面读出数据。

  6、 后台处理

  经过上面的方法你的应用程序应该运行得很快了,是否是?可是在某些时候,程序中的一次请求中可能要执行一个很是耗时的任务。如发送邮件或者是检查提交的数据的正确性等。

  当咱们把asp.net Forums 1.0集成在CS中的时侯,发现提交一个新的帖子的时候会很是的慢。每次新增一个帖子的时侯,应用程序首先要检查这个帖子是否是重复提的,而后用 “badword”过滤器来过滤,检查图片附加码,做帖子的索引,把它添加到合适的队列中,验证它的附件,最后,发邮件到它的订阅者邮件箱中。显然,这个 工做量很大。

  结果是它把大量的时间都花在作索引和发送邮件中了。作帖子的索引是一项很耗时的操做,而发邮件给订阅都须要链接到SMTP服务,而后给每个订阅者都发一封邮件,随着订阅用户的增长,发送邮件的时间会更长。

  索引和发邮件并不须要在每次请求时触发,理想状态下,咱们想要批量的处理这些操做,每次只发25封邮件或者每隔5分钟把全部的要发的新邮件发一次。咱们决定使用与数据库原型缓存同样的代码,可是失败了,因此又不得不回到VS.NET 2005。

  咱们在System.Threading命名空间下找到了Timer类,这个类很是有用,但却不多有人知道,Web开发人员则更少有人知道了。 一旦他建了该类的实例,每隔一个指定的时间,Timer类就会从线程池中的一个线程中调用指定的回调函数。这意味着你的asp.net应用程序能够在没有 请求的时候也能够运行。这就是后以处理的解决方案。你就可让作索引和发邮件工做在后台运行,而不是在每次请求的时候必须执行。

  后台运行的技术有两个问题,第一是,当你的应用程序域卸载后,Timer类实例就会中止运行了。也就是不会调用回调方法了。另外,由于CLR的 每一个进程中都有许多的线程在运行,你将很难让Timer得到一个线程来执行它,或者能执行它,但会延时。Asp.net层要尽可能少的使用这种技术,以减小 进程中线程的数量,或者只让请求用一小部分的线程。固然若是你有大量的异步工做的话,那就只能用它了。

  这里没有足够的空间有贴代码,你能够从http://www.rob-howard.net/中下载示例程序,请下载Blackbelt TechEd 2004的示例程序。

  7、 页面输出缓存和代理服务

  Asp.net是你的界面层(或者说应该是),它包含页面,用户控件,服务器控件(HttpHandlers 和HttpModules)以及它们生成的内容。若是你有一个Asp.net页面用来输出html,xml,imgae或者是其它的数据,对每个请求你 都用代码来生成相同的输出内容,你就颇有必要考虑用页面输出缓存了。

  你只要简单的把下面的这一行代码复制到你的页面中就能够实现了:

  你就能够有效的利用第一次请求里生成的页面输出缓存内容,60秒后从新生成一道页面内容。这种技术其实也是运用一些低层的Cache API来实现。用页面输出缓存有几个参数能够配置,如上面所说的VaryByParams参数,该参数表示何时触发重输出的条件,也能够指定在 Http Get或Http Post 请求模式下缓存输出。例如当咱们设置该参数为VaryByParams=”Report”的时候,default.aspx?Report=1或者 default.aspx?Report=2请求的输出都会被缓存起来。参数的值能够是多个用分号隔开参数。

  许多人都没有意识到当用页面输出缓存的时候,asp.net也会生成HTTP头集(HTTP Header)保存在下游的缓存服务器中,这些信息能够用于Microsoft Internet安全性中以及加速服务器的响应速度。当HTTP缓存的头被重置时,请求的内容会被缓在网络资源中,当客户端再次请求该内容时,就不会再从 源服务器上得到内容了,而直接从缓存中得到内容。

  虽然用页面输出缓存不提升你的应用程序性能,可是它能减小了从的服务器中加载已缓存页面内容的次数。固然,这仅限于缓存匿名用户能够访问的页面。由于一旦页面被缓存后,就不能再执行受权操做了。

  8、 用IIS6.0的Kernel Caching

  若是你的应用程序没用运行在IIS6.0(windows server 2003)中,那么你就失去了一些很好的提升应用程序性能的方法。在第七个方法中,我讲了用页面输出缓存提升应用程序的性能的方法。在IIS5.0中,当 一个请求到来到IIS后,IIS会把它转给asp.net,当应用了页面输出缓存时,ASP.NET中的HttpHandler会接到该请求, HttpHandler从缓存中把内容取出来并返回。

  若是你用的是IIS6.0,它有一个很是好的功能就是Kernel Caching,并且你没必要修改asp.net程序中任何代码。当asp.net接到一个已缓存的请求,IIS的Kernel Cache会从缓存中获得它的一份拷贝。当从网络中传来一个请求的时,Kernel层会获得该请求,若是该请求被缓存起来了,就直接把缓存的数据返回,这 样就完工了。这就意味着当你用IIS的Kernel Caching来缓存页面输出时,你将得到不可置信的性能提高。在开发VS.NET 2005的 asp.net时有一点,我是专门负asp.net性能的程序经理,个人程序员用了这个方法,我看了全部日报表数据,发现用kernel model caching的结果老是最快的。它们的一个共同的特征就是网络的请求和响应量很大,但IIS只占用了5%的CPU资源。这是使人惊奇的。有许多让你使用 用IIS6.0的理由,但kernel cashing是最好的一个。

  9、 用Gzip压缩数据

  除非你的CPU占用率过高了,才有必要用提高服务器性能的技巧。用gzip压缩数据的方法能够减小你发送到服务端的数据量,也能够提升页面的运 行速度,同时也减小了网络的流量。怎么样更好的压缩数据取决于你要发送的数据,还有就是客户端的浏览器支不支持(IIS把用gzip压缩后的数据发送到客 户端,客户端要支持gzip才能解析,IE6.0和Firefox都支持)。这样你的服务器每秒能多响应一些请求,一样,你也减小了发送响应的数据量,也 就能多发送一些请求了。

  好消息,gzip压缩已经被集成在IIS6.0中了,它比IIS5.0中gzip更好。不幸的是,在IIS6.0中启用gzip压缩,你不能在 IIS6.0的属性对话中设置。IIS开发团队把gzip压缩功能开发出来了,但他们却忘了在管理员窗口中让管理员能很方便的启用它。要启用gzip压 缩,你只能深刻IIS6.0的xml配置文件中修改它的配置。

  除了阅读本文之外,只好再看看Brad Wilson写的《IIS6 压缩》一文(http://www.dotnetdevs.com/articles/IIS6compression.aspx);另外还有一篇介绍aspx压缩基础知识的文章,Enable ASPX Compression in IIS。可是要注意,在IIS6中动态压缩和kernel cashing是互斥的。

  10、 服务器控件的ViewState

  ViewState是asp.net中的一个特性,它用于把生成页面要用的一状态值保存在一个隐藏域中。当页面被回传到服务器时,服务器要解 析,校验和应用ViewState中的数据以还原页面的控件树。ViewState是一个很是有用的特性,它能持久化客户端的状态而不用cookie或者 服务器的内存。大部分的服务器控件都是用ViewState来持久化那些在页面中与用户交互的元素的状态值。例如,用以保存用于分页的当前页的页码。

  用ViewState会带来一些负面的影响。首先,它加大的服务器的响应和请求的时间。其次,每次回传时都增长了序列化和反序列化数据的时间。最后,它还消耗了服务器更多的内存。

  许多的服务器控件很趋于使用ViewState,如众所周知的DataGrid,而有时候是没有必须使用的。默认状况下是容许使用 ViewState的,若是你不想使用ViewState的话,你能够在控件或页面级别把关闭它。在控件中,你只要把EnableViewState属性 设为False就能够了;你也能够在页面中设置,使它的范围扩展到整个页面中:

  若是页面无需回传或者每次请求页面只是呈现控件。你就应该在页面级别中把ViewState关掉。

  总结

  我只是提供我几个我认为有助于提升写高性能的asp.net应用程序的技巧,本文提到的提升asp.net性能的技巧只是一个起步,更多的信息 请参考《Improving ASP.NET Performance》一书。只有经过本身的实践,你才能找到对你的项目最有帮助的技巧。然而,在你的开发旅程中,这些技巧能够起一些指导性的做用。在 软件开发中,这些都不是绝对有用的,由于各个项目都不同。

  服务器操做系统"管理工具"中的"性能"计数器,能够对服务器进行监测以了解应用程序性能。

相关文章
相关标签/搜索