改善 ASP 性能和外观的技巧(二)

发布时间:2013-09-19 11:33:46来源:阅读(1500)

    技巧 12:将经常使用的数据复制到脚本变量中 
    当访问 ASP 中的 COM 对象时,应将经常使用的对象数据复制到脚本变量中。这样做可减少 COM 方法调用,因为 COM 方法调用与访问脚本变量相比,开销相对较大。当访问 Collection 和 Dictionary 对象时,这种技术也会减少开销很大的查找。 
    一般来说,如果您打算不止一次访问对象数据,那么就应将数据放到脚本变量中。这种优化的主要目标是 Request 变量(Form 和 QueryString 变量)。例如,您的站点可传递一个名为 UserID 的 QueryString 变量。假设此 UserID 在特定页面上被引用 12 次。可以无须调用 Request(?UserID?) 12 次,而是在 ASP 页面最上面将 UserID 指派到一个变量。然后在该页面自始至终使用该变量。这样就省去了 11 次 COM 方法调用。 
    实际上,访问 COM 属性或方法的开销并没有那么大。下面举一个例子,说明某相当常见的代码(从语法上讲): 
    Foo.bar.blah.baz = Foo.bar.blah.qaz(1)
    If Foo.bar.blah.zaq = Foo.bar.blah.abc Then ’ ...

    当此代码运行时,下面是发生的情况: 
    变量 Foo 被解析为全局对象。 
    变量 bar 被解析为 Foo 的成员。这实际就是一次 COM 方法调用。 
    变量 blah 被解析为 Foo.bar 的成员。这又是一次 COM 方法调用。 
    变量 qaz 被解析为 foo.bar.blah 的成员。没有错,这还是一次 COM 方法调用。 
    调用 Foo.bar.blah.quaz(1)。再一次 COM 方法调用。懂了吗? 
    再次执行步骤 1 至步骤 3 以解析 baz。系统并不知道调用 qaz 是否改变对象模型,因此必须再次执行步骤 1 至 3 以解析 baz。 
    将 baz 解析为 Foo.bar.blah 的成员。赋予属性。 
    再次执行步骤 1 至步骤 3 以解析 zaq。 
    再次执行步骤 1 至步骤 3 以解析 abc。 
    正如您可看到的,效率相当差(且慢)。以 VBScript 写此代码的快速方法是: 
    Set myobj = Foo.bar.blah ’ do the resolution of blah ONCE
    Myobj.baz = myobj.qaz(1)
    If Myobj.zaq = Myobj.abc Then ’...

    如果您使用 VBScript 5.0 或更高版本,您可以使用 With 语句写此代码: 
    With Foo.bar.blah
    .baz = .qaz(1)
    If .zaq = .abc Then ’...
    ...
    End With

    注意此技巧也适用于 VB 程序设计。 
    技巧 13:避免重新确定数组的维数 
    应尽量避免 Redim 数组。就性能而言,如果计算机的物理内存大小有限,最好将数组的初始维数设置为其最不利的情况 - 或将维数设置为其最佳的情况,然后再按需要重新确定维数。这并非意味着,如果知道您不需要内存时,就随便分配几兆字节的内存。 
    下面的代码给您显示使用 Dim 和 Redim 不当的情形。 
    <% 
    Dim MyArray()
    Redim MyArray(2)
    MyArray(0) = ?hello?
    MyArray(1) = ?good-bye?
    MyArray(2) = ?farewell?
    ...
    ’ some other code where you end up needing more space happens, then ...
    Redim Preserve MyArray(5)
    MyArray(3) = ?more stuff?
    MyArray(4) = ?even more stuff?
    MyArray(5) = ?yet more stuff?
    %>

    最好一开始就将数组的初始大小 Dim 正确(在本例中,是 5)比 Redim 数组使其更大好得多。您可能浪费一些内存(如果您没有使用所有的元素),但获得的好处是速度变得更快。 
    技巧 14:使用响应缓冲 
    您可以通过启用“响应缓冲”,将要输出的一整页缓冲起来。这样就将写到浏览器的量减到最少,从而改善总体性能。每个写操作都会产生很大的系统开销(在 IIS 中以及在通过网络发送的数据量方面),因此写操作越少越好。由于其启动慢且使用 Nagling 算法(用来减轻网络塞车情况),TCP/IP 在发送一些大的数据块时比必须发送许多小的数据块时的效率高得多。 
    有两个方法启用响应缓冲。第一种,您可以使用 Internet Services Manager 为整个应用程序启用响应缓冲。我们建议采用这种方法,在 IIS 4.0 和 IIS 5.0 中默认为新的 ASP 应用程序启用响应缓冲。第二种,可以在每个 ASP 页面的接近顶端的地方加入下面的代码行,从而启用响应缓冲: 
    <% Response.Buffer = True %>

    此代码行必须在任何响应数据被写到浏览器之前执行(即,在任何 HTML 出现在 ASP 脚本之前以及在使用 Response.Cookies 集合设置任何 Cookies 之前)。一般来说,最好为整个应用程序启用响应缓冲。这样,您就不必在每个页面最上面写入上述的代码行。 
    Response.Flush
    关于响应缓冲有一个常见的抱怨,就是用户感觉到 ASP 页面的响应速度很慢(即使整个响应时间得到改进),因为他们必须等到整个页面生成,然后他们才能看到东西。对于运行时间长的页面,您可以设置 Response.Buffer = False,禁用响应缓冲。但是,一个更好的策略是利用 Response.Flush 方法。这种方法将 ASP 转换的所有 HTML 送到浏览器。例如,在转换 1,000 行的表的前 100 行之后,ASP 可以调用 Response.Flush,强制将转换的结果送到浏览器,这样可使用户在其余的行准备好之前看到头 100 行。这种技术可以将响应缓冲与浏览器逐渐显示数据完美地结合在一起。 
    (注意在上面的 1,000 行表的举例中,许多浏览器在它们看到关闭  标记之前不会开始显示表。检查您的目标浏览器是否支持。为避免这种情况,将表分成多个具有较少行的表,并在每个表之后调用 Response.Flush。较新版本的 Internet Explorer 在表完全下载之前就开始显示表,如果您指定表列宽,显示速度就会特别快,这样做可避免强制 Internet Explorer 通过测量每个单元格的内容宽度来计算列宽。) 
    另一个关于响应缓冲的常见的抱怨是,当产生非常大的页面时,将占用许多服务器内存。撇开产生大页面的方法不谈,这种问题也可通过巧妙使用 Response.Flush 来加以解决。 
    技巧 15:批处理内嵌脚本和 Response.Write 语句 
    VBScript 语法 <% = expression %> 将“expression”的值写到 ASP 输出流中。如果响应缓冲未启用,那么执行其中的每一条语句,都会以许多小的数据包通过网络将数据写到浏览器中。这样速度很慢。而且穿插执行少量的脚本和 HTML,将引起脚本引擎和 HTML 之间的切换,从而降低性能。因此,使用下面的技巧:使用 Response.Write 调用代替捆绑紧密的内嵌表达式。例如,在下面的示例中,在每一行的每一字段对响应流有一次写操作,每一行在 VBScript 和 HTML 之间有许多切换: 

    <% For Each fld in rs.Fields %>

    <%
    Next 
    While Not rs.EOF
    %>

    <% For Each fld in rs.Fields %>

    <% Next 

    <% rs.MoveNext 
    Wend %>
    <% = fld.Name %>
    <% = fld.Value %>


    下面的代码更有效,每一行对响应流有一次写操作。所有的代码都包含在一个 VBScript 块内: 

    <%
    For each fld in rs.Fields
    Response.Write (?? & vbCrLf)
    Next
    While Not rs.EOF
    Response.Write (??)
    For Each fld in rs.Fields %>
    Response.Write(?? & vbCrLf)
    Next
    Response.Write ??
    Wend
    %>
    ? & fld.Name & ?
    ? & fld.Value & ?


    当禁用响应缓冲时,这一技巧的效果特别大。最好启用响应缓冲,然后看批处理 Response.Write 是否有助于提高性能。 
    (在这一特定举例中,建立表主体的嵌套循环 (While Not rs.EOF...) 可以用仔细构建的 GetString 调用来替代。) 
    技巧 16:如果页面需要很长时间才能完成,那么执行前使用 Response.IsClientConnected 
    如果用户性急,他们可能会在您开始执行他们的请求之前,就会放弃 ASP 页面。如果他们单击刷新或移到服务器上的另一个页面,在 ASP 请求队列的末尾就有一个新的请求等候,在队列的中间有一个断开连接的请求。当服务器的负载很高时(因此请求队列就会很长,响应时间也会相应地变长),就会经常发生这种情况,这样只能使情况变得更糟。如果用户不再连接,执行 ASP 页面(特别是慢的、大的 ASP 页面)已没有任何意义。您可以使用 Response.IsClientConnected 属性检查这一情况。如果它返回 False,则应调用 Response.End 并放弃页的其余部分。事实上,IIS 5.0 已将这一做法编为程序 - 每当 ASP 即将执行新请求时,它就会检查请求在队列中已等候了多长时间。如果已经在那里等候了多于 3 秒钟,ASP 将检查客户机是否仍处于连接状态,如果没有连接,就立即终止请求。您可以在配置数据库中使用 AspQueueConnectionTestTime 设置将超时时间由 3 秒调整为其它值。 
    如果页面要花很长时间才能执行完,也可以不时地检查 Response.IsClientConnected。当启用了响应缓冲时,最好不时地执行 Response.Flush,以用户知道,正在发生什么事。 
    注意 在 IIS 4.0 上,除非先执行了 Response.Write,否则 Response.IsClientConnected 就不能正常工作。如果启用了缓冲,您也必须执行 Response.Flush。在 IIS 5.0 上,却没有必要这样做,- Response.IsClientConnected 工作正常。在任何情况下,Response.IsClientConnected 都会有一些开销,因此只有在一个操作至少要花(比方说) 500 毫秒(如果您想维持每秒钟数十页的吞吐量,这是一个很长的时间)才使用它。经验表明,不要每次重复执行紧密循环时都调用它,如显示表的许多行时 - 每隔二十或五十行调用一次可能比较合适。 
    技巧 17:使用  标记例示对象 
    如果要引用不在所有代码路径(特别是服务器或应用程序作用域的对象)中使用的对象,使用 Global.asa 中  标记声明它们,而不使用 Server.CreateObject 方法。Server.CreateObject 能立即创建对象。如果以后不再使用该对象,您就浪费了资源。 标记声明 objname,但在其方法或属性第一次使用以前,不会创建 objname。 
    这又是一个惰性计算的例子。 
    技巧 18:对于 ADO 和其它组件使用 TypeLib 声明 
    当使用 ADO 时,开发人员经常加入 adovbs.txt,以访问 ADO 的各种常量。在要使用常量的每个页面中必须包含此文件。此常量文件相当大,给每个 ASP 页面的编译时间和脚本大小增加了许多系统开销。 
    IIS 5.0 引入了绑定到组件类型库的功能。这可使您引用类型库一次,并将其用在每个 ASP 页面上。每个页面不会产生编译常量文件的开销,且组件开发人员不必建立 VBScript#_include 文件以在 ASP 上使用。 
    要访问 ADO TypeLib,将下面一条语句放在 Global.asa 中。 


    或 


    技巧 19: 利用浏览器的验证功能 
    现今的浏览器对一些高级功能如 XML、DHTML、Java 小程序和远程数据服务提供支持。尽可能使用这些功能。所有这些技术都可以执行客户机端验证和数据缓存,免去了到 Web 服务器的往返。如果您在运行一个智能浏览器,那么浏览器就能为您进行一些验证(例如,在执行 POST 之前,检查信用卡校验和是否有效)。尽可能使用这一功能。通过减少客户-服务器之间的往返,可降低 Web 服务器上的负载,并能减少网络通信量(虽然发送到浏览器的第一个页面可能比较大)以及服务器访问的任何后端资源。此外,用户不必像住常一样读取新页,从而用户的感觉会好一些。这样做并不意味着您可以不进行服务器端验证 - 您还应始终进行服务器端验证。这可以防止由于某种原因(如黑客,或浏览器不运行客户机端验证例程)客户机产生错误的数据。 
    人们已经进行了大量的工作,开发“独立于浏览器”的 HTML。正是由于这种忧虑,开发人员不愿再使用流行的浏览器功能,但这些功能本可以改善性能。对于一些真正的高性能站点,必须关心浏览器“访问”问题,一个好的策略是优化页面,使其适应流行的浏览器。使用浏览器功能组件,可以在 ASP 中方便地检测到浏览器功能。Microsoft FrontPage 等工具有助于设计适合于浏览器和指定 HTML 版本的代码。参见 When is Better Worse?Weighing the Technology Trade-Offs,以了解更进一步的讨论。 
    技巧 20:避免在循环语句中使用字符串串联 
    许多人在循环语句中建立一个字符串,如下所示: 
    s = ?? & vbCrLf
    For Each fld in rs.Fields
    s = s & ?  ?
    Next
    While Not rs.EOF
    s = s & vbCrLf & ? ?
    For Each fld in rs.Fields
    s = s & ?  ?
    Next
    s = s & ? ?
    rs.MoveNext
    Wend
    s = s & vbCrLf & ?
    ? & fld.Name & ?
    ? & fld.Value & ?
    ? & vbCrLf
    Response.Write s

    采用这种方法会出现一些问题。第一个问题是反复串联字符串需要花两次方的时间,更通俗地说,运行这种循环语句所花的时间与记录数乘以字段数所得值的平方成正比。举一个更简单的例子,就可以更清楚地说明这一问题。 
    s = ??
    For i = Asc(?A?) to Asc(?Z?)
    s = s & Chr(i)
    Next

    在第一次迭代中,您获得了一个字符的字符串 ?A?。在第二次迭代中,VBScript 必须重新分配字符串并将两个字符 (?AB?) 复制到 s 中。在第三次迭代中,它还必须再次重新分配 s 并将三个字符复制到 s 中。在 N 次(第 26 次)迭代中,它必须重新分配并将 N 个字符复制到 s 中。总共就是 1+2+3+...+N,即 N*(N+1)/2 次复制。 
    在上面的记录集举例中,如果有 100 个记录和 5 个字段,内循环将执行 100*5 = 500 次,所有的复制和重新分配所花的时间与 500*500 = 250,000 成正比。这对于中等大小的记录集来说复制操作太多了。 
    在本例中,代码可以用 Response.Write() 或内嵌脚本 (<% = fld.Value %>) 替代字符串串联来改进。如果启用了响应缓冲的话(应该的),这样做就会更快,因为 Response.Write 只将数据附加到响应缓冲的末尾。并不涉及重新分配,因此效率很高。 
    在将 ADO 记录集转换为 HTML 表的特定情况下,应考虑使用 GetRows 或 GetString。 
    如果在 JScript 中串联字符串,特别建议使用 += 运算符,即,使用 s += ?某字符串?,而不使用 s = s + ?某字符串?。 
    技巧 21:启用浏览器和代理缓存 
    在默认情况下,ASP 禁止在浏览器和代理中进行缓存。这是有意义的,因为就实质而言 ASP 页面是动态的,上面有随时间不断变化的潜在信息。如果页面不要求在每个视图上进行刷新,您应启用浏览器和代理缓存。这可使浏览器和代理在一定的时间内使用页面的“缓存”副本,您可以控制时间的长短。缓存可以大大减轻服务器上的负载,缩短用户的等待时间。 
    哪一种动态页面可作为要缓存的页面呢?下面举一些例子: 
    天气预报页面,在此页面上,每隔 5 分钟更新一次天气预报。 
    列出新闻条目或新闻稿的主页,它一天更新两次。 
    共同基金业绩列表,在此列表中,基本统计信息每隔几小时更新一次。 
    注意,在使用浏览器或代理缓存的情况下,Web 服务器上记录的访问次数减少了。如果您想准确地测量所有页面视图或张帖公布,您就不希望使用浏览器和代理缓存。 
    浏览器缓存由 HTTP“过期”报头控制,该报头由 Web 服务器发送给浏览器。ASP 提供两个简单的机制发送此报头。要设置页面使其过多少分钟后到期,则应设置 Response.Expires 属性。下面的例子告诉浏览器内容在 10 分钟内过期: 
    <% Response.Expires = 10 %> 

    若将 Response.Expires 设置为负数或 0,则禁用缓存。一定要使用大的负数,如 -1000(略多于一天),以避免服务器和浏览器时钟之间的不匹配。第二个属性 Response.ExpiresAbsolute 将使您设置内容过期的具体时间: 
    <% Response.ExpiresAbsolute = #May 31,2001 13:30:15# %>

    您可以不使用 Response 对象设置过期时间,而将  标记写进 HTML,通常写在 HTML 文件的  部分。一些浏览器将遵照此指令,而代理则不然。 


    最后,您可以使用 Response.CacheControl 属性,指示其内容是否可以让 HTTP 代理缓存。若将此属性设置为“Public”,代理就可以缓存此内容。 
    <% Response.CacheControl = ?Public? %>

    在默认情况下,此属性被设置为“Private”。注意,对于显示某用户特定数据的页面,不应启用代理缓存,因为代理可能给用户提供属于其他用户的页面。 
    技巧 22:尽可能使用 Server.Transfer 代替 Response.Redirect 
    Response.Redirect 让浏览器请求另一个页面。此函数常用来将用户重定向到一个登录或错误页面。因为重定向强制请求新页面,结果是浏览器必须到 Web 服务器往返两次,且 Web 服务器必须多处理一个请求。IIS 5.0 引入了一个新的函数 Server.Transfer,它将执行转移到同一台服务器上的另一个 ASP 页。这样就避免多余的浏览器-Web-服务器的往返,从而改善了总体系统性能以及缩短了用户的响应时间。检查“重定向”中的“新的方向”,上面应该是 Server.Transfer 和 Server.Execute。 
    另请参见 Leveraging ASP in IIS 5.0,了解 IIS 5.0 和 ASP 3.0 新功能的完整列表。 
    技巧 23:在目录 URL 中使用后斜杠 
    一个相关的技巧是确保在指向目录的 URL 中使用后斜杠 (/)。如果您省略了后斜杠,浏览器就会向服务器发出请求,只是为了告诉服务器,它在请求目录。浏览器就会发出第二个请求,将斜杠附加到 URL 后面,只有此后,服务器才能以该目录的默认文档或目录列表(如果没有默认文档且启用了目录浏览的话)响应。附加斜杠可省去第一个、无用的住返。为便于用户阅读,可以省略显示名称中的后斜杠。 
    例如,写: 
    Workshop?>http://msdn.microsoft.com/workshop

    这也适用于指向 Web 站点上主页的 URL:使用下面的:,而不使用 。 
    技巧 24:避免使用服务器变量 
    访问服务器变量会使 Web 站点向服务器发出一个特殊请求,并收集所有服务器变量,而不只是您请求的那个变量。这种情况类似于,在发霉的阁楼上,在一个文件夹中查找某个文件。当您想要找那个文件时,您必须去阁楼上,先找到文件夹,然后才能找到这份文件。当您请求服务器变量时,发生的情况是一样的 - 您第一次请求服务器变量时,就会使性能受到影响。后面的对其它服务器变量的请求,则不会对性能产生影响。 
    决不要访问非限定的 Request 对象(例如,Request("Data"))。对于不在 Request.Cookies、Request.Form、Request.QueryString 或 Request.ClientCertificate 中的项目,则隐式调用 Request.ServerVariables。Request.ServerVariables 集合比其它集合慢得多。 
    技巧 25:升级到最新和最出色的 
    系统组件是恒定的,我们建议您将它们升级到最新和最好的配置。最好升级到 Windows 2000(因此,也应升级到 IIS 5.0、ADO 2.5、MSXML 2.5、Internet Explorer 5.0、VBScript 5.1 和 JScript 5.1)。在多处理器计算机上,实施 IIS 5.0 和 ADO 2.5 可显著改善性能。在 Windows 2000 下,ASP 可以很好地扩展到四个处理器或更多,而在 IIS 4.0 下,ASP 的扩展性不能超出两个处理器。在应用程序中使用的脚本代码和 ADO 越多,升级到 Windows 2000 之后,性能的改善就会越多。 
    如果目前还不能升级到 Windows 2000,您可以升级到 SQL Server、ADO、VBScript 和 JScript、MSXML、Internet Explorer 和 NT 4 Service Packs 的最新版本。它们均可提高性能和可靠性。 
    技巧 26:优化 Web 服务器 
    有多种 IIS 优化参数可以改善站点性能。例如,对于 IIS 4.0,我们常常发现,增加 ASP ProcessorThreadMax 参数(参见 IIS 文档)可以显著改善性能,特别是在倾向于等待后端资源(如数据库)或其它中间产品(如屏幕刷)的站点上。在 IIS 5.0 中,您可能发现启用 ASP Thread Gating 比查找一个 AspProcessorThreadMax 最佳设置效率更高,这一点现在已为大家所熟知。 
    有关较好的参考资料,参见下面的优化 IIS。 
    最佳的配置设置取决于(其中一些因素)应用程序代码、运行所在的系统硬件和客户机工作负荷。找到最佳设置的唯一方法是进行性能测试,这是我们在下一个技巧中所要讨论的。 
    技巧 27:进行性能测试 
    正如我们在前面已经讲过,性能是一个特征。如果您想要改善站点的性能,那么就制定一个性能目标,然后逐步改进,直到达到目标为止。不要,就不进行任何性能测试。通常,在项目结束时,再作必需的结构调整已经为时太晚,您的客户将为此感到失望。将性能测试作为您日常测试的一部分来进行。可以对单个组件分别进行性能测试,如针对 ASP 页或 COM 对象,或将站点作为一个整体来测试。 
    许多人使用单个浏览器请求页面,来测试 Web 站点的性能。这样做就会给您一个感觉,即站点的响应能力很好,但这样做实际上并不能告诉您在负载条件下站点的性能如何。 
    一般情况下,要想准确地测试性能,您需要一个专门的测试环境。此环境应包括硬件,其处理器速度、处理器数量、内存、磁盘、网络配置等方面与生产环境的硬件相似。其次,您必须指定客户机的工作负荷:有多少同时的用户,他们发出请求的频率,他们点击页面的类型等等。如果您没有站点实际使用情况的数据,您必须估计一下使用的情况。最后,您需要一个可以模拟预期客户机工作负荷的工具。有了这些工具,您就可以开始回答诸如“如果我有 N 个同时的用户,那么需要多少服务器?”之类的问题。您还可以找出出现瓶颈的原因,并以此为目标进行优化。 
    下面列出了一些好的 Web 负载测试工具。我们特别推荐 Microsoft Web Application Stress (WAS) 工具包。WAS 可使您记录测试脚本,然后模拟数百或成千上万个用户访问 Web 服务器。WAS 报告很多统计信息,包括每秒钟的请求数,响应时间分布情况和错误计数。WAS 有丰富的客户机界面和基于 Web 的界面两种,Web 界面可使您进行远程测试。 
    一定要阅读 IIS 5.0 Tuning Guide。 
    技巧 28:阅读资源链接 
    下面是一些与性能有关的出色的资源链接。如果您想了解有关信息,请阅读 Developing Scalable Web Applications。 

    关键字