简介
性能是一个很重要的特征。你需要事先设计好性能指标,否则日后就要为此重新编写程序。就是说:要设想好怎样最佳化地执行ASP程序?
本文提出了一些优化ASP应用和VBScript的技巧,许多技巧和缺陷都经过了研讨。这里列出的建议已经在http://www.microsoft.com 和其他站点上进行了测试,都工作得非常好。本文假设你具备ASP开发的基本知识,包括VBScript或者JScript,ASP应用程序,ASP Session,以及其他ASP内置对象(Request,Response和Server)。
通常,ASP的执行性能远远不仅仅依赖ASP代码本身!在本文的尾部列出了与性能相关的资源,它们含概了ASP和非ASP的部分,包含ActiveX Data Objects(ADO),Component Object Model(COM),数据库(Database),以及Internet信息服务器(IIS)的配置。除了这些,还有一些非常好的链接值得你一看。
技巧1:在Web服务器上缓存经常使用的数据
典型的情况是:ASP页面从后台存储中取回数据,然后以超文本标记语言(HTML)的形式形成结果。不管数据库的速度如何,从内存中取回数据要比从后台存储设备中快得多。从本地硬盘读取数据通常也非常快。所以,提高性能可以通过缓存服务器上的数据来实现,无论是将数据缓存在内存中,或者本地硬盘中。
缓存是经典的“空间换时间”的折中方式。如果缓存得恰当,就可以看到显著的性能提升。为了让缓存有效,必须保证缓存数据是经常要重用的,而且也是计算起来繁琐的。装满陈旧数据的缓存是对内存的浪费。
不经常改变的数据是缓存的较好对象,因为不需要随时考虑这些数据更新后的同步操作。组合框、参考表格、DHTML代码、扩展标记语言串、菜单以及站点配置变量(包括数据源名字DSNS,Internet协议地址IP以及Web路径)都是很好的缓存对象。注意:要缓存数据表达式而不是数据本身。如果一个ASP页面经常变化并且很费力去缓存(比如整个产品目录),就要考虑预产生HTML,而不是每次发生请求时再描述它。
技巧2:在Application或Session对象中缓存经常使用的数据
ASP中的Application和Session对象是在内存中缓存数据的便利容器。你可以将数据赋值给Application和Session对象,这些数据在HTTP调用期间将一直保持在内存中。Session中的数据是为每一个用户服务的,Application中的数据是所有用户共享的。
何时需要在Application和Session中装入数据?通常,当应用程序启动或者会话开始时,数据就被装入了。为了在这时装入数据,在Application OnStart()或者Session OnStart()中分别添加适当的代码。这些函数位于文件Global.asa中,如果原来不存在,就添加上。也可以在数据首次需要的时候调入,在ASP页面中添加代码,检查数据是否存在,如果没有发现,就调入它。这里有一个例子,它代表了被称为“lazy evalution”的经典性能处理技术:直到需要时,再去计算。例子如下:
<%
Function GetEmploymentStatusList
Dim d
d = Application("EmploymentStatusList")
If d = "" Then
' FetchEmploymentStatusList function (not shown)
' fetches data from DB, returns an Array
d = FetchEmploymentStatusList()
Application("EmploymentStatusList") = d
End If
GetEmploymentStatusList = d
End Function
%>
对于不同的数据,可以编写类似的函数代码。
数据应该按什么格式保存?任何变量类型都可以,因为所有的脚本变量都是不同的。比如说,可以保存为字符串、整型或者数据。通常,将ADO记录集的内容存储到这些变量类型中一个。为了从ADO记录集中取出数据,需要手工地拷贝数据到VBScript变量中,每次一个字段。使用任意一个ADO记录集的函数functions GetRows(),GetString() 或者 Save() (ADO 2.5)都非常得快速而且简单,这里有个函数,描述了如何使用GetRows()返回记录集数据的数组:
' Get Recordset, return as an Array
Function FetchEmploymentStatusList
Dim rs
Set rs = CreateObject("ADODB.Recordset")
rs.Open "select StatusName, StatusID from EmployeeStatus", _
"dsn=employees;uid=sa;pwd=;"
FetchEmploymentStatusList = rs.GetRows() " Return data as an Array
rs.Close
Set rs = Nothing
End Function
上述代码的一个更深的技巧是为列表缓存了HTML。下面是个简单的例子:
' Get Recordset, return as HTML Option list
Function FetchEmploymentStatusList
Dim rs, fldName, s
Set rs = CreateObject("ADODB.Recordset")
rs.Open "select StatusName, StatusID from EmployeeStatus", _
"dsn=employees;uid=sa;pwd=;"
s = "<select name=""EmploymentStatus">" & vbCrLf
Set fldName = rs.Fields("StatusName") ' ADO Field Binding
Do Until rs.EOF
' Next line violates Don't Do String Concats,
' but it's OK because we are building a cache
s = s & " <option>" & fldName & "</option>" & vbCrLf
rs.MoveNext
Loop
s = s & "</select>" & vbCrLf
rs.Close
Set rs = Nothing ' See Release Early
FetchEmploymentStatusList = s ' Return data as a String
End Function
在合适的环境下,可以在Application或者Session中缓存ADO记录集本身,但是有2点提示:
- ADO必须是自由线程标记的
- 需要使用disconnected recordset方式
如果不能保证上述2个条件,就不要缓存ADO记录集,因为这会产生很大的危险性。
当在Application或Session中保存数据后,数据将一直保持,除非程序改变它、Session变量到期或者Web应用程序重新启动。如果数据需要更新,怎么办?可以调用只有管理员才能访问的ASP页面来更新数据,或者,通过函数周期性的自动更新数据。下面的例子中,与缓存数据一起保存了时钟标记,过一段时间后,就刷新数据。
<%
' error handing not shown...
Const UPDATE_INTERVAL = 300 ' Refresh interval, in seconds
' Function to return the employment status list
Function GetEmploymentStatusList
UpdateEmploymentStatus
GetEmploymentStatusList = Application("EmploymentStatusList")
End Function
' Periodically update the cached data
Sub UpdateEmploymentStatusList
Dim d, strLastUpdate
strLastUpdate = Application("LastUpdate")
If (strLastUpdate = "") Or _
(UPDATE_INTERVAL < DateDiff("s", strLastUpdate, Now)) Then
' Note: two or more calls might get in here. This is okay and will simply
' result in a few unnecessary fetches (there is a workaround for this)
' FetchEmploymentStatusList function (not shown)
' fetches data from DB, returns an Array
d = FetchEmploymentStatusList()
' Update the Application object. Use Application.Lock()
' to ensure consistent data
Application.Lock
Application("EmploymentStatusList") = d
Application("LastUpdate") = CStr(Now)
Application.Unlock
End If
End Sub
必须意识到,在Session或者Application对象中缓存大容量的数组不是一个好的方法。存取数组中任何元素前,脚本语言的规则要求首先要建立整个数组的临时备份。比如,如果在Application对象中缓存一个100,000个元素的数组,其中包含U.S.邮政编码与本地气象站的对应关系,ASP就必须首先拷贝所有100,000个气象站信息到临时数组中,然后才能选择其中一个字符串进行处理。在这种情况下,创建一个定制的组件,编写一个方法存储气象站信息,是非常好的方法。
技巧3:在Web服务器磁盘上缓存数据和HTML页面
有时候,有“许多”数据要在内存中缓存。“许多”是相对而言的,它取决于能消耗多少内存、缓存项目的数量以及取回数据的频度。任何情况下,如果需要在内存中缓存大量的数据,请考虑以text或者XML文件格式在Web服务器硬盘上做缓存。当然,也可以混合使用硬盘缓存数据以及内存缓存数据,从而达到最佳缓存。
注意:当测试一个单一ASP页面的性能时,从磁盘取回数据不一定比从网络数据库中取回数据快,但是缓存减少了网络数据库的调用。在大规模调用时,这将明显地提高网络的吞吐能力。缓存一个费时的查询结果是非常有用的,比如对于一个复杂的存储过程,或者大量的结果数据。
ASP和COM提供了几种建立基于磁盘缓冲配置的工具。ADO记录集的Save()和 Open()函数负责保存和调入磁盘上的记录集。另外还有一些组件:
- Scripting.FileSystemObject 允许你创建、读取和写文件
- MSXML,Microsoft XML 解析器随Internet Explorer而来,支持保存和装入XML文档
- LookupTable对象(比如在MSN上使用)是从磁盘调入简单列表的很好选择。
最后,考虑缓存磁盘数据的表达式,而不是数据本身。预处理的HTML可以存储为.htm或者.asp文件,链接直接指向它们。使用诸如XBuilder或者Microsoft SQL Server Internet发布类的商业工具,能够自动处理这些过程。而且,也可以在.asp文件中包含HTML程序片断。同样,也可使用FileSystemObject从磁盘上读取HTML文件,或者使用XML for early rendering来做这个工作。
技巧4:避免在Application或Session对象中缓存非轻快型组件
在Application或Session对象中缓存数据是一个很好的方法,但是,缓存COM对象却有严重的缺陷。在Application或Session对象中缓存经常使用的COM对象这个工作是非常吸引人的,但是很不幸,许多COM对象,包括用Visual Basic 6.0或者以前版本编写的对象组件,当存储在Application或Session对象中后,都会产生严重的瓶颈问题。
特别地,当组件编写得不是很轻巧时,就极可能产生瓶颈问题。一个轻型组件就是标记了ThreadingModel=Both的组件,其中合计了自由线程的排列(FTM),或者标记了ThreadingModel=Neutral(Neutral模式是Windows2000和COM+中的新特征)。下面的组件不是轻型的:
- Free-threaded组件(除非被集合成FTM)
- Apartment-threaded组件
- Single-threaded组件
Configured components不是轻型组件,除非它们是Neutral-threaded的。Apartment-threaded组件和其他非轻型组件在页范围内工作得很好,就是说,它们是在一个单一ASP页面中创建并释放的。
如果缓存了非轻型组件,将会发生什么错误?在Session对象中缓存的非轻型组件将会“锁住”会话。ASP维护着一个响应请求的工作线程池,通常,新的请求被第一个可用的工作线程控制。如果一个会话被锁在一个线程中,那么请求就被迫等待到相关的线程变为可用。这里有一个类比:你前往一个超级市场,挑选了一些食品,并在3号付款台付款。从那以后,只要在那个超级市场买食品付款,你就会经常到3号付款台去,虽然其他的付款台人少些甚至没有人。
技巧5:不要在Application或Session对象中缓存数据库连接
缓存ADO连接通常不是一个好的策略。如果一个连接对象被存储在Application对象中,并在所有的页面使用,那么所有页面将会争夺该连接的使用。如果存储在ASP Session对象中,那么将要为每一个用户都打开数据库连接。这将挫败连接池的使用意图,并且在Web服务器和数据库上都施加了不必要的高代价压力。
为了替代缓存数据库连接,可以在使用ADO的每个ASP页面中创建并释放ADO对象。这将非常有效,因为IIS拥有内建的数据库连接池。更准确地说,IIS自动处理OLEDB和ODBC连接池,这将保证在每个页面创建并且释放连接的工作高效进行。
由于连接的记录集存储了数据库连接的参考,所以,不要在Application或Session对象中缓存连接的记录集。然而,可以安全地缓存disconnected类型的记录集,它们并不保存相应数据库连接的参考。为了断开记录集,执行下面2步:
Set rs = Server.CreateObject("ADODB.RecordSet")
rs.CursorLocation = adUseClient ' step 1
' Populate the recordset with data
rs.Open strQuery, strProv
' Now disconnect the recordset from the data provider and data source
rs.ActiveConnection = Nothing ' step 2
更多的关于连接池的信息请访问 ADO and SQL Server。
技巧6:聪明地使用Session对象
Session在繁忙站点上使用时有几个缺陷。繁忙的意思是:站点上每秒有上百的页面被请求,或者同时有上千的访问用户。这个技巧对于那些要求水平扩展强的站点非常重要,也就是指这些站点:它们利用多个服务器完成数据装载或者处理大量容错。对于小型站点,比如内部网Intranet,Session是非常值得提倡的。
再次重申,ASP自动地为每一个首次点击Web服务器的用户创建一个Session,每一个Session占有大约10KB的内存,生存期默认是20分钟。
使用Session最大的问题不是性能,而是扩展性,Session不能跨越多个Web服务器,一旦在一个服务器上创建了Session,它的数据就驻留在那里。这意味着,如果在Web上使用Session,你就得为每一个直接访问存放Session服务器的用户请求设计一个策略。这就是将用户“粘”在Web服务器上,术语“sticky sessions”就来源于此。如果Web服务器遇到障碍,“Stuck”用户就会丢失他们的Session状态,因为Session不保留在磁盘上。
执行粘性session的策略包括硬件与软件解决方式,比如windows2000高级服务器中的 Network Load Balancing 以及Cisco公司的Local Director,但换取这些要牺牲一定的扩展性。
Application对象也不能跨越服务器。如果需要在Web群中共享并更新Application数据,就需要使用后台数据库。然而,只读Application数据在Web群中仍然很有用。
许多对任务要求严格的站点都要设立至少2个Web服务器,所以在设计严格任务的应用程序时,就需要执行“sticky sessions”,或者简单地避免使用Session,同时也可以采取其他保存用户状态到独立Web服务器的管理技术。
如果不使用Session,一定要确认将它们关闭,这可以通过Internet服务管理器实现。如果决定使用Session,可以通过几种方法来最小化它们的影响。
可以将不需要Session的内容(比如帮助画面,访问者区域,等等)移动到关闭Session的独立ASP应用程序中。在基础页面上,可以给ASP一个指示,让它不需要使用Session。将下面的代码直接加入到ASP页面的头部:
<% @EnableSessionState=False %>
使用这个指示的一个很好的解释是在框架结构中Session创建了一个有趣的问题。ASP确保在一个时刻只有一个来自Session的请求被执行,这就确保了如果浏览器为单个用户请求多个页面时,只有一个ASP请求在那时能够接受Session,如此就避免了存取Session对象时的多线程问题。很不幸,在框架结构中的所有页面将按照连续的顺序显示出来,一个接一个,而不是同时,所以用户为了看到整个框架必须要等很长时间。规则是:如果一定的框架页面没有使用Session,就一定要告诉ASP直接使用@EnableSessionState=False。
除了使用Session对象,还有许多其他管理会话状态的选择。对于小数量的状态(小于4KB),我们通常建议使用cookie、查询字符串变量以及表单隐藏域。对于象购物车一样的大数量数据,后台数据库是最合适的选择。
技巧7:将代码装入COM对象中
如果要编写很多VBScript或者JScript,为了提个性能,可以将代码编写成COM对象并且编译使用。编译代码基本上比解释性代码运行快许多,编译组件对象可通过“early binding”存取其他COM对象,这比在脚本中调用组件要有效。
这么做有许多优点:
- COM对象有益于从商业规则中独立出表达式规则
- COM对象使代码重用变为可能
- 许多开发者发现用VB,C++或者Visual J++编写程序,比ASP更容易调试
COM对象也有缺点,包括初始开发时间和对不同编程技巧的需要。注意将少量ASP代码做成COM对象组件不会有好处,反而可能导致性能的损失,从而失去了编译代码的优势。怎样组合使用ASP脚本和COM对象达到最佳性能是一个测试的问题。我们注意到微软公司已经大规模在Windows 2000/IIS 5.0上提高了脚本与ADO的性能,由此,随着IIS5.0版本的引进,减少了编译代码的性能优势。
技巧8:使用Option Explicit
要在ASP文件中使用Option Explicit定义,并且放置到ASP文件的头部,从而强迫开发者在使用前声明所有的变量。许多程序员都认为这在应用程序调试时非常有用,因为它避免了产生错误类型变量以及偶然创建新变量的可能。
也许更重要的是,声明的变量要大大快于非声明变量。
技巧9:拷贝经常使用的数据到脚本变量中
在ASP中存取COM对象时,应该拷贝经常使用的对象数据到脚本变量中,这样就减少了对COM对象的方法调用。这些调用要比存取脚本变量相对来说费时费力。当存取Collection和Dictionary对象时,使用这项技巧也减少了昂贵的查找操作。
通常,如果要不止一次地存取对象数据,就应将数据放入脚本变量中,对象数据主要也就是Request变量(表单和查询字符串变量)。比如,站点要传递一个叫做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 ' ...
上面的代码执行后,发生以下事情:
1、变量Foo被当作全局对象
2、变量bar被当作Foo的一员
3、变量blah被当作Foo.bar的一员
4、变量qaz被当作Foo.bar.blah的一员
5、调用Foo.bar.blah.quaz(1)
6、再执行步骤1到3分解baz
7、分解baz做为Foo.bar.blah的一员
8、再执行步骤1到3分解zaq
9、再执行步骤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编程中。
技巧10:避免再定义数组
争取不要再定义数组。考虑到性能问题,如果机器的物理内存大小不够,最好按最差情况或者最佳情况设置数组的初始尺寸,需要时再重新定义。
下面的代码展示了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"
% >
简单地定义数组初始尺寸为合适的大小是非常好的,而不要用Redim加大数组。这么做也许浪费了一些内存(如果没有完全地使用空间),但是赢得了速度。
技巧11:使用Response Buffering
通过打开“response buffering”可以缓冲一个值得输出的整个页面内容,这将最小化输出到浏览器的数据量,从而提高了整体性能。每一次输出都耗费许多,所以写得越少,效果越好。TCP/IP在发送少量大的数据包时,要比发送大量小的数据包工作效率高,因为它是慢速启动并不断发送的。
有2种方法打开Response Buffering。首先,可以使用Internet Services Manager为整个应用程序打开response buffering,这是推荐的方式,而且在IIS4.0和IIS5.0中,默认状态下,response buffering是打开的。其次,在每一页面上,可以在头部放置如下代码开打开response buffering:
< % Response.Buffer = True % >
这段代码必须在任何数据输出到浏览器前被执行(就是说,在任何html内容显示前和在任何cookie被设置前)。通常情况下,为整个应用程序打开response buffering是很好的方案,这么做后就不用在每个页面头部设置如上的代码。
关于打开response buffering的一个通用问题是:用户必须要等待整个页面全部产生后,才能看到内容。对于一个长时间运行的页面来说,可以设置Response.Buffer=False关闭缓冲。然后,好的策略是利用Response.Flush方法,它将输出所有已被ASP描述的HTML内容到浏览器。比如,在描述了一个1,000行表格的100行后,ASP就可以使用Response.Flush来强迫输出这100行的内容到浏览器,这时用户就可以看到前100行数据,同时其余的行数据正在准备生成。
注意,关于上面的1,000行表格输出的例子,对于一些浏览器器来说,除非遇到< /table >标记,它们不会输出表格的任何内容。如果这样,可以将表格分割成许多含有少量行的多个表格,然后在每一个表格产生后,调用Response.Flush输出。新版的Internet Explorer在整个表格下载后才显示内容,并且,如果定义了表格的列宽度,生成表格的速度将特别快。
关于打开response buffering的另外一个问题是:当生成非常大的页面时,将消耗非常大的服务器内存。
技巧12:批处理单行脚本和Response.Write命令
VBScript语法< % = expression % >的意思是输出expression的数值。如果response buffering没有打开,每个这样的语句将按照许多小数据包的形式输出数据到浏览器,这将降低程序性能。因此,请使用下面的技巧:替换紧挨着的多个一行表达式调用为一个调用,用Response.Write名称输出。比如,在下面的例子中,对于每行每个字段的输出,只有一个写操作:
<table>
<% For Each fld in rs.Fields %>
<th><% = fld.Name %></th>
<%
Next
While Not rs.EOF
%>
<tr>
<% For Each fld in rs.Fields %>
<td><% = fld.Value %></td>
<% Next
</tr>
<% rs.MoveNext
Wend %>
</table>
下面是更有效率的代码,每行一个输出:
<table>
<%
For each fld in rs.Fields
Response.Write ("<th>" & fld.Name & "</th>" & vbCrLf)
Next
While Not rs.EOF
Response.Write ("<tr>")
For Each fld in rs.Fields %>
Response.Write("<td>" & fld.Value & "</td>" & vbCrLf)
Next
Response.Write "</tr>"
Wend
%>
</table>
当response buffering关闭时,这个技巧非常得有用。最好是打开response buffering,这样就可以看到批量的Response.Wwrite是如何提高了程序性能。
技巧13:使用< OBJECT >标记引用对象
如果需要引用除代码路径外的对象(尤其是服务器、Application范围的对象),请在Global.asa文件中使用
< object runat=server id=objname >标记来定义它们,而不要使用Server.CreateObject方法。使用Server.CreateObject方法可以立即创建对象,这样如果随后不使用它,就浪费了资源。使用
< object id=objname >标记可以定义对象objname,但是直到它的属性或者方法首次使用时,objname才实际创建。
技巧14:避免在循环中串联字符串
许多人在循环中建立一个字符串,就象下面的样子:
s = "<table>" & vbCrLf
For Each fld in rs.Fields
s = s & " <th>" & fld.Name & "</th> "
Next
While Not rs.EOF
s = s & vbCrLf & " <tr>"
For Each fld in rs.Fields
s = s & " <td>" & fld.Value & "</td> "
Next
s = s & " </tr>"
rs.MoveNext
Wend
s = s & vbCrLf & "</table>" & vbCrLf
Response.Write s
这存在几个问题。首先是重复的连接字符串消耗二次方的时间,而且,运行的时间与计算的字段数量也是平方的关系。下面的简单例子更清楚地说明这一点:
s = ""
For i = Asc("A") to Asc("Z")
s = s & Chr(i)
Next
在第1层循环时,S的值是“A”;第2层循环时,VBScript要重新分配字符串,拷贝了2个字符(“AB”)到S中;第3层循环时,需要再重新分配并且拷贝3个字符到S中。在第N层循环时,就需要重新分配并拷贝N个字符到S中。那就是1+2+3+...+N的总和,也就是N*(N+1)/2个拷贝。
在上面的记录集例子中,如果有100个记录和5个字段,内部循环就要执行100*5=500次,并且,完成所有拷贝和再分配任务的时间将接近500*500=250,000。这还是一个适当尺寸记录集的拷贝工作。
在这个例子中,可以通过替换字符串连接为Response.Write()或者行内脚本(< % =fld.Value % >)的方法提高程序性能。如果response buffering打开(也应该打开),这将很快,因为Response.Write仅仅附加数据在缓冲区的尾部,而且不需要再分配。
如果用JScript连接字符串,强烈建议使用“+=”操作符,就是说,使用s+=“字符串”,而不是s = s+“字符串”。
技巧15:打开浏览器和代理的缓冲
默认情况下,ASP禁止了浏览器和代理的缓冲功能。如果有一个每次都不要更新的页面,就应该打开浏览器和代理的缓冲,这将允许浏览器和代理在一段时间内使用该页面的“缓冲”拷贝数据。缓冲能够大大地减轻服务器的数据转载量,并提高用户的浏览性能。
哪些类别的动态页面适合被缓存呢?下面是一些例子:
- 天气页面,每5分钟更新一次
- 新闻或版本列表页面,每天更新2次
注意:使用浏览器或者代理缓存后,对Web服务器的点击次数就会减少。如果想精确地了解所有页面,或者对于邮递广告,就不适于使用浏览器和代理缓存了。
浏览器缓存由HTTP“Expires”头参数控制,它由Web服务器发送给浏览器。ASP提供了2个简单的方法发送这个头部参数。设置页面在未来一定时间内到期,可以使用Response.Expires属性。下面的例子将告诉浏览器内容在10分钟后过期:
< % Response.Expires = 10 % >
设置Response.Expires为负数或者0,就禁止了缓存。对第2个属性Response.ExpiresAbsolute的设置,允许指定在一个特殊时间到来时内容过期。
< % Response.ExpiresAbsolute = #May 31,2001 13:30:15# % >
除了使用Response对象来设置到期时间,还可以在HTML文件头部写< META >标记。尽管代理不会注意到这个标记,但是一些浏览器可以。
< META HTTP-EQUIV="Expires" VALUE="May 31,2001 13:30:15" >
最后,对于HTTP代理,使用Response.CacheControl可以指示是否缓存内容。设置属性为“Public”,打开代理缓存内容的功能。
< % Response.CacheControl = "Public" % >
默认情况下,这个属性是设置成“Private”的。注意:不要让代理缓冲那些显示给特定用户的页面,因为代理可能会将属于其他用户的页面送给当前用户。
技巧16:在任何可能时使用Server.Transfer,而不要用Response.Redirect
Response.Redirect告诉浏览器请求另一个不同的页面,这常常用于引导用户到登录页面或者出错处理页面。由于重定向强迫了一个新页面请求,结果是浏览器必须要与Web服务器循环2次,并且Web服务器必须处理一个额外的请求。IIS5.0引进了一个新功能Server.Transfer,它执行在同一服务器上的页面传输,这将避免额外的浏览器-Web服务器的数据循环,形成良好的系统性能,对于用户也有较好的响应时间。
技巧17:避免使用服务器变量
存取服务器变量导致Web站点建立一个特殊的请求并收集所有的服务器变量,而并不是你要求的那个变量。这类似于在文件夹中取回一个特殊的文件,要想取回一个文件,就得首先获取所在文件夹的信息。
不要存取非法的Request对象(比如Request("Data")),对于那些不在Request.Cookies、Request.Form、Request.QueryString或者Request.ClientCertificate中的项目,隐含就指向了Request.ServerVariables变量,而这些变量要比其他集合对象慢得多。
技巧18:调整Web服务器
有几个IIS调整参数可以提高站点性能。比如,对于IIS4.0,我们经常发现提高ASP ProcessorThreadMax参数能够产生重大的效果,特别是在那些要等待后台资源比如数据库或中间件产品的站点。在IIS5.0中,你可以发现调整ASP线程通道要比调整AspProcessorThreadMax效果更佳。
最佳的配置设定取决于应用程序代码、支持的硬件设备以及客户端的工作量。发现最佳配置的唯一方法就是测试。