现在的位置: 首页 > 综合 > 正文

改进性能和样式的 24个 ASP 技巧(zz)

2013年08月03日 ⁄ 综合 ⁄ 共 21738字 ⁄ 字号 评论关闭
简介
技巧1:在Web服务器上缓存常用数据
技巧2:在Application或Session对象中缓存常用数据
技巧3:在Web服务器磁盘上缓存数据和HTML
技巧4:避免在Application或Session对象中缓存非灵活组件
技巧5:不要在Application或Session对象中缓存数据库连接
技巧6:妙用Session对象
技巧7:在COM对象中封装代码
技巧8:晚点获取资源,早点释放资源
技巧9:进程外的执行将牺牲可靠性
技巧10:显式使用选项
技巧11:在子例程和函数中使用局部变量
技巧12:将常用数据复制到脚本变量
技巧13:避免重新定义数组
技巧14:使用响应缓冲
技巧15:批处理内嵌脚本和Response.Write语句
技巧16:在开始长时间的任务之前先使用Response.IsClientConnected
技巧17:使用<OBJECT>标记实例化对象
技巧18:使用ADO对象和其他组件的TypeLib绑定
技巧19:利用浏览器的验证能力
技巧20:在循环中避免字符串串联
技巧21:启用浏览器和代理缓存
技巧22:尽可能使用Server.Transfer替代Response.Redirect
技巧23:在目录URL尾部加斜线
技巧24:避免使用服务器变量

--------------------------------------------------------------------------------
简介
性能是一个特性。您需要预先设计性能,或是在日后重新编写应用程序。换句话说,什么是最大限度优化ActiveServerPages(ASP)应用程序性能的好策略?
本文为优化ASP应用程序和"VisualBasic(R)脚本编辑器(VBScript)"提供了许多技巧。对许多陷阱和缺陷进行了讨论。本文所列的建议均在http://www.microsoft.com及其他站点上进行了测试,而且工作正常。本文假定您对ASP开发有基本的理解,包括对VBScript和/或JScript、ASPApplication、ASPSession和其他ASP内部对象(请求、响应和服务器)。
ASP的性能,通常不止取决于ASP代码本身。我们并不想在一篇文章中囊括所有的至理名言,只在最后列出与性能相关的资源。这些链接包括ASP和非ASP主题,包括"ActiveX(R)数据对象(ADO)"、"部件对象模型(COM)"、数据库和"Internet信息服务器(IIS)"配置。这些是我们喜欢的链接-务请关注它们。
技巧1:在Web服务器上缓存常用数据
典型的ASP页从后端数据库检索数据,然后将结果转换为超文本标记语言(HTML)。无论数据库的速度如何,从内存检索数据要比从后端数据库检索数据快得多。从本地硬盘读取数据通常也要比从数据库检索数据快得多。因此,通常可以通过在Web服务器(在内存或磁盘)上缓存数据来改善性能。
缓存是典型的空间与时间的折衷。如果恰当地缓存数据,您将看到性能会有惊人的提高。为使缓存发挥效力,它必须保持经常重用的数据,而且重新计算这些数据的代价是昂贵的或比较昂贵的。如果缓存充满了垃圾数据,则是对存储器的浪费。
不经常变化的数据也是缓存的候选数据,因为您无须担心数据与数据库的同步问题。组合框、引用表、DHTML碎片、可扩展标记语言(XML)字符串、菜单项和站点配置变量(包括数据源名称(DSN)、Internet协议(IP)地址和Web路径)都是缓存的候选数据。注意,您可以缓存数据的表示而不是数据本身。如果ASP页不经常更改,而且缓存的成本也非常高(例如,整个产品目录),请考虑预先生成HTML,而不是在每次请求时重新绘制。
数据应缓存在何处,有哪些缓存策略?数据经常缓存在Web服务器内存或Web服务器磁盘上。下面两个技巧讨论这些选项。
技巧2:在Application或Session对象中缓存常用数据
ASPApplication和Session对象为在内存中缓存数据提供了方便的容器。既可以将数据赋予Application对象,也可将数据赋予Session对象,这些数据在HTTP调用中将保留在内存中。Session数据按用户存储,而Application数据在所有用户间共享。
何时将数据载入Application或Session?通常,在Application或Session启动时加载数据。要在Application或Session启动时加载数据,请在下面两函数中添加相应的代码:
Application_OnStart()

Session_OnStart()
。这两个函数应该位于Global.asa;如果没有,可以添加这些函数。也可以在第一次需要数据时加载数据。要进行上述操作,请在ASP页中添加一些代码(或编写可重用的脚本函数),这些代码检查数据是否存在,并在数据不存在时加载数据。这是称为迟缓计算的经典性能技术的例子-在您的确需要它之前,不进行计算。请看例子:
<%
FunctionGetEmploymentStatusList
Dimd
d=Application("EmploymentStatusList")
Ifd=""Then
'FetchEmploymentStatusList函数(不显示)
'从DB中取出数据,返回数组
d=FetchEmploymentStatusList()
Application("EmploymentStatusList")=d
EndIf
GetEmploymentStatusList=d
EndFunction
%>
可以为每一块所需的数据编写类似的函数。
数据应该以什么格式存储?任何变量类型均可存储,因为所有脚本变量是各不相同的。例如,可以存储字符串、整型或数组。通常,您将以这些变量类型之一存储ADO记录集的内容。若要获取ADO记录集衍生的数据,可以手工将数据复制到VBScript变量中,每次一个字段。使用一个ADO记录集保留函数GetRows()、GetString()或Save()(ADO2.5),会更快更简便。完整而详细的内容已超出了本文的范围。下面的演示函数使用了
GetRows()
来返回记录集数据的数组:
'取记录集,以数组返回
FunctionFetchEmploymentStatusList
Dimrs
Setrs=createObject("ADODB.Recordset")
rs.Open"selectStatusName,StatusIDfromEmployeeStatus",_
"dsn=employees;uid=sa;pwd=;"
FetchEmploymentStatusList=rs.GetRows()'以数组返回数据
rs.Close
Setrs=Nothing
EndFunction
对上面示例的进一步改进应当是缓存该列表的HTML,而不是缓存数组。下面是一个简单的范例:
'取记录集,以"HTML选项"列表返回
FunctionFetchEmploymentStatusList
Dimrs,fldName,s
Setrs=createObject("ADODB.Recordset")
rs.Open"selectStatusName,StatusIDfromEmployeeStatus",_
"dsn=employees;uid=sa;pwd=;"
s="<selectname=""EmploymentStatus">"&vbCrLf
SetfldName=rs.Fields("StatusName")'ADO字段绑定
DoUntilrs.EOF
'下面一行违背了不要进行字符串连接,
'但这是可以的,因为我们正在建立高速缓存
s=s&"<option>"&fldName&"</option>"&vbCrLf
rs.MoveNext
Loop
s=s&"</select>"&vbCrLf
rs.Close
Setrs=Nothing'参见尽早释放
FetchEmploymentStatusList=s'以字符串返回数据
EndFunction
在正常的情况下,可以在Application或Session作用域中缓存ADO记录集本身。有两个警告:
ADO必须为标记的自由线程
必须使用断开连接的记录集。
如果不能保证满足这两个要求,请不要缓存ADO记录集。在下面的非灵活组件和不要缓存连接技巧中,我们将讨论在Application或Session作用域中存储COM对象的危险。
如果在Application或Session作用域中存储数据,这些数据将一直保留在那儿,直到在程序中改变它、Session过期或Web应用程序重新启动时为止。数据需要更新如何处理?若要用手工强制更新应用程序数据,可以调用只允许管理员访问的数据更新ASP页。另外,还可以通过函数,周期地自动刷新数据。下面的示例存储带缓存数据的时间戳,在指定时间间隔后刷新数据。
<%
'未显示错误处理...
Constupdate_INTERVAL=300'刷新时间间隔,以秒计
'函数返回雇佣状态列表
FunctionGetEmploymentStatusList
updateEmploymentStatus
GetEmploymentStatusList=Application("EmploymentStatusList")
EndFunction
'定期更新缓存的数据
SubupdateEmploymentStatusList
Dimd,strLastupdate
strLastupdate=Application("Lastupdate")
If(strLastupdate="")Or_
(update_INTERVALDateDiff("s",strLastupdate,Now))Then
'注意:此处可能有两个或多个调用。这是可以的,只不过
'产生几个不必要的取指令罢了(就此有一个工作区)
'FetchEmploymentStatusList函数(不显示)
'从DB中取数据,返回一个数组
d=FetchEmploymentStatusList()
'更新Application对象。用Application.Lock()
'来确保一致的数据
Application.Lock
Application("EmploymentStatusList")=d
Application("Lastupdate")=CStr(Now)
Application.Unlock
EndIf
EndSub
其他示例,请参阅具有Application数据的最快列表框(英文)。
请注意,在Session或Application对象中缓存大型数组并非上策。在访问数组元素之前,脚本语言的语法要求建立整个数组的临时副本。例如,如果在Application对象中缓存了将美国邮政编码映射到本地气象站的字符串数组,该字符串数组有100,000个元素,ASP在找出一个字符串之前,必须将所有100,000个气象站复制到临时数组中。在这种情况下,建立带自定义方法的自定义组件,来存储气象站-或使用一个字典组件,也许更好。
请不要在倒洗澡水时把孩子一同倒掉,对这种观点的一个新的注解是:数组提供了对内存中相邻关键-数据对的快速查找和存储。索引字典比索引数组要慢。您应该根据具体情况选择能够提供最佳性能的数据结构。
技巧3:在Web服务器磁盘上缓存数据和HTML
有时,数据过多不能在内存中进行缓存。"过多"是一种定性的判断;它取决于打算消耗的内存量,还有缓存项的数量和这些项的检索频率。总之,如果有过多的数据要在内存中缓存,请考虑以文本或XML文件的形式,在Web服务器的硬盘上缓存数据。可以将在磁盘上缓存数据和在内存中缓存数据组合起来,为站点建立最优的缓存策略。
注意,在度量单个ASP页的性能时,在磁盘上检索数据不一定比从数据库中检索数据快。但是,缓存减轻了数据库和网络的负荷。在高负荷情况下,这将明显提高总体通信量。在查询成本很高时缓存查询的结果,缓存便非常有效,例如多表联合或复杂的存储过程,或缓存大型的结果集。按照惯例,测试竞争方案。
ASP和COM提供了几种构建磁盘缓存方案的工具。ADO记录集的Save()和Open()函数,保存和加载磁盘上的记录集。您可以使用这些方法重写上面Application数据缓存技巧中的范例代码,用Save()文件替换向Application对象写入数据的代码。
还有其他一些处理文件的组件:
Scripting.FileSystemObject使您能够创建、读取和写入文件。
MSXML是随InternetExplorer提供的Microsoft(R)XML解析器,它支持保存和加载XML文档。
LookupTable对象(在MSN上使用的范例)是从磁盘加载简单列表的良好选择。
最后,请考虑在磁盘上缓存数据的表示,而不是数据本身。预制的HTML可以作为.htm或.asp文件存储在磁盘上;超级链接可以直接指向这些文件。可以使用商业工具,如XBuilder或Microsoft(R)SQLServer的Internet发行功能来自动化HTML生成过程。另外,可以将HTML片段#include到.asp文件。还可以使用FileSystemObject从磁盘读取HTML文件或使用XML进行早期调整(英文)。
技巧4:避免在Application或Session对象中缓存非灵活组件
虽然在Application或Session对象中缓存数据是个好主意,但是缓存COM对象可能有严重缺陷。将常用COM对象嵌入Application或Session对象通常具有吸引力。遗憾的是,很多COM对象,包括用VisualBasic6.0或更早版本编写的COM对象,在Application或Session对象中存储时将导致严重的瓶颈。
特别是任何非灵活组件,在Session或Application对象中缓存时将导致性能瓶颈。灵活组件是标记为
ThreadingModel=Both
的组件(它聚集了自由线程汇集器(FTM))或标记为
ThreadingModel=Neutral
的组件(Windows(R)2000和COM+中新增的"中性"模型。)下列组件是非灵活的:
自由线程组件(除非它们聚集了FTM)。
单元线程组件。
单线程组件。
已配置组件(MicrosoftTransactionServer(MTS)/COM+库和服务器包/应用程序)为非灵活组件,除非它们是"中性"线程的。单元线程组件和其他非灵活组件最适于在页作用域工作(也就是说,它们在单个ASP页上创建和销毁)。
在IIS4.0中,标记为
ThreadingModel=Both
的组件被视为灵活的。在IIS5.0中,这已经不够了。组件不仅必须标记为Both,而且还必须聚集FTM。灵活性文章说明了如何使得用"活动模板库"编写的C++组件聚集FTM。请注意,如果组件缓存接口指针,这些指针本身必须为灵活的、或者必须存储在"COM全局接口表(GIT)"中。如果不能重新编译Both线程组件,使它聚集FTM,则可以将该组件标记为
ThreadingModel=Neutral
。另外,如果不希望IIS进行灵活性检查(这样,希望非灵活组件能够存储在Application或Session作用域中),可以在metabase中设置
AspTrackThreadingModel

True
。不主张更改
AspTrackThreadingModel

如果试图在Application对象中存储用
Server.createObject
创建的非灵活组件,IIS5.0将产生错误。可以通过在Global.asa中使用
<objectrunat=serverscope=application...>
解决该问题,但是不主张这样做,因为这将导致汇集和串行化,说明如下。
如果缓存非灵活组件,会发生什么错误呢?缓存在Session对象中的非灵活组件,将把会话"锁定"到某个ASP工作器线程。ASP维护着一个工作器线程池,它向请求提供服务。通常,新的请求由第一个可用的工作器线程来处理。如果Session被锁定到某个线程,则该请求将不得不等待它所关联的线程变为可用。打个比方:您进入一个超市,挑选了一些食品,然后在第3号收款台交款。从这以后,每当您在这个超市购买食品,都不得不始终在第3号收款台交款,即使是在其他收款台人少或没人时。
将非灵活组件存储在Applicaton作用域甚至会对性能产生更严重的影响。ASP将不得不创建专用的线程来运行非灵活的、Applicaton作用域内的组件。这将导致两种后果:所有调用不得不被汇集到该线程,而且所有调用被串行化。汇集意味着:参数不得不存储在内存的共享区;对该专用线程执行昂贵的上下文切换;组件的方法被执行;结果汇集到共享区域;以及经过另一个昂贵的上下文切换,使控制权返回原来的线程。串行化意味着所有方法必须一个挨一个地运行(同一时刻只能运行一个方法)。两个不同的ASP工作器线程不可能同时执行共享组件上的方法。这将扼杀并行机制,尤其是在多处理器计算机上。更坏的是,所有非灵活的、Application作用域内的组件都将共享一个线程("HostSTA"),所以串行化的影响更加严重。
是否感到困惑?下面我们提出几个通用规则。如果您正在用VisualBasic(6.0)或更早版本编写对象,请不要将它们缓存在Application或Session对象中。如果您不知道对象的线程模型,就不要缓存它。不要缓存非灵活对象,而应当在每页上创建并释放它们。对象将直接运行在ASP工作器线程上,这样,将不会发生汇集或串行化。如果COM对象正运行在IIS框中,而且如果它们没有花很长时间来初始化和取消,性能将是足够的。注意,不要用该方法使用单线程对象。小心:VB可以创建单线程的对象!如果您必须以该方式使用单线程的对象(如MicrosoftExcel电子表格),则不要期望有很高的吞吐量。
当ADO被标记为自由线程时,则缓存ADO记录集是安全的。要将ADO标记为自由线程,请使用Makfre15.bat文件,该文件通常位于如下目录中://ProgramFiles/Common/System/ADO。
警告:如果您正在用MicrosoftAccess作为数据库,则不应当将ADO标记为自由线程。通常,ADO记录集还必须是断开连接的,如果您不能控制站点的ADO配置(例如,您是独立的软件厂商[ISV],将Web应用程序卖给客户,然后由他们来管理他们自己的配置),那么不缓存记录集可能会更好。
词典组件也是灵活对象。LookupTable从数据文件加载它的数据,并且它对组合框数据和配置信息是有用的。来自DuwamishBooks的PageCache对象提供了目录语义,和CaprockDictionary的表现一样。这些对象或它们的派生对象可以构成有效缓存策略的基础。注意,Scripting.Dictionary对象不是灵活的,所以不应当存储在Application或Session作用域。
技巧5:不要在Application或Session对象中缓存数据库连接
缓存ADO连接通常是不好的策略。如果一个Connection对象存储在Application中,并在所有页上使用,那么所有页将竞争使用该连接。如果Connection对象存储在ASPSession对象中,那么将为每个用户创建数据库连接。这将连接池的好处毁于一旦,并对Web服务器和数据库产生不必要的压力。
取代缓存数据库连接的方法是,在每个使用ADO的ASP页上创建并取消ADO对象。这是个有效的方法,因为IIS具有内置的数据库连接池。更准确的说,IIS自动启用OLEDB和ODBC连接池。这确保了创建并取消每个页上的连接将是有效的。
由于被连接的记录集中存储有对数据库连接的引用,所以,不应当在Application或Session对象中缓存被连接的记录集。但是,可以安全地缓存断开连接的记录集,因为它不包含对其数据连接的引用。要断开记录集的连接,请执行如下两个步骤:
Setrs=Server.createObject("ADODB.RecordSet")
rs.CursorLocation=adUseClient'第1步
'植入带数据的记录集
rs.OpenstrQuery,strProv
'现在断开记录集同数据提供者和数据源的连接
rs.ActiveConnection=Nothing'第2步
有关连接池的详细信息,请参阅ADO和SQLServer(英文)引用。
技巧6:妙用Session对象
在肯定了在Applications和Sessions中缓存的优点之后,我们建议您避免使用Session对象。下面将会谈到,当用于忙碌站点时,Sessions有几个缺点。所谓忙碌,通常是指站点每秒请求数百页或同时有数千个用户。该技巧对于必须进行水平扩展的站点,即那些利用多个服务器来适应负载或执行容错功能的站点来说,更加重要。对于较小的站点,如intranet站点,Sessions的便利,与开销相比也是值得的。
为了翻新,ASP自动为每个访问Web服务器的用户创建一个Session。每个Session有大约10KB内存开销(在存储在Session中的任何数据中是最高的),并使所有的请求都慢了一点。Session一直保持活动状态,直到达到可配置的超时(通常20分钟)为止。
Session最大的问题不是性能而是可伸缩性。Session不能跨越Web服务器;一旦在一个服务器上创建了Session,它的数据就保持在那里。这意味着,如果您在Web领域中使用Sessions,您将不得不为每个用户的请求设计一种策略,以便始终将这些请求引向用户的Session所在的服务器。这被称为将用户"粘"到Web服务器上。术语"粘性会话"即来源于此。由于Session没有保持到磁盘上,所以,当Web服务器崩溃时,被"粘住"的用户将丢失他们的Sessions状态。
用于实施粘性会话的策略包括硬件和软件解决方案。如Windows2000AdvancedServer中的网络负载平衡解决方案和Cisco公司的"本地指向器"解决方案可以实施粘性会话,但以牺牲一些可伸缩性为代价。这些解决方案并不完美。我们不主张您现在全盘推翻您的软件解决方案(我们过去常用ISAPI筛选器和URL矫直对方案进行检查)。
Application对象也不能跨越服务器;如果您需要在Web领域内共享并更新Application数据,则需要使用后端数据库。但只读的Application数据在Web领域中仍然有用。
如果只是为了增加正常运行时间(用于处理故障转移和服务器维护),大多数执行重要任务的站点将需要部署至少两台Web服务器。所以,在设计执行重要任务的应用程序时,您将需要实施"粘性会话",或者简单地避开Sessions以及其他任何在单个Web服务器上存储用户状态的状态管理技术。
如果当前没有使用Sessions,请确保将它们关闭。可以通过"Internet服务管理器"(请参阅ISM文档)来为应用程序执行该操作。如果决定使用Sessions,可以采取几个方法来将对性能的影响降低到最小。
可以将不需要Sessions的内容(如"帮助"屏幕、访问者区域等)移动到关闭了Sessions的、单独的ASP应用程序中。可以逐页提示ASP:在给定的页中您不需要Session对象;使用位于ASP页顶端的如下指令:
<%@EnableSessionState=False%>
使用该指令的一个很好的原因是,Session给框架集带来了有趣的问题。ASP保证任何时候只执行一个来自Session的请求。这样可以确保如果浏览器为一个用户请求了多个页时,在每一时刻只有一个ASP请求将进入Session;这就避免了在访问Session对象时出现多线程问题。遗憾的是,结果,框架集中的所有页均被以串行化方式绘制,一个接一个地,而不是同时地。这样,用户可能不得不等待很长时间才能得到所有框架内容。这意味着:如果某些框架页不信任Session,一定要使用
@EnableSessionState=False
指令告诉ASP。
作为使用Session对象的替代方式,有很多方法可以用来管理Session状态。对于状态数量较小的情况(不到4KB),通常建议使用Cookies、QueryString变量和隐藏形式的变量。对于较大数量的数据,如购物推车,则使用后端数据库是最合适的选择。关于在Web服务器领域中的状态管理技术已经有很多资料。详细信息,请参阅会话状态(英文)。
技巧7:在COM对象中封装代码
如果您有很多VBScript或JScript,那么您可以通过把代码移动到已编译的COM对象来经常改进它们的性能。已编译的代码通常比被解释代码运行得更快。已编译的COM对象可以通过"早期绑定"访问其他COM对象,这种调用COM对象方法的手段,比脚本所使用的"后期绑定"更有效。
将代码封装在COM对象种有如下好处(超越性能):
COM对象是将表达逻辑与业务逻辑分隔开来的好办法。
COM对象启用了代码重用。
很多开发商发现,用VB、C++或VisualJ++书写的代码,比ASP更容易调试。
COM对象有一些缺点,包括初始开发时间以及需要不同的编程技巧。需要警告您的是,封装"少"量的ASP可能会导致性能降低,而不是提高。通常,在少量ASP代码封装到COM对象时出现这样的情况。这时候,创建和调用COM对象的开销,超过了已编译代码的好处。至于ASP脚本和COM对象代码怎样合并才能产生最佳性能还有待测试。注意,与WindowsNT(R)4.0/IIS4.0相比,Microsoft已经在Windows2000/IIS5.0中极大地提高了脚本和ADO性能。这样,已编译代码对ASP代码的性能优势已经随着IIS5.0的引入而降低。
有关在ASP中使用COM对象的优缺点的更多讨论,请参阅ASP组件准则和用COM和MicrosoftVisualBasic6.0对分布式应用程序进行编程(英文)。如果您的确部署了COM组件,要对它们进行强度测试是非常重要的。实际上,所有ASP应用程序都应当作为正式过程进行强度测试。
技巧8:晚点获取资源,早点释放资源
这是个小技巧。通常,最好晚点获取资源而要早点释放资源。这些资源包括COM对象、文件句柄和其他资源。
ADO连接和记录集是这种优化的首要目标。当您使用完记录集,就是说用它的数据打印完一个表格后,请立即将它释放,而不是等到页的末尾。将您的VBScript变量设置为
Nothing
是最好的做法。不要让记录集简单地脱离作用域。同时,应当释放任何有关的Command或Connection对象。(不要忘了对记录集或"连接"调用
Close()
,在将它们设置为
=Nothing
之前。)这将缩短数据库必须为您调整资源的时间跨度,并将数据库连接尽可能快地释放给连接池。
技巧9:进程外的执行将牺牲可靠性
ASP和MTS/COM+都有允许您以可靠性换取性能的配置选项。当建立和部署应用程序时,应当理解这种交换。
ASP选项
ASP应用程序可以配置为以三种方式之一运行。在IIS5.0中引入了术语"隔离级"来描述这些选项。三个隔离级值分别是低、中和高:
低级隔离。该隔离级在所有版本的IIS中受到支持,并且是最快的。它在主IIS进程Inetinfo.exe中执行ASP。如果ASP应用程序崩溃,则IIS也将崩溃。(要在IIS4.0下重新启动IIS,Web站点管理员需要使用工具,如InetMon,来监视站点,如果服务器失败,将运行批处理文件来重新启动服务器。而IIS5.0则引入了可靠的重新启动,它将自动重新启动失败的服务器。)
中级隔离。IIS5.0引入了这个新隔离级,它称为进程外的,这是因为ASP运行在IIS进程之外。在中级隔离中,所有被配置按"中级"运行的ASP应用程序,将共享单个进程空间。这将减少在一个服务器上运行多个进程外的ASP应用程序所需的进程数。中级是IIS5.0中默认的隔离级。
高级隔离。在IIS4.0和IIS5.0中受到支持,高级隔离也是进程外的。如果ASP崩溃,则Web服务器并不崩溃。ASP应用程序将在下一个ASP请求时自动重新启动。使用高级隔离,每个被配置为按高级运行的ASP应用程序,将在其自己的进程空间中运行。这样可以保护ASP应用程序彼此不受干扰。它的缺点是它需要为每个ASP应用程序建立独立的进程。当需要在一个服务器上主持十多个应用程序时,会增加很多开销。
那么,哪个选项是最好的呢?在IIS4.0中,运行进程外的应用程序会极大地影响性能。在IIS5.0中,做了许多工作,使得进程外运行ASP应用程序对性能产生的影响降到了最低。实际上,在大多数测试中,在IIS5.0中的ASP进程外应用程序,要比IIS4.0中的进程内应用程序运行得更快。无论如何,进程内(低隔离级)在两种平台上仍然产生了最好的性能。但是,如果您的命中率相对较低或最大吞吐量较低,选择低隔离级不会有太大的好处。所以,除非您需要每个Web服务器每秒处理数百或数千个页面,否则没有必要选择低隔离级。同样,应当测试多种配置并判断哪种情形最适合您。
注意:当您进程外运行ASP应用程序(中级或高级隔离)时,则在NT4上它们将运行在MTS中,而在Windows2000上它们将运行在COM+中。即,在NT4上它们运行在Mtx.exe中,而在Windows2000上它们运行在DllHost.exe中。在"任务管理器"中,您可以看见这些正在运行的进程。还可以看见IIS如何为进程外的ASP应用程序配置MTS程序包或COM+应用程序。
COM选项
COM组件也有三个配置选项,虽然与ASP选项不完全相似。COM组件可以被:"不配置"、配置为"库应用程序"或配置为"服务器应用程序"。"不配置"是指不向COM+注册组件。组件将运行在调用者的进程空间,就是说,它们是"进程中"的。"库应用程序"也是进程中的,但受惠于COM+的服务,包括安全性、事务和环境支持。"服务器应用程序"被配置为在其自己的进程空间中运行。
您可能看到,不配置的组件比库应用程序优点稍微多些。您还可能看到"库应用程序"比"服务器应用程序"有很大的性能优点。这是因为"库应用程序"与ASP运行在同一个进程中,而"服务器应用程序"则运行在自己的进程中。内部进程调用的开销要比进程内调用的开销大得多。而且,当在进程之间传递数据(如记录集)时,必须在两个进程之间复制所有的数据。
缺点!当使用"COM服务器应用程序"时,如果要在ASP和COM之间传递对象,请确保对象实现"按值汇集",即MBV。实现MBV的对象将其自身从一个进程复制到另一个进程。这比另一种方式好,在另一种方式中,对象留在创建它的进程中,而其他进程则重复调用创建使用该对象的进程。被断开连接的ADO记录集将是按值汇集的,已连接的记录集则不是。Scripting.Dictionary并不实现MBV,不会在进程之间传递。最后,要另外告诉VB程序员的是:MBV不是通过传递参数
ByVal
获得的。MBV是由原始组件创作者实现的。
怎么办?
如果您想要以性能与可靠性的合理交换来完成您的配置,我们的推荐如下:
在IIS4.0上,使用ASP的低隔离级别,并使用"MTS服务器包"。
在IIS5.0上,使用ASP的中隔离级别,并使用"COM+库应用程序"。
这些是很一般的准则;通常让公司以中或高隔离级别运行ASP,而单一目的的Web服务器可运行于低隔离级别。请权衡折中并自行决定满足需求的配置。
技巧10:显式使用选项
在.asp文件中显式使用
选项Explicit
。置于.asp文件开头的这一指令,强制开发人员声明所有要使用的变量。许多开发人员认为这有助于调试应用程序,因为它避免了错误键入变量名称而不经意地新建变量(例如,
MyXLMString=...
而非
MyXMLString=)

也许更重要的是,声明的变量比未声明的变量快。实际上,脚本运行时,在每次使用未声明变量时按照名称引用。而声明的变量,在编译或运行时分配了序号。这样,声明的变量按照该序号引用。由于
选项Explicit
强制变量声明,因此保证声明了所有变量而实现快速访问。
技巧11:在子例程和函数中使用局部变量
局部变量是在子例程和函数中声明的变量。在子例程和函数中,局部变量访问要快于全局变量访问。使用局部变量还可以使代码更加清晰,因此尽可能使用局部变量。
改进性能和样式的24个ASP技巧

www.hackbase.com阅读:时间:2005-4-78:13:54来源:www.hackbase.com

技巧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)
IfFoo.bar.blah.zaq=Foo.bar.blah.abcThen'...
在运行这段代码时,将发生下列事件:
变量
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编写该代码实现的快速方法为:
Setmyobj=Foo.bar.blah'对blah做一次解析
Myobj.baz=myobj.qaz(1)
IfMyobj.zaq=Myobj.abcThen'...
如果您使用的是VBScript5.0或更高版本,则可用
With
语句来写这段代码:
WithFoo.bar.blah
.baz=.qaz(1)
If.zaq=.abcThen'...
...
EndWith
请注意该技巧对VB编程同样有效。
技巧13:避免重新定义数组
尽量避免
Redim
数组。从关心性能的角度来说,如果计算机受物理内存的限制,最好一开始将数组的维数设置为最差方案-而不要将维数设置为最佳方案,再根据需要重新定义维数。这并不意味着明知道不需要那么多而就是应该分配太多的内存。
下面代码展示了您没有必要地使用了
Dim

Redim
来解决。
<%
DimMyArray()
RedimMyArray(2)
MyArray(0)="hello"
MyArray(1)="good-bye"
MyArray(2)="farewell"
...
'一些别的代码中,这里您不需要更多的空间,然后...
RedimPreserveMyArray(5)
MyArray(3)="morestuff"
MyArray(4)="evenmorestuff"
MyArray(5)="yetmorestuff"
%>
更好的办法是只须一开始
Dim
数组为正确的大小(本例中为5),而不是
Redim
数组,再加大数组。这可能会浪费一点儿内存(如果没有用尽所有元素),但是获得的是速度。
技巧14:使用响应缓冲
您可以通过打开"响应缓冲区"来缓冲值得输出的整个页。这将写入浏览器的数据量降为最小,从而提高总体性能。每次写入都会有大量开销(包括IIS和通过电缆发送的数据量),因此写入的越少越好。TCP/IP的工作效率,在发送少量大的数据块时明显高于发送大量小的数据块时,原因在于它的低速启动和Nagling算法(用于最小化网络阻塞)。
打开响应缓冲有两种方法。第一种,可以使用"Internet服务管理器"为整个应用程序打开响应缓冲。这是推荐的方法,在IIS4.0和IIS5.0中,在默认情况下,为新的ASP应用程序打开响应缓冲。第二种,逐页将下列代码行放在ASP页的开头,从而启用响应缓冲:
<%Response.Buffer=True%>
该行代码必须在任何响应数据写入浏览器之前执行(也就是说,在任何HTML出现在ASP脚本中之前和任何Cookies被使用
Response.Cookies
集合设置之前)。通常,最好是为整个应用程序打开响应缓冲。这允许省略上面每页中的代码行。
Response.Flush
响应缓冲的通病是用户感觉ASP页响应迟钝(尽管总体响应时间改善了),因为他们需要等到整个页生成后才能看见该页。对于长时间运行的页面,可以通过设置
Response.Buffer=False
关闭响应缓冲。但是,更好的策略是使用
Response.Flush
方法。该方法刷新由ASP绘入浏览器的所有HTML。例如,绘制了具有1,000行的表的100行后,ASP可以调用
Response.Flush
强制将结果绘制到浏览器;这允许用户在其余的行准备好之前先看到头100行。该技术给了您两个举世无双的好东西-响应缓冲与浏览器中数据的逐步显示的组合。
(注意,在上面1,000行表的示例中,许多浏览器,在看到</table>结束标记之前不会开始绘制表。请检查目标浏览器的支持性。要解决该问题,请将表分割为具有较少行的多个表,然后在每个表后面调用
Response.Flush
。新版本的InternetExplorer将在表完全下载之前绘制表,特别是如果指定表的列宽则绘制速度更快;这避免强制InternetExplorer通过度量每个单元格的内容来计算列宽。)
响应缓冲的另一个通病是在生成大型页时将使用服务器的大量内存。对于该问题,除了要求生成大型页的技巧外,还可以通过巧妙地使用
Response.Flush
来解决。
技巧15:批处理内嵌脚本和Response.Write语句
VBScript语法
<%=expression%>
将"
表达式
"的值写入ASP输出流。如果响应缓冲没有打开,则这些语句的每一句都会导致通过网络,以许多小型包的形式,向浏览器写入数据。这是非常慢的。另外,解释少量脚本和HTML,将导致在脚本引擎和HTML之间切换,也降低了性能。因此,请使用下面技巧:用对
Response.Write
的一个调用,替换内嵌的密集组合表达式。例如,在下面范例中,每行每字段有一个对响应流的写入,每行都有许多VBScript和HTML之间的切换:
<table>
<%ForEachfldinrs.Fields%>
<th><%=fld.Name%></th>
<%
Next
WhileNotrs.EOF
%>
<tr>
<%ForEachfldinrs.Fields%>
<td><%=fld.Value%></td>
<%Next
</tr>
<%rs.MoveNext
Wend%>
</table>
下面是更有效的代码,每行中有一个对响应流的写入。所有代码均包含在一个VBScript块内:
<table>
<%
Foreachfldinrs.Fields
Response.Write("<th>"&fld.Name&"</th>"&vbCrLf)
Next
WhileNotrs.EOF
Response.Write("<tr>")
ForEachfldinrs.Fields%>
Response.Write("<td>"&fld.Value&"</td>"&vbCrLf)
Next
Response.Write"</tr>"
Wend
%>
</table>
当响应缓冲被禁用时,本技巧的作用更大。最好启用响应缓冲,然后观察批处理
Response.Write
是否对性能有帮助。
(在这一特例中,构建表的主体的嵌套循环(
WhileNotrs.EOF...
)可以被精心构造的、对GetString的调用所替代。)
技巧16:在开始长时间的任务之前先使用Response.IsClientConnected
如果用户失去耐心,他们可以在开始执行他们的请求之前放弃ASP页。如果他们单击了Refresh或跳转到服务器的其他页上,在ASP请求队列的末尾将有一个新的请求,而在队列的中间有一个断开连接的请求。这通常发生在服务器处于高负荷的情况下(它有一个很长的请求队列,相应的响应时间也很长),这只能使情况更糟。如果用户不再连接,将没有执行ASP页的点(特别是低速、重量级的ASP页)。可以使用
Response.IsClientConnected
属性检查这种情况。如果它返回
False
,则应调用
Response.End
并放弃该页的剩余内容。实际上,每当ASP要执行新的请求时,IIS5.0便将该方法编码,来检查队列中的请求有多长。如果在那里超过了3秒钟,ASP会检查客户是否仍然连接着,如果客户已断开连接,就立即结束该请求。您可以使用metabase中的
AspQueueConnectionTestTime
设置,调整这3秒的超时时间。
如果有某页执行了很长时间,您可能还想按一定的时间间隔检查
Response.IsClientConnected
。在启用响应缓冲之后,按一定的时间间隔执行
Response.Flush
,告诉用户正在进行的是哪些事情,是个好办法。
注意在IIS4.0中,
Response.IsClientConnected
将不能正常工作,除非首先执行
Response.Write
。如果启用了缓冲,也需要执行
Response.Flush
。在IIS5.0中则不必如此-
Response.IsClientConnected
工作得很好。在任何情况下,
Response.IsClientConnected
都要有些开销,所以,只有在执行至少要用500毫秒(如果想维持每秒几十页的吞吐量,这是一个很长的时间了)的操作前才使用它。作为通常的规则,不要在紧密循环的每次迭代中调用它,例如当绘制表中的行,可能每20行或每50行调用一次。
技巧17:使用<OBJECT>标记实例化对象
如果需要引用不能在所有代码路径中使用的对象(尤其是服务器-或应用程序-作用域的对象),则使用Global.asa中的
<objectrunat=serverid=objname>
标记来声明它们,而不是使用
Server.createObject
方法。
Server.createObject
立刻创建对象。如果以后不使用那个对象,就不要浪费资源。
<objectid=objname>
标记声明了objname,但实际上objname此时并没有创建,直到它的方法或属性第一次被使用时才创建。
这是迟缓计算的另一个例子。
技巧18:使用ADO对象和其他组件的TypeLib声明
当使用ADO时,开发人员经常包含
adovbs.txt
来获得对ADO不同常量的访问权。该文件必须包含在要使用这些常量的每一页中。该常量文件非常大,给每个ASP页增加了很多编译时间和脚本大小方面的开销。
IIS5.0提供了绑定到组件类型库的能力。允许您在每个ASP页上引用一次类型库并使用它。每页不需要为编译常量文件付出代价,并且组件开发人员不必为在ASP中的使用而生成VBScript#include文件。
要访问ADO类型库,请将下列语句之一放入Global.asa中。
<!--METADATANAME="MicrosoftActiveXDataObjects2.5Library"
TYPE="TypeLib"UUID=""-->
或者
<!--METADATATYPE="TypeLib"
FILE="C:/ProgramFiles/CommonFiles/system/ado/msado15.dll"-->
技巧19:利用浏览器的验证能力
流行的浏览器具有对以下功能的高级支持,例如XML、DHTML、Java小程序以及远程数据服务。请尽量利用这些功能。所有这些技术,都可以通过执行客户端的验证和数据缓存,减少了与Web服务器之间的往返。如果您正在运行智能浏览器,该浏览器可以为您进行一些验证(例如,在运行POST之前检查信用卡的校验和否有效)。重申一次,请尽量使用这些功能。由于削减了客户端到服务器的往返路程,将减少对Web服务器的压力,并且削减了网络通信量(虽然发送给浏览器的初始页面可能更大),服务器访问的所有后端资源也削减了。而且用户不必经常提取新页,使用户的感受好一些。这并不减轻对服务器端验证的需要。还是应该经常进行服务器端的验证。这样能够防止由于某些原因从客户端来的坏数据,例如黑客,或者不运行客户端验证程序的浏览器。
许多站点由独立于浏览器创建的HTML组成。这一点经常阻碍开发人员利用可以提高性能的流行浏览器功能。对于真正高性能的、必须关心浏览器的站点,良好的策略是针对流行的浏览器优化您的页面。在ASP中使用"浏览器性能组件",很容易检测到浏览器的功能。诸如MicrosoftFrontPage等工具,能帮助您设计使用所希望的目标浏览器和HTML版本的代码。更详细的讨论,请查看WhenisBetterWorse?WeighingtheTechnologyTrade-Offs(英文)。
技巧20:在循环中避免字符串串联
许多人在循环中创建类似这样的字符串:
s="<table>"&vbCrLf
ForEachfldinrs.Fields
s=s&"<th>"&fld.Name&"</th>"
Next
WhileNotrs.EOF
s=s&vbCrLf&"<tr>"
ForEachfldinrs.Fields
s=s&"<td>"&fld.Value&"</td>"
Next
s=s&"</tr>"
rs.MoveNext
Wend
s=s&vbCrLf&"</table>"&vbCrLf
Response.Writes
这种方法有几个问题。首先,重复连接字符串所花费的时间,以二次方曲线的速率增长;粗略地计算,运行循环所花费的时间,与记录数乘以字段数的平方成正比。举一个简单的例子,便能清楚地说明这一点。
s=""
Fori=Asc("A")toAsc("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分钟更新一次。
列出新闻的主页或新闻发布的主页,每天更新2次。
公共基金运营列表,基本的统计数小时更新1次。
请注意,使用浏览器或代理缓存,只有很少的命中被记录到Web服务器上。如果想精确测量所有页面查看或者张贴广告,也许不喜欢使用浏览器和代理缓存。
浏览器缓存是由Web服务器发往浏览器的HTTP截至期限标题控制的。ASP提供了两种发送标题的机制。要将页面设置为在未来某个分钟数后过期,请设置
Response.Expires
属性。以下的例子通知浏览器:内容在10分钟后过期:
<%Response.Expires=10%>
设置
Response.Expires
为负数或0则禁用缓存。一定要使用较大的负数,例如-1000(大于一天),来克服服务器时钟和浏览器时钟之间的差异。第二个属性
Response.ExpiresAbsolute
,允许设置内容过期的指定时间:
<%Response.ExpiresAbsolute=#May31,200113:30:15#%>
如果不想使用Response对象设置过期时间,可以将<META>标记写入HTML,通常写在HTML文件的<HEAD>内部。一些浏览器会响应这条指令,但代理不会。
<METAHTTP-EQUIV="Expires"VALUE="May31,200113:30:15">
最后,可以标识内容对HTTP代理缓存是否有效,请使用
Response.CacheControl
属性。设置属性为"Public",允许代理缓存内容。
<%Response.CacheControl="Public"%>
默认情况下,该属性设置为"Private"。注意,不应当为显示某用户专用数据的页启用代理缓存,因为代理也许为属于其他用户的用户页面服务。
技巧22:尽可能使用Server.Transfer替代Response.Redirect
Response.Redirect
通知浏览器,请求一个不同的页面。该函数经常用于重定向用户到登录或错误页面。既然重定向强制一个新页请求,浏览器就必须做两次到Web服务器的往返,而且Web服务器必须处理额外的请求。IIS5.0引入一个新的函数,
Server.Transfer
,该函数执行传送到相同服务器上的不同ASP页。这样避免了额外的、从浏览器到Web服务器的往返,从而改善了整体系统性能,同时改善了对用户的响应时间。请查看重定向中的新方向(英文),它讨论了
Server.Transfer

Server.Execute

也可以查看LeveragingASPinIIS5.0中有关IIS5.0和ASP3.0新功能的完全列表。(英文)
技巧23:在目录URL尾部加斜线
相关的技巧是,一定要定在指向目录的URL尾部加斜线
(/)
。如果省略了斜线,浏览器将向服务器提出请求,仅通知它正寻找一个目录。然后浏览器发出第二个请求,在URL末尾添加斜线,然后服务器将那个目录的默认文档作为响应,或者如果没有默认文档并且目录浏览已被启用,就以目录列表作为响应。添加了斜线便省去了第一个没用的往返。出于对用户的友好,也许想要在显示的名称的末尾省略斜线。
例如,写:
<ahref="http://msdn.microsoft.com/workshop/"title="MSDNWeb
Workshop">"target=_blank>http://msdn.microsoft.com/workshop</a>
它还适用于指向在Web站点主页的URL:请使用下面的:<ahref=",不要用'target=_blank>http://msdn.microsoft.com/">,不要用<ahref="./'target=_blank>http://msdn.microsoft.com">.

技巧24:避免使用服务器变量
访问服务器变量将引起Web站点向服务器提出特殊的请求,然后收集所有的服务器变量,并不止是需要的那个。这好像从发霉的阁楼中的文件夹中检索某条特殊的信息一样。当想要某条信息时,在访问该信息之前必须先上阁楼取得文件夹。这与请求服务器变量时,性能访问出现第一次请求服务器变量所发生的一样。后续的对其他服务器变量的访问不会引起性能访问。
从不访问不合格的Request对象(例如,
Request("Data")
)。对于不在
Request.Cookies

Request.Form

Request.QueryString

Request.ClientCertificate
中的项,有对
Request.ServerVariables
的隐含调用。
Request.ServerVariables
集合比其他集合慢很多。

抱歉!评论已关闭.