使用服务的应用程序的性能注意事项

更新:2007 年 11 月

本主题讨论在使用 ASP.NET 成员资格、角色、配置文件属性、会话状态、Web 部件个性化设置和站点导航时的性能优化。

成员资格

本节包含关于有效地使用 ASP.NET 成员资格的信息。

有效地收集成员资格列表

在调用 Membership 类的方法时,应调用接受 PageIndex 和 PageSize 成员的重载方法。这些重载方法运行得更快,因为它们减少了从 Web 服务器传输的数据。Membership 类的 GetAllUsers 方法就是一个这样的方法。不带参数调用 GetAllUsers 方法将返回所有成员资格用户。相比之下,调用 GetAllUsers 重载只返回一页用户,从而减少了该页处理的数据量。

在获取联机用户数量时缓存结果

可以调用 GetNumberOfUsersOnline 方法来显示被视为网站活动用户的数量。此方法在每次被调用时检查成员资格表中的每一行,如果数据库中的用户数量很大,这可能是严重的性能损失。应该仅在必要时才调用该方法,如果不需要确切的计数,可在一段时间内缓存该值。

角色

默认情况下,GetRolesForUser 方法在每次加载页时自动被调用,并返回用户所属的角色。角色存储在 RolePrincipal 对象中的字典中。当网页正在运行时,对角色的检查是使用此字典来执行的。为了防止在每次页加载时访问提供程序,从而减少服务器处理时间,可将应用程序的 Web.config 文件中的 CacheRolesInCookie 属性设置为 true。这样导致将用户角色列表存储在 Cookie 中。在后续的页加载时,可以从 Cookie 读取角色信息而不必对提供程序进行调用。

可以在代码中使用 GetRolesForUser 方法、IsInRole 属性和 GetRoles 方法验证角色中的成员资格。通过了解这些方法如何交互,可以为应用程序编写对于所需执行的任务来说工作得最有效的代码。

GetRolesForUser 方法始终访问提供程序。当未启用 Cookie 缓存时,对 IsInRole 方法的调用始终在第一次请求页时导致对 GetRolesForUser 方法的调用。如果启用了 Cookie 缓存,对 IsInRole 方法的调用将改为使用缓存在 Cookie 中的角色信息。无论是否启用 Cookie 缓存,对 GetRoles 方法的第一次调用都会导致对 GetRolesForUser 方法的调用。但是网页上对 GetRoles 方法的后续调用将使用缓存在 RolePrincipal 中的角色信息。

ms228100.alert_note(zh-cn,VS.90).gif说明:

将敏感信息存储在 Cookie 中可能会向用户公开该信息。为了最大程度的安全性,请不要启用 Cookie 缓存。

配置文件属性

如果代码访问任何配置文件属性,那么在加载页时,配置文件提供程序将读取当前用户的所有配置文件属性。(明确地说,提供程序将读取与该配置文件提供程序关联的所有属性。)如果任何配置文件属性值被更改,新的信息将在页卸载时被写回提供程序数据存储区。ASP.NET 能够确定整数和字符串等内部类型是否已更改,但是不能确定非内部类型是否已更改。

用于确定配置文件属性是否已保存的算法如下:

  • 如果所有配置文件属性类型都是内部类型并且都未发生更改,则不会将配置文件写到数据存储区。

  • 如果所有配置文件属性类型都是内部类型并且任何类型发生了更改,则将所有配置文件属性值写到数据存储区。

  • 如果任一属性不是内部类型,则 ASP.NET 不能确定值是否已更改。如果在代码中访问了该属性,则将该属性值写到数据存储区。

    ms228100.alert_note(zh-cn,VS.90).gif说明:

    在所有情况下,该决策都适合于特定提供程序的所有配置文件属性。

如果当前使用非内部属性类型,则在每个页请求结束时写入配置文件数据要花一些时间。可以通过以下方法减轻此开销:

  • 在配置文件中仅使用内部类型。

  • 将 <profile> 配置元素中的 automaticSaveEnabled 属性设置为 Off,并根据需要编写自定义代码来检测更改和保存属性值。

  • 编写自定义代码来处理 ProfileAutoSaving 事件,并在事件代码中确定配置文件属性是否发生了任何更改。如果没有属性发生更改,则取消自动保存操作,并将事件的 ContinueWithProfileAutoSave 属性设置为 false。

  • 有关更多信息,请参见为个别用户创建网站 (Visual Studio).

会话状态

在使用任何进程外会话状态模式时,性能是一个考虑事项(有关更多信息,请参见会话状态模式)。本节提供关于优化会话状态性能的信息。

减少会话状态读写开销

默认情况下,会话状态信息在每次页加载过程中加载。@ Page 指令中的 EnableSessionState 属性通过以下设置提供了对加载会话状态的控制:

  • True 在每次页加载时读取会话状态数据,并在已更改时保存该数据。ASP.NET 能够确定整数和字符串等内部类型是否已更改。如果所有会话状态值都是内部类型并且都未发生更改,则不保存会话。如果其中任一值不是内部类型,并且访问了任何非内部类型的会话值,则保存所有会话状态信息。这是因为 ASP.NET 不跟踪非内部类型是否已更改,并通过保存所有数据来做出最安全的选择。

  • False 不在加载页时读取会话状态数据。

  • ReadOnly 在每次页加载时读取会话状态数据,但是从不保存 - 即使在代码中对会话状态值进行了更改。

避免会话状态的锁争用

避免在需要会话状态的页上使用 <IFRAME> 元素。如果使用相同会话状态标识符对 ASP.NET 发出多个请求,并且这些请求全都针对 EnableSessionState 设置为 true 的 ASP.NET 页,则这些并行请求将相互争用与进程外会话状态关联的锁。在存在 <IFRAME> 元素的情况下,这意味着显示在一页上的多个 <IFRAME> 元素中的多个 ASP.NET 页会潜在地争用相同会话数据。结果是每个单独的请求将在服务器上序列化。

Web 部件个性化设置

当页运行时,将加载 ASP.NET 个性化设置信息。为了确定任一 Web 部件个性化设置信息是否已更改,ASP.NET 调用 Equals 方法将使用 PersonalizableAttribute 属性 (Attribute) 进行标记的属性 (Property) 的旧值和新值进行比较。

如果任何个性化设置属性值已更改,则保存个性化设置数据。对于整数等内部类型,Equals 方法直接比较旧的和新的属性值。但是对于非内部类型,ASP.NET 比较引用的值,但是不一定比较类型实例所维护的数据。

例如,对于 ArrayList 类型的属性,Equals 方法比较 ArrayList 引用的旧值和新值,但是不比较 ArrayList 对象的内容。如果 ArrayList 引用未更改(即使已有新的项被添加到该列表),则 Equals 方法将返回 true。在这种情况下,ArrayList 数据不会被保存。

用于非内部类型的此算法是有效的,但是在不保存数据方面是错误的。如果想要保存 ArrayList 对象等非内部类型的数据,可将 IsDirty 属性设置为 true(如果该控件是从 WebPart 派生的),或者调用接受控件作为参数的 SetPersonalizationDirty 方法。

站点导航

与小型站点地图相比,大型站点地图将降低性能。例如,在测试方案中,将节点数量从 100 增加到 1000(增加十倍)会将页加载时间增加三分之一。

与增加节点数量相比,基于角色筛选节点的安全修整带来更大的性能损失。例如,1000 个节点的站点地图的处理开销十倍于 100 个节点的站点地图。

用于减轻此影响的一些建议为:

  • 当启用安全修整时,建议的最大节点数为 150。

  • 设置站点地图的每个节点中的 Roles 属性。当此角色属性存在时,只要用户属于该属性中列出的角色之一,ASP.NET 就可以绕过与该 SiteMapNode 关联的 URL 的 URL 授权和文件授权。但是要注意,如果用户不属于指定的任何角色,则 ASP.NET 将回退到较慢的文件和 URL 授权检查。

  • 创建从 SiteMapProvider 类继承的类,并重写 IsAccessibleToUser 方法以便仅检查站点地图的每个节点中的 Roles 属性。这样可以加速筛选过程,因为它绕过了 URL 授权和文件授权。但是,此方法需要 Web 应用程序中的两个并行安全定义。第一个是 Web.config 文件中的授权信息(如果启用了 Windows 身份验证,则还包括用于文件授权的 NTFS 访问控制列表)。第二个是站点地图中的角色信息。应该对拆分安全信息的安全管理开销与站点地图处理方面的性能改进进行权衡。

有关更多信息,请参见 ASP.NET 站点地图安全性调整ASP.NET 授权

请参见

概念

保证 ASP.NET 站点导航的安全

ASP.NET 会话状态概述