65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 高性能应用程序最佳实践

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (178投票s)

2006 年 2 月 17 日

13分钟阅读

viewsIcon

565798

本文列出了可用于最大化 ASP.NET 应用程序性能的技术。它提供了常见问题、设计指南和编码技巧,以构建最优和健壮的解决方案。

引言

ASP.NET 比传统的 ASP 强大得多,但了解如何利用其强大功能来构建高效、可靠和健壮的应用程序至关重要。在本文中,我尝试重点介绍可以用来最大化 ASP.NET 页面性能的关键技巧。列表可以更长,我只强调其中最重要的部分。

1. 开发前规划和研究

研究并探索 .NET 如何真正使您受益。.NET 在应用程序设计和开发的各个层面都提供了各种解决方案。了解您的情况以及此丰富开发环境支持的各种方法的优缺点至关重要。Visual Studio 是一个全面的开发包,提供了实现相同逻辑的许多选项。仔细检查每个选项并找到最适合当前任务的最优解决方案非常重要。使用分层来将应用程序逻辑逻辑上划分为表示层、业务层和数据访问层。这不仅有助于您创建可维护的代码,还允许您单独监控和优化每个层的性能。清晰的逻辑分离也为扩展应用程序提供了更多选择。尝试减少代码隐藏文件中的代码量,以提高可维护性和可扩展性。

2. 字符串连接

如果处理不当,字符串连接会严重降低应用程序的性能。您可以通过两种方式连接字符串。

  • 首先,通过使用字符串并将新字符串添加到现有字符串。但是,此操作的成本非常高(尤其是在循环中连接字符串时)。当您将一个字符串添加到现有字符串时,框架会将现有数据和新数据复制到内存中,删除现有字符串,然后将数据读入一个新字符串。对于较长的字符串连接操作,此操作可能非常耗时且成本高昂。
  • 第二种也是更好的连接字符串的方法是使用 StringBuilder 类。下面是两种方法的示例。如果您正在考虑进行任何类型的字符串连接,请务必单独测试这两种例程。结果可能会让您感到惊讶。

    'Concatenation using String Class 
    
    Response.Write("<b>String Class</b>")
    Dim str As String = ""
    Dim startTime As DateTime = DateTime.Now
    Response.Write(("<br>Start time:" + startTime.ToString()))
    Dim i As Integer
    For i = 0 To 99999
    str += i.ToString()
    Next i
    Dim EndTime As DateTime = DateTime.Now
    Response.Write(("<br>End time:" + EndTime.ToString()))
    Response.Write(("<br># of time Concatenated: " + i.ToString))

    结果:完成 100,000 次连接花费了 4 分 23 秒。

    String
    • 开始时间:2006/2/15 10:21:24 AM
    • 结束时间:2006/2/15 10:25:47 AM
    • 连接次数:100000

    'Concatenation using StringBuilder
    
     Response.Write("<b>StringBuilder Class</b>")
     Dim strbuilder As New StringBuilder()
     Dim startTime As DateTime = DateTime.Now
     Response.Write(("<br>Start time:" + startTime.ToString()))
     Dim i As Integer
     For i = 0 To 99999
     strbuilder.Append(i.ToString())
     Next i
     Dim EndTime As DateTime = DateTime.Now
     Response.Write(("<br>Stop time:" + EndTime.ToString()))
     Response.Write(("<br># of time Concatenated: " + i.ToString))

    结果:完成 100,000 次连接花费不到 1 秒。

    StringBuilder
    • 开始时间:2006/2/15 10:31:22 AM
    • 结束时间:2006/2/15 10:31:22 AM
    • 连接次数:100000

这是 ASP.NET 在传统 ASP 方面提供极高性能优势的众多情况之一。

3. 避免与服务器进行往返通信

您可以使用以下技巧来避免不必要的与 Web 服务器的往返通信:

  • 尽可能实现 Ajax UI。其理念是避免完全刷新页面,仅更新需要更改的部分。我认为 Scott 的文章 提供了有关如何实现 Ajax Atlas 和 <atlas:updatepanel> 控件的绝佳信息。
  • 使用客户端脚本。客户端验证可以帮助减少处理用户请求所需的往返通信。在 ASP.NET 中,您还可以使用客户端控件来验证用户输入。
  • 使用 Page.IsPostBack 属性,确保仅在页面首次加载时执行页面初始化逻辑,而不是在响应客户端回发时执行。

    If Not IsPostBack Then 
    LoadJScripts()
    End If
  • 在某些情况下,执行回发事件处理是不必要的。您可以使用客户端回调从服务器读取数据,而不是执行完整的往返通信。 点击此处了解详情。

4. 仅在必要时保存 ViewState

ViewState 主要由服务器控件使用,用于保留仅在向自身回发数据的页面上的状态。信息被传递到客户端并在隐藏变量中读取。对于不需要 ViewState 的页面,它会带来不必要的开销。随着 ViewState 的大小增长,它会影响垃圾回收的性能。您可以通过遵循以下技巧来优化应用程序使用 ViewState 的方式:

不需要 ViewState 的情况

ViewState 在 ASP.NET 中默认启用。您可能不需要 ViewState ,因为您的页面是仅输出的,或者因为您在每次请求时显式重新加载数据。在以下情况下,您不需要 ViewState

  • 您的页面不回发。如果页面不将信息回发给自己,如果页面仅用于输出,并且如果页面不依赖于响应处理,则您不需要 ViewState。
  • 您不处理服务器控件事件。如果您的服务器控件不处理事件,并且您的服务器控件没有动态或数据绑定属性值,或者它们在每次请求时都在代码中设置,则您不需要 ViewState
  • 每次刷新页面时重新填充控件。如果您忽略旧数据,并在每次刷新页面时重新填充服务器控件,则您不需要 ViewState

禁用 viewstate

有几种方法可以在不同级别禁用 ViewState

  • 要禁用页面上单个控件的 ViewState ,请将控件的 EnableViewState 属性设置为 false
  • 要禁用单个页面的 ViewState ,请将 @ Page 指令中的 EnableViewState 属性设置为 false。例如:

    <%@ Page EnableViewState="false" %> 
  • 要禁用特定应用程序的 ViewState ,请在应用程序的 Web.config 文件中使用以下元素:

    <pages enableViewState="false" />
  • 要禁用 Web 服务器上所有应用程序的 ViewState ,请如下配置 Machine.config 文件中的 <pages> 元素:

    <pages enableViewState="false" />

确定 ViewState 的大小

通过为页面启用跟踪,您可以监控每个控件的 ViewState 大小。您可以使用此信息来确定 ViewState 的最优大小,或者确定可以禁用 ViewState 的控件。

谨慎使用会话变量

避免在会话变量中存储过多数据,并确保您的会话超时设置合理。这可能会占用大量服务器内存。请记住,存储在会话变量中的数据可能在用户关闭浏览器后很长时间仍然存在。过多的会话变量可能导致服务器不堪重负。如果您在特定页面或应用程序中不使用会话变量,请禁用会话状态。

  • 要为页面禁用会话状态,请将 @ Page 指令中的 EnableSessionState 属性设置为 false。例如:

    <%@ Page EnableSessionState="false" %> 
  • 如果页面需要访问会话变量但不会创建或修改它们,请将 @ Page 指令中的 EnableSessionState 属性设置为 ReadOnly。例如:

    <%@ Page EnableSessionState="ReadOnly" %>   
  • 要禁用特定应用程序的会话状态,请在应用程序的 Web.config 文件中使用以下元素:

    <sessionState mode='Off'/>
  • 要禁用 Web 服务器上所有应用程序的会话状态,请在 Machine.config 文件中使用以下元素:

    <sessionState mode='Off'/>

6. 使用 Server.Transfer

使用 Server.Transfer 方法在同一应用程序内的页面之间进行重定向。在页面中使用此方法,并使用 Server.Transfer 语法,可以避免不必要的客户端重定向。考虑使用 Server.Transfer 而不是 Response.Redirect。但是,您不能总是用 Server.Transfer 替换 Response.Redirect 调用。如果您在重定向过程中需要进行身份验证和授权检查,请使用 Response.Redirect 而不是 Server.Transfer ,因为这两种机制不尽相同。当您使用 Response.Redirect 时,请确保使用接受布尔值第二个参数的重载方法,并将值传递为 false 以确保不引发内部 exception。另请注意,您只能使用 Server.Transfer 将控件转移到同一应用程序内的页面。要转移到其他应用程序中的页面,您必须使用 Response.Redirect

7. 在适当的时候使用服务器控件,并避免创建深度嵌套的控件

HTTP 协议是无状态的;但是,服务器控件提供了一个丰富的编程模型,该模型通过使用 ViewState 在页面请求之间管理状态。但是,天下没有免费的午餐,服务器控件需要固定的处理量来建立控件及其所有子控件。这使得服务器控件相对于 HTML 控件或静态文本而言成本较高。当您不需要丰富的交互时,请将服务器控件替换为您想要呈现的用户界面的内联表示。如果满足以下条件,最好替换服务器控件:

  • 您不需要在回发之间保留状态
  • 控件中显示的数据是静态的,或者控件显示只读数据
  • 您不需要在服务器端对控件进行编程访问

服务器控件的替代方法包括简单渲染、HTML 元素、内联 Response.Write 调用以及原始内联尖括号(<% %>)。平衡权衡非常重要。如果开销可以接受并且您的应用程序在性能目标范围内,请避免过度优化。

控件的深度嵌套层次结构会增加创建服务器控件及其子控件的成本。深度嵌套的层次结构会产生额外的处理,而这些处理可以通过使用不同的设计(使用内联控件)或使用更扁平的服务器控件层次结构来避免。当您使用 RepeaterDataListDataGrid 等控件时,这一点尤其重要,因为它们会在容器中创建额外的子控件。

8. 选择适合您解决方案的数据查看控件

根据您在 Web Forms 页面中显示数据的方式,便利性和性能之间通常存在显著的权衡。在使用控件之前,请始终比较其优缺点。例如,您可以使用以下三个控件(DataGridDataListRepeater)中的任何一个来显示数据,您的任务是找出哪个控件将为您带来最大的收益。DataGrid 控件可以是一种快速简便的数据显示方式,但它在性能方面通常成本最高。自己通过生成适当的 HTML 来渲染数据在某些简单情况下可能有效,但自定义和浏览器定位可能会迅速抵消所涉及的额外工作。Repeater Web 服务器控件是在便利性和性能之间的一种折衷。它高效、可自定义且可编程。

9. 优化代码和异常处理

要优化昂贵的循环,请在性能关键的代码路径中使用 For 而不是 ForEach。此外,不要依赖代码中的 exception,并编写避免 exception 的代码。由于 exception 会严重影响性能,因此您绝不应将其用作控制正常程序流的方式。如果可以在代码中检测到会导致 exception 的情况,请这样做。在处理该情况之前,不要捕获 exception 本身。不要使用 exception 来控制逻辑。数据库连接打开失败是一个 exception,但用户输错密码只是一个需要处理的情况。常见场景包括检查 null、将值赋给一个将解析为数字值的 String,或者在应用数学运算之前检查特定值。以下示例演示了可能导致 exception 的代码和测试情况的代码。两者产生相同的结果。

'Unnecessary use of exception

 Try
     value = 100 / number
Catch ex As Exception
    value = 0
End Try

' Recommended code

If Not number = 0 Then
    value = 100 / number
Else
    value = 0
End If

检查 null 值。如果对象可能为 null,请检查以确保它不是 null,而不是抛出 exception。这通常发生在您从 ViewState、会话状态、应用程序状态或缓存对象以及查询字符串和表单字段变量中检索项时。例如,不要使用以下代码访问会话状态信息:

'Unnecessary use of exception

Try
value = HttpContext.Current.Session("Value").ToString
Catch ex As Exception
Response.Redirect("Main.aspx", False)
End Try
 
'Recommended code 

If Not HttpContext.Current.Session("Value") Is Nothing Then
value = HttpContext.Current.Session("Value").ToString
Else
Response.Redirect("Main.aspx", False)
End If

10. 使用 DataReader 进行快速高效的数据绑定

如果您不需要缓存数据、显示只读数据,并且需要尽快将数据加载到控件中,请使用 DataReader 对象。DataReader 是检索只读数据(单向)的最佳选择。将数据加载到 DataSet 对象然后将 DataSet 绑定到控件会将数据移动两次。此方法还会产生构建 DataSet 的相对较大的开销。此外,当您使用 DataReader 时,可以使用特定类型的专用方法来检索数据以获得更好的性能。

11. 高效分页

允许用户请求和检索超过他们能消耗的数据量,这会对您的应用程序资源造成不必要的压力。这种不必要的压力会导致 CPU 利用率增加、内存消耗增加以及响应时间缩短。对于连接速度慢的客户端尤其如此。从可用性的角度来看,大多数用户不想看到数千行作为一个整体呈现。实现分页解决方案,仅从数据库检索所需数据,并减少后端数据库的工作量。您应该优化从数据库服务器返回到中间层 Web 服务器的行数。有关更多信息,请 阅读本文 以在数据库级别实现分页。如果您使用的是 SQL Server 2000,请也查看 本文。

12. 显式释放或关闭所有资源

为确保在发生异常时资源得到清理,请使用 try/finally 块。在 finally 子句中关闭资源。使用 try/finally 块可确保即使发生 exception,资源也会被释放。在需要时打开连接,并在完成后尽快关闭它。您的座右铭应该是“进入、获取/保存数据、退出”。如果您使用不同的对象,请确保调用对象的 Dispose 方法或提供的方法的 Close 方法。未能调用 Close Dispose 会延长对象在内存中的生命周期,直到客户端停止使用它。这会延迟清理并可能导致内存压力。数据库连接和文件是应显式关闭的共享资源的示例。

Try
_con.Open()
Catch ex As Exception
Throw ex
Finally
If Not _con Is Nothing Then
_con.Close()
End If
End Try

13. 禁用跟踪和调试

在部署应用程序之前,请禁用跟踪和调试。跟踪和调试可能会导致性能问题。在生产环境中运行应用程序时不建议使用跟踪和调试。您可以使用以下语法在 Machine.configWeb.config 中禁用跟踪和调试:

<configuration>
   <system.web>
      <trace enabled="false" pageOutput="false" />
      <compilation debug="false" />
   </system.web>
</configuration>

14. 预编译页面并禁用 AutoEventWireup

通过预编译页面,用户无需经历 ASP.NET 文件的批量编译;这将提高用户体验到的性能。

此外,在 Machine.config 文件中将 AutoEventWireup 属性设置为 false 意味着页面不会将方法名称与事件匹配并进行连接(例如,Page_Load)。如果页面开发人员希望使用这些事件,他们将需要覆盖基类中的方法(例如,他们将需要覆盖 Page 的 Page.OnLoad 以用于页面加载事件,而不是使用 Page_Load 方法)。如果禁用 AutoEventWireup,通过将事件连接留给页面作者而不是自动执行,您的页面将获得轻微的性能提升。

15. 使用存储过程和索引

在大多数情况下,使用编译后的存储过程而不是临时查询可以获得额外的性能提升。

确保为表创建索引,并明智地选择索引。尝试使用索引优化向导,让它报告您认为最适合创建索引的候选对象。您不必遵循它的所有建议,但它可能会揭示有关您的结构或数据的信息,从而帮助您选择更合适的索引。

  • 在 SQL Server Management Studio (SQL Server 2005) 中,突出显示您的查询。然后,从 Query 菜单中,单击 Analyze Query in Database Engine Tuning Advisor。
  • 您可以在 SQL Server 2000 中执行类似操作来运行索引优化向导吗?在 Query Analyzer 中,突出显示您的查询。从 Query 菜单中,单击 Index Tuning Wizard。

参考文献

© . All rights reserved.