问:使用 .js 和级联样式表 (CSS) include 时是否有什么技巧,以便对它们进行缓存,而不必在每次发出请求时都进行提取?
答:为了实现这种类型的缓存,您可以使用 IIS 管理单元中的 HTTP 标头属性表,通过选中“启用内容过期”复选框来启用内容过期。这样会使用 Expires 标头为该 HTTP 响应添加标记(请参阅启用内容过期)。如果您在使用 ASP、ASP.NET、PHP 等,则还可以使用 Cache-Control 标头来获得相同的结果。
Microsoft® Internet Explorer 支持可用于相似用途的 META 标记,但是 HTTP 响应标头仍然是首选的方式。使用此方式,无需更新任何 HTML 内容,该设置会基于 IIS 中的站点、虚拟根或文件而进行全局应用。
有关在对象缓存方面运行性能极好的站点示例,请使用一个 HTTP 调试代理,如 Fiddler(它会记录客户端和 Web 服务器之间的所有 HTTP 通信),然后访问 office.microsoft.com。当您查看 Fiddler 中的跟踪时,EXPIRES 列中的大多数对象就都会设置一个 Expiration,并且不会再次请求。图 1 显示了 Fiddler 左侧的列表视图。您会注意到,Office Online 会在事务上发送一个 HTTP/1.0 EXPIRES 日期(例如 Mon, 01 Nov 2004)或一个 HTTP/1.1 Cache-Control(例如 max-age=86400)标头。
问:对于视图状态数据,是否存在一个推荐的大小限制,可帮助避免明显的性能下降?
答:当您下载大量视图状态数据时,如果考虑用户的体验,则会认识到用户的痛苦阈值是真正的限制因素,而在服务器出现问题之前就已经超过了此阈值。请参阅 Chapter 6 — Improving ASP.NET Performance 上标题为“View State”的章节。
如果不考虑服务器规格、网格体系结构和客户端的话,就很难说视图状态有多大。Don't let the BinaryFormatter get at it! 上的 blog 张贴说明了如何将 LosFormatter 和 BinaryFormatter 用于视图状态,以及经过序列化的类型如何影响视图状态大小。
Whidbey will brings us a shorter viewstate, guaranteed! 上的跟贴说明了在 .NET Framework 2.0 中它发生了怎样的变化。
有关经过批准的新“视图状态”的信息,请参阅 2004 年 10 月 MSDN®Magazine 中的一篇文章,“Speed Up Your Site with the Improved View State in ASP.NET 2.0”,网址是 Speed Up Your Site with the Improved View State in ASP.NET 2.0。
问:最近我遇到了一个问题,一个层上的所有计算机(大约 18 个)差不多同时回收了 W3wp(辅助进程)。我想知道在所有这些不同的计算机上计时器是如何保持同步的呢。我正在使用应用程序中心 (Application Center),并且最近执行了推入内容的操作。
答:计时器是在应用程序池启动时开始的。最常见的做法是在对应用程序进行重大更改之后(推入内容)回收应用程序池,这在群集中可能已经进行了自动化处理,因此所有池几乎都在同时启动。
问:在 ADO.NET 中,一个进程可以创建多少连接池?我知道,如果我有 n 个连接字符串的话,将有 n 个池。但是这个 n 是否具有最大值呢?另外,MSDN 文章中指出,一旦创建了连接池,那么这些连接池在活动进程结束之前就不会被破坏。非活动池或空池的维护涉及的系统开销很小。非活动池是否会被剪除?
答:如果您问可以创建多少池的话,答案是每个应用程序域使用的每个连接字符串一个池。对于集成的安全连接,会根据不同用户的数量而创建子池。如果您在某个特定的应用程序域中使用 n 个连接字符串,则将拥有 n 个池。连接池是以每个应用程序域为基础创建的,而不是根据每个进程创建的。池是在有用户连接请求的需要时创建的。还有一个逻辑用于剪除池,甚至在用户没有指定其他设置的情况下,可在一段非活动时间之后删除这些池。
可以创建的池数量没有上限。您提到的 MSDN 注释对于 Microsoft .NET Framework 1.0 和 1.1 是正确的。如果您没有指定最小池大小的话,这些池在非活动时间之后将一直保留,即使它们是空的也是如此。在 .NET Framework 2.0 中,一段非活动时间之后,Framework 将删除该池。
问:我有一个关于 HTML 页截止日期的问题(Windows? XP 和 Microsoft Internet Explorer 6.x)。在 Expire1.htm 中,我指定了页立即过期:
<html>
<head>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="expires" CONTENT="-1">
</head><body>
<a href="HtmlPage1.htm">HtmlPage1.htm</a>
</body>
</html>
使用 Internet Explorer 时,如果我浏览到 Expire1.htm(使用 HTTP,不是 HTTPS),则当我单击链接之后,会被带入 HtmlPage1.htm。如果我更改 Expire1.htm 的内容,然后单击“Back”按钮,得到的是 Expire1.htm 的旧内容,而不是新内容。在 Internet Explorer 中,我选择了在每次访问该页时都“检查”存储页的更新版本的选项。
答:此行为是设计使然。通过使用 META 标头,您可以依赖于 MSHTML 来判断是否删除缓存中的内容,缓存中内容的删除只由 MSHTML 在这些内容通过安全套接字层 (SSL) 下载时执行。因为您使用“Forward”和“Back”按钮,所以不是真正的导航;您只是在显示前面下载的内容。如果连接不是一个 SSL 连接,MSHTML 则只是为存储在缓存中的内容设置 Expiration 标头。
这些操作取决于 WININET 在下载时是否缓存了文件,您通过在整个网站上设置标头已经发现了这个情况。如果您控制 IIS 服务器或宿主这些 HTML 文件的 Web 服务器,则可以基于站点、目录或文件设置 Expires 标头,因此可以一直向下到文件级别,获得合适的缓存行为。
从 Expire1.htm 导航到 HtmlPage1.htm 之后,但在单击“Forward”和“Back”按钮之前,您需要查看“Tools | Internet options | Settings button | View Files”按钮。在此显示中,找到 Expires1.htm,然后查看“截止期”列。是否存在一个日期/时间反映您何时访问了该文件?如果是,图 2 中显示的规则会在使用“Forward”和“Back”按钮时应用到这些经过缓存的内容。您可能会看到,使用“Forward”和“Back”按钮时,只有当前 Internet Explorer 会话期间尚未下载内容的情况下才会发生同步。
现在为了获得您希望的行为,在加载了 HtmlPage1.htm 之后,请不要使用“Forward”和“Back”按钮来访问 Expire1.htm,而是单击地址栏,然后手动输入 Expire1.htm 的完整路径。这样应该会导致显示新内容,因为现在缓存规则是基于超链接单击或地址栏导航的。这种情况下,内容的“截止期”将决定是否应该再次下载该内容。
问:如果某个应用程序同时建立了 50 个与 SQL Server™ 的连接,则连接池中将具有 50 个连接。何时从池中将它们删除?如果它们的 ConnectionLifetime 为 0,文章中说明此设置表示使用最长的连接生命周期,那么这个生命周期是多长?
答:删除池中连接背后的逻辑没有记录,但是这并没有阻止人们发现该逻辑的尝试,以及有关此类文章的编写。最好的一篇文章可能是 SqlClient Connection Pooling Exposed - Reflection allows .NET developers to peer into the internals of SqlClient。您还可以查看下列 blog:ADO.NET The misunderstood "Connection Lifetime" managed pooler connection string keyword.
基本的理念是,在池中保持闲置状态的连接将在大约 4 分钟和 8 分钟之间一个任意的时间内进行清除,具体取决于实施细节。正如前面提到的那篇文章所讲述的那样,如果不使用反射,您根本不可能更改该默认值。
设置了最小连接池大小的连接的真正行为与没有此设置的连接相同。关闭该连接时,会检查该连接生命周期,并且该连接将被处置。托管的池将注意到您已经小于最小池大小,因此会在后台线程上打开一个新连接。连接生命周期与连接池闲置时间无关。将其设置为 0 相当于设置为 int32.MaxValue 的最大值,2147483647 秒。
问:ADO.NET 是否会执行任何操作来保护传递到 SQLConnection 对象的连接字符串参数的安全?我担心的是公开连接字符串的内存转储问题。对连接字符串执行比较,以确定是否可以使用池中的连接时,是否会传递真正的字符串?
答:首先,应该注意,有关公开连接字符串的担心并非 ADO.NET 所特有的;这对于所有数据库访问技术来说都是一个合理的担心。实际上没有什么方法可用来保护密码不会被对 ADO.NET 应用程序具有管理员权限的用户看到。他可以从 .exe 文件、内存转储、通过从网络进行数据包探测,通过欺骗服务器信息等方式来获取密码。
一个很好的解决方案是使用集成安全。这就意味着,您的程序可以执行经过身份验证的用户在没有您程序的情况下有权限执行的任何操作。如果使用集成安全,在此过程中将不会显示任何密码。
作为在连接字符串中使用密码的众多危险的一个示例,请采用此方案。首先,请使用包含密码的连接字符串调用 SqlConnection.Open。第二,SqlConnection 尽快对该密码进行内部加密。但是,即使使用此设计,在垃圾收集发生之前的进程内存转储仍会向您显示调用 SqlConnection.Open 时所使用的字符串对象(连接字符串)。因此,无论 SqlConnection 执行什么操作,它都无法隐藏此连接字符串的内容。SqlConnection 甚至无法擦除传入连接字符串中的数据,因为字符串在 .NET 中是不可变的。有关在基于 .NET 的应用程序中保护字符串安全的详细信息,请参阅本期 MSDN Magazine 中的 Security Briefs,尤其是有关 System.Security 和新 SecureString 类的部分。
第二个选择是完全向用户隐藏数据库(如使用 Web 服务解决方案),以便该数据库只能由 Web 服务器访问。
问:我想知道如何使得页上的菜单项不会出现在正在显示的图后面?该图是一个包含在 <DIV> 标记中的 flash 对象。是否应该使用 Z-索引?当我在主菜单上移动鼠标时,应该显示菜单项,但是其中某些菜单项总是会隐藏在该图之后。
答:在 <DIV> 元素上设置 Z-索引属性不会有所帮助,这是因为 Flash 控件是一个有窗口的元素,不允许其他 DHTML 内容置于它上面的层中。您可能能够将您的菜单更改为使用 createPopup,这样就会位于另一个有窗口对象的上面了。但是,请注意,辅助功能应该仍然能够使用这些菜单项。有关如何完成上述任务的详细信息,请参阅 createPopup Method。
答:为了实现这种类型的缓存,您可以使用 IIS 管理单元中的 HTTP 标头属性表,通过选中“启用内容过期”复选框来启用内容过期。这样会使用 Expires 标头为该 HTTP 响应添加标记(请参阅启用内容过期)。如果您在使用 ASP、ASP.NET、PHP 等,则还可以使用 Cache-Control 标头来获得相同的结果。
Microsoft® Internet Explorer 支持可用于相似用途的 META 标记,但是 HTTP 响应标头仍然是首选的方式。使用此方式,无需更新任何 HTML 内容,该设置会基于 IIS 中的站点、虚拟根或文件而进行全局应用。
有关在对象缓存方面运行性能极好的站点示例,请使用一个 HTTP 调试代理,如 Fiddler(它会记录客户端和 Web 服务器之间的所有 HTTP 通信),然后访问 office.microsoft.com。当您查看 Fiddler 中的跟踪时,EXPIRES 列中的大多数对象就都会设置一个 Expiration,并且不会再次请求。图 1 显示了 Fiddler 左侧的列表视图。您会注意到,Office Online 会在事务上发送一个 HTTP/1.0 EXPIRES 日期(例如 Mon, 01 Nov 2004)或一个 HTTP/1.1 Cache-Control(例如 max-age=86400)标头。
问:对于视图状态数据,是否存在一个推荐的大小限制,可帮助避免明显的性能下降?
答:当您下载大量视图状态数据时,如果考虑用户的体验,则会认识到用户的痛苦阈值是真正的限制因素,而在服务器出现问题之前就已经超过了此阈值。请参阅 Chapter 6 — Improving ASP.NET Performance 上标题为“View State”的章节。
如果不考虑服务器规格、网格体系结构和客户端的话,就很难说视图状态有多大。Don't let the BinaryFormatter get at it! 上的 blog 张贴说明了如何将 LosFormatter 和 BinaryFormatter 用于视图状态,以及经过序列化的类型如何影响视图状态大小。
Whidbey will brings us a shorter viewstate, guaranteed! 上的跟贴说明了在 .NET Framework 2.0 中它发生了怎样的变化。
有关经过批准的新“视图状态”的信息,请参阅 2004 年 10 月 MSDN®Magazine 中的一篇文章,“Speed Up Your Site with the Improved View State in ASP.NET 2.0”,网址是 Speed Up Your Site with the Improved View State in ASP.NET 2.0。
问:最近我遇到了一个问题,一个层上的所有计算机(大约 18 个)差不多同时回收了 W3wp(辅助进程)。我想知道在所有这些不同的计算机上计时器是如何保持同步的呢。我正在使用应用程序中心 (Application Center),并且最近执行了推入内容的操作。
答:计时器是在应用程序池启动时开始的。最常见的做法是在对应用程序进行重大更改之后(推入内容)回收应用程序池,这在群集中可能已经进行了自动化处理,因此所有池几乎都在同时启动。
问:在 ADO.NET 中,一个进程可以创建多少连接池?我知道,如果我有 n 个连接字符串的话,将有 n 个池。但是这个 n 是否具有最大值呢?另外,MSDN 文章中指出,一旦创建了连接池,那么这些连接池在活动进程结束之前就不会被破坏。非活动池或空池的维护涉及的系统开销很小。非活动池是否会被剪除?
答:如果您问可以创建多少池的话,答案是每个应用程序域使用的每个连接字符串一个池。对于集成的安全连接,会根据不同用户的数量而创建子池。如果您在某个特定的应用程序域中使用 n 个连接字符串,则将拥有 n 个池。连接池是以每个应用程序域为基础创建的,而不是根据每个进程创建的。池是在有用户连接请求的需要时创建的。还有一个逻辑用于剪除池,甚至在用户没有指定其他设置的情况下,可在一段非活动时间之后删除这些池。
可以创建的池数量没有上限。您提到的 MSDN 注释对于 Microsoft .NET Framework 1.0 和 1.1 是正确的。如果您没有指定最小池大小的话,这些池在非活动时间之后将一直保留,即使它们是空的也是如此。在 .NET Framework 2.0 中,一段非活动时间之后,Framework 将删除该池。
问:我有一个关于 HTML 页截止日期的问题(Windows? XP 和 Microsoft Internet Explorer 6.x)。在 Expire1.htm 中,我指定了页立即过期:
<html>
<head>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="expires" CONTENT="-1">
</head><body>
<a href="HtmlPage1.htm">HtmlPage1.htm</a>
</body>
</html>
使用 Internet Explorer 时,如果我浏览到 Expire1.htm(使用 HTTP,不是 HTTPS),则当我单击链接之后,会被带入 HtmlPage1.htm。如果我更改 Expire1.htm 的内容,然后单击“Back”按钮,得到的是 Expire1.htm 的旧内容,而不是新内容。在 Internet Explorer 中,我选择了在每次访问该页时都“检查”存储页的更新版本的选项。
答:此行为是设计使然。通过使用 META 标头,您可以依赖于 MSHTML 来判断是否删除缓存中的内容,缓存中内容的删除只由 MSHTML 在这些内容通过安全套接字层 (SSL) 下载时执行。因为您使用“Forward”和“Back”按钮,所以不是真正的导航;您只是在显示前面下载的内容。如果连接不是一个 SSL 连接,MSHTML 则只是为存储在缓存中的内容设置 Expiration 标头。
这些操作取决于 WININET 在下载时是否缓存了文件,您通过在整个网站上设置标头已经发现了这个情况。如果您控制 IIS 服务器或宿主这些 HTML 文件的 Web 服务器,则可以基于站点、目录或文件设置 Expires 标头,因此可以一直向下到文件级别,获得合适的缓存行为。
从 Expire1.htm 导航到 HtmlPage1.htm 之后,但在单击“Forward”和“Back”按钮之前,您需要查看“Tools | Internet options | Settings button | View Files”按钮。在此显示中,找到 Expires1.htm,然后查看“截止期”列。是否存在一个日期/时间反映您何时访问了该文件?如果是,图 2 中显示的规则会在使用“Forward”和“Back”按钮时应用到这些经过缓存的内容。您可能会看到,使用“Forward”和“Back”按钮时,只有当前 Internet Explorer 会话期间尚未下载内容的情况下才会发生同步。
现在为了获得您希望的行为,在加载了 HtmlPage1.htm 之后,请不要使用“Forward”和“Back”按钮来访问 Expire1.htm,而是单击地址栏,然后手动输入 Expire1.htm 的完整路径。这样应该会导致显示新内容,因为现在缓存规则是基于超链接单击或地址栏导航的。这种情况下,内容的“截止期”将决定是否应该再次下载该内容。
问:如果某个应用程序同时建立了 50 个与 SQL Server™ 的连接,则连接池中将具有 50 个连接。何时从池中将它们删除?如果它们的 ConnectionLifetime 为 0,文章中说明此设置表示使用最长的连接生命周期,那么这个生命周期是多长?
答:删除池中连接背后的逻辑没有记录,但是这并没有阻止人们发现该逻辑的尝试,以及有关此类文章的编写。最好的一篇文章可能是 SqlClient Connection Pooling Exposed - Reflection allows .NET developers to peer into the internals of SqlClient。您还可以查看下列 blog:ADO.NET The misunderstood "Connection Lifetime" managed pooler connection string keyword.
基本的理念是,在池中保持闲置状态的连接将在大约 4 分钟和 8 分钟之间一个任意的时间内进行清除,具体取决于实施细节。正如前面提到的那篇文章所讲述的那样,如果不使用反射,您根本不可能更改该默认值。
设置了最小连接池大小的连接的真正行为与没有此设置的连接相同。关闭该连接时,会检查该连接生命周期,并且该连接将被处置。托管的池将注意到您已经小于最小池大小,因此会在后台线程上打开一个新连接。连接生命周期与连接池闲置时间无关。将其设置为 0 相当于设置为 int32.MaxValue 的最大值,2147483647 秒。
问:ADO.NET 是否会执行任何操作来保护传递到 SQLConnection 对象的连接字符串参数的安全?我担心的是公开连接字符串的内存转储问题。对连接字符串执行比较,以确定是否可以使用池中的连接时,是否会传递真正的字符串?
答:首先,应该注意,有关公开连接字符串的担心并非 ADO.NET 所特有的;这对于所有数据库访问技术来说都是一个合理的担心。实际上没有什么方法可用来保护密码不会被对 ADO.NET 应用程序具有管理员权限的用户看到。他可以从 .exe 文件、内存转储、通过从网络进行数据包探测,通过欺骗服务器信息等方式来获取密码。
一个很好的解决方案是使用集成安全。这就意味着,您的程序可以执行经过身份验证的用户在没有您程序的情况下有权限执行的任何操作。如果使用集成安全,在此过程中将不会显示任何密码。
作为在连接字符串中使用密码的众多危险的一个示例,请采用此方案。首先,请使用包含密码的连接字符串调用 SqlConnection.Open。第二,SqlConnection 尽快对该密码进行内部加密。但是,即使使用此设计,在垃圾收集发生之前的进程内存转储仍会向您显示调用 SqlConnection.Open 时所使用的字符串对象(连接字符串)。因此,无论 SqlConnection 执行什么操作,它都无法隐藏此连接字符串的内容。SqlConnection 甚至无法擦除传入连接字符串中的数据,因为字符串在 .NET 中是不可变的。有关在基于 .NET 的应用程序中保护字符串安全的详细信息,请参阅本期 MSDN Magazine 中的 Security Briefs,尤其是有关 System.Security 和新 SecureString 类的部分。
第二个选择是完全向用户隐藏数据库(如使用 Web 服务解决方案),以便该数据库只能由 Web 服务器访问。
问:我想知道如何使得页上的菜单项不会出现在正在显示的图后面?该图是一个包含在 <DIV> 标记中的 flash 对象。是否应该使用 Z-索引?当我在主菜单上移动鼠标时,应该显示菜单项,但是其中某些菜单项总是会隐藏在该图之后。
答:在 <DIV> 元素上设置 Z-索引属性不会有所帮助,这是因为 Flash 控件是一个有窗口的元素,不允许其他 DHTML 内容置于它上面的层中。您可能能够将您的菜单更改为使用 createPopup,这样就会位于另一个有窗口对象的上面了。但是,请注意,辅助功能应该仍然能够使用这些菜单项。有关如何完成上述任务的详细信息,请参阅 createPopup Method。