你的位置:首页 > 软件开发 > ASP.net > 全面优化ASP应用程序的性能

全面优化ASP应用程序的性能

发布时间:2008-07-12 21:43:00
ASP 本身并不是一种脚本语言,它只是提供了一种使镶嵌在 HTML 页面中的脚本程序得以运行的环境,而在ASP中最常用的脚本语言就是VBScript了。虽然ASP的脚本语言很简单,但是要想让一个ASP程序能够最优化的运行也不是一件简单的事情。   现在国内的网络带宽很有限,网络十分拥挤,如何使得自己的ASP应用程序能够快速的运行就成为了每一个ASP程序员的梦想了。那就跟随我来一同加速你的ASP程序
  get='_blank'>ASP 本身并不是一种脚本语言,它只是提供了一种使镶嵌在 HTML 页面中的脚本程序得以运行的环境,而在ASP中最常用的脚本语言就是VBScript了。虽然ASP的脚本语言很简单,但是要想让一个ASP程序能够最优化的运行也不是一件简单的事情。

  现在国内的网络带宽很有限,网络十分拥挤,如何使得自己的ASP应用程序能够快速的运行就成为了每一个ASP程序员的梦想了。那就跟随我来一同加速你的ASP程序吧!

一. 有关操作数据库的优化方法

  我们使用ASP最主要的用途就是对数据库进行操作了,如何更快速的完成这些动作呢?

1. 不要任意使用“SELECT  *  ......”

  请尽量拾取你所需要的那些字段,比如,一个Table中有10个字段,但是你只会用到其中的一个字段(name),就要使用“select name from yourtable”,而不是用“select * from yourtable”。你或许会说,我是这么做的阿,但是,如果一个table中有50个字段,你需要用到其中的23个字段的时候,你会怎么做呢?为了节省打字以及查找对应字段名称的麻烦,你就不一定会老老实实的用“select name,sex,age... from yourtable”了吧!

  实际证明,尽量拾取你所需要的那些字段来使用select语句将会是你的ASP程序至少加快5%左右。

2. 尽可能使用系统存储过程(针对MS SQL Server)

  有的时候完成一个读取操作,使用SQL语句和存储过程同样可以完成,但是使用存储过程将会大大加快完成读取操作的速度,也就提高了你的ASP程序运行的速度。

3. 注意你的游标使用方法

  如果你仅仅是对一个table进行读取操作,那么请你使用forward-only,read-only游标,因为这种游标读取数据库是最为快速的,尤其是你的读取数据量很大的情况下。

4. 不要打开无用的独立记录集

  也许你在笑了,我会打开没有用的记录集吗?是的,你当然会,比如在生成一个树型记录集的时候,你不得不打开父记录集以及对应的子记录集,甚至还有孙记录集,其实你可以使用ADO提供的Data Shaping技术来替代打开多个独立的记录集,那样会加快程序的运行速度。(关于Data Shaping的用法可以参考ADO帮助)

5. 一定要记着关闭打开的记录集对象以及连接(Connection)对象

  有些朋友总是奇怪为何自己的ASP程序刚开始的时候运行速度很快,可是多运行几遍就越来越慢了呢?甚至出现服务器死机的情况。发生这种情况,就很可能是你打开了太多的记录集对象以及连接(Connection)对象而最后却没有关闭他们引起的。使用如下方法进行关闭:

YourRecordSet.close

Set YourRecordSet=Nothing

Set YourConnection=Nothing

6. 取得数据库数据的方法比较

  你是如何得到记录集的数据的呢?是不是用YourRecordSet(字段编号),还是
YourRecordSet("字段名称")?其实还有其他的使用方法,现在我们就比较一下吧(100条记录):

Rs("字段名称")

Rs("字段名").Value

Rs("字段编号")

Set方法

数据库回应时间

2.967秒

2.936秒

1.650秒

0.586秒

2.824秒

2.914秒

1.611秒

0.602秒

2.893秒

2.943秒

1.613秒

0.594秒

平均回应时间

2.895秒

2.931秒

1.625秒

0.594秒

  这下知道了吧,前三种方法大家都可以看的明白,我就说说第四种方法(Set方法)如何使用:

Dim strSQL

StrSQL="select name,sex,age from yourtable"

Dim rs

Set rs=server.createobject("ADODB.RECORDSET")

Rs.open strSQL,conn,1,1

Const fieldsOrder=2

Dim objOrder

Set objOrder=rs(fieldsOrder)

Response.write objOrder  'Set方法

二. 有关ASP内置对象使用方面的优化方法

1. 尽量减少Session对象和Application对象的使用

  虽然ASP中提供的这两个对象对我们的编程提供了很大的帮助,但是,对这两个对象要合理的使用,不要滥用。因为大量的使用这两个对象将会极大的增大服务器的负担,严重消耗系统资源。也将使你的ASP程序运行慢如老牛。

2. 要及时的关闭不再使用的对象(尤其是Session和Application)

  不及时的关闭你所使用的对象会导致系统运行速度变得缓慢。也许你会问,
Session和Application不是可以自动消失的吗?完全正确,系统默认30分钟内用户如果没有任何操作就自动触发Session_OnEnd和Application_OnEnd事件,但是大量的用户频繁的读取服务器,服务器将在很长的一段时间保持那些已经没有用的
Session,Application对象,如果不及时关闭使用完的Session和Application后果将不堪想象了。

关闭的方法是:

   Set 对象=Nothing

三. 合理使用Include文件

  我们这里说的是指用<!--#include file="xxx"-->形式包含进来的文件,并且文件内容全是ASP程序,也就是说你将一些公共的函数放到一个文件中,并在其他有可能调用其中函数的页面包含进来。

建议你不要将所有的函数放到一个包含文件中,因为你在其他页面包含这个文件的时候,服务器端是要进行预编译的,很可能在一个包含文件里面有上百个函数,而你只是想要使用其中的一个函数,这样就得不偿失了。所以,尽可能的分割你的包含文件成为多个小的包含文件。这样也可以提高程序的运行速度哦。

四. 有关VBScript语言方面的优化方法

1.尽量使用系统函数代替自己编写的函数

   比如要想分割一个有规律的字符串("sss,ddd,fff,ggg"),就不必自己使用什么Mid(),Instr等等函数来分析了,其实VBScript就提供了一个函数 Split(),这样既省时间,又提高了速度,何乐而不为呢?

2.减少动态数组的使用

3.尽可能的养成提前声明变量的习惯

   不要小看了这一条,提前声明变量会加快程序的解释执行时间。相反,从不声明变量,不但程序难以阅读,整个程序在服务器的执行效率也会大打折扣的。

五. 其他方面的优化方法

1. 在ASP文件中尽量使用<%%>嵌入到HTML标签中,而不要使用Response.write的方式,比如:

<html>

<body>

<%If ok =1 then %>

  Hello! World!

<%End If%>

</body>

</html>

就远远比:

<%

Response.write "<html>"

Response.write " <body>"

            If ok =1 then

  Response.write "Hello! World!"

            End If

Response.write " </body>"

Response.write "</html>"

%>

的运行速度要快,尤其是你的ASP文件比较大的情况下。因为,第二种方式增加了服务器端的解释时间,因而也就降低了ASP程序的性能。

2. 尽量用一个ASP文件完成一个动作

  很多人喜欢在一个ASP程序中同时完成诸如添加,删除,查找等等多个动作,不要认为这样是有效的利用了文件,相反,这样做的结果是使得应用程序的运行速度减慢很多。

应当将添加,删除,查找等分割成单个的独立的ASP文件来完成。这样使得文件不会过于庞大,降低服务器端解释执行的负担,并且阅读程序也很快捷。

  好了,经过我们的全面优化,你的ASP应用程序是不是运行的更加稳定,更加快速了呢?


22个提高ASP性能的技巧(1-12)


 

介绍
对于ASP程序来说,性能非常重要;必须在最初设计程序时就要考虑到性能,否则以后恐怕就要因为性能问题而重写代码。怎样才能使ASP程序性能达到最大呢?本文就将介绍一些提高ASP程序性能的技巧。

技巧1:将常用数据在Web服务器端缓存起来
大部分的ASP页面都要从后台数据库中提取数据,然后将数据用HTML方式表现出来。
不管你的数据库多么快,从内存中提取数据总比从后台数据库中提取快;从本地硬盘中读取数据通常也比从数据库中快。因此,你可以通过在Web服务器端缓存数据来提高性能。

缓存是个典型的以空间换取时间的交易。如果你正确的缓存了数据,性能可能会突飞猛进。要想一个缓存能真正发挥效益,必须缓冲那些常用和计算复杂的数据。装满过期数据的缓冲区只能浪费内存。

不经常变化的数据也是缓存的一个良好候选者,因为你可以不用关心同数据库中的数据保持同步。下拉列表框、引用表、小段DHTML代码,

技巧2:用Application对象或Session对象缓存常用数据
ASP的Application和Session对象是一个极其方便的在内存中缓存数据的容器。你可以把数据放到Application或Session对象中,这些数据就会在整个HTTP调用中一直存在。每个用户有自己的Session对象中的数据,而Application对象中的数据可以在所有用户**享。

应该在什么时候将数据装入Application或Session中呢?通常,数据在Application或Session启动的时候装入。要想在Application或Session启动的时候装入数据,需要分别在Global.asa的Application_OnStart()或Session_OnStart()中添加适当
的代码;如果Global.asa中没有这两个函数,你可以手工添加。也可以在数据第一次使用的时候将其装入。要想这样,应该在ASP页面中写一些代码(或是写一个可重用的脚本函数)来检查数据是否存在并且如果数据不存在则将其装入内存。下面是一个经典的性能调整技术--Lazy Evaluation:

<%
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
%>

Similar functions could be written for each chunk of data needed.

In what format should the data be stored? Any variant type can be
stored, since all script variables are variants. For instance, you
can store strings, integers, or arrays. Often, you’ll be storing the
contents of an ADO recordset in one of these variable types. To get
data out of an ADO recordset, you can manually copy the data into
VBScript variables, one field at a time. It’s faster and easier to
use one of the ADO recordset persistence functions GetRows(),GetString
() or Save() (ADO 2.5). Full details are beyond the scope of this
article, but here’s a function that demonstrates using GetRows() to
return an array of recordset data:

' 获取记录集,返回数组
Function FetchEmploymentStatusList
  Dim rs
  Set rs = CreateObject("ADODB.Recordset")
  rs.Open "select StatusName, StatusID from EmployeeStatus", _
          "dsn=employees;uid=sa;pwd=;"
  FetchEmploymentStatusList = rs.GetRows() ' 将记录集用数组返回
  rs.Close
  Set rs = Nothing
End Function

A further refinement of the above might be to cache the HTML for the
list, rather than the array. Here’s a simple sample:

' 获取记录集,返回HTML Option列表
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 字段绑定
  Do Until rs.EOF
    s = s & " <option>" & fldName & "</option>" & vbCrLf
    rs.MoveNext
  Loop
  s = s & "</select>" & vbCrLf
  rs.Close
  Set rs = Nothing ' 释放rs
  FetchEmploymentStatusList = s ' 用字符串方式返回数据
End Function

在正确情况下,你可以将ADO记录集本身缓存在Application或Session范围,但必须满足下面两个条件: .ADO必须被标记为自由线程模型(Free-threaded) .必须使用无连接记录集

如果不能满足上面两个条件,一定不要缓存记录集。在下面的“不灵活的组件”和“不要缓存Connection”两个技巧中,我们将讨论在Application和Session中保存COM对象的危险性。

当你在Application或Session中存储数据后,数据将一直保存,知道你的程序改变它,或是Session过期,或是Web服务重新启动。What if the data needs to be updated?手工刷新Application数据,可以调用只有管理员才可访问的用来刷新数据的ASP页面;或者定期的通过一个函数来周期性的更新数据。下面的例子在缓存数据中保存了一个时间戳,然后一段时间之后自动刷新数据。

<%
Const UPDATE_INTERVAL = 300 ' 刷新间隔,单位是秒

'返回雇员状态列表
Function GetEmploymentStatusList
  UpdateEmploymentStatus
  GetEmploymentStatusList = Application("EmploymentStatusList")
End Function

'周期性的更新缓存中的数据
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()

      ' 更新Application对象时用Application.Lock()来保持数据一致性
      Application.Lock
      Application("EmploymentStatusList") = Events
      Application("LastUpdate") = CStr(Now)
      Application.Unlock
  End If
End Sub

要知道在Session或Application中缓存大数组并不是一个太好的方法。在访问数组中的任何元素之前,脚本解释器都需要生成一个临时的整个数组的副本。例如,如果你缓存了一个100,000个字符串元素的数组,用来将邮政编码和当地的天气对应一一起来,在访问数组中任何一个字符串之前,ASP解释器首先必须复制所有的100,000个天气情况数据到一个临时数组中。在这种情况下,开发一个组件来储存天气情况数据或是使用词典(Dictioary)对象更为合适一点。不过,也不要因小失大,数组对象的的查找速度更快。索引一个词典比索引一个数组慢。你可以因你的情况而宜,选择合适的数据结构。

技巧3:在硬盘上缓存数据和HTML页面
有时,可能有太多的数据缓存在内存中。“太多”是个模糊的说法,它取决与Web服务器的内存大小、缓存项的数目和这些缓存项被访问的频度。无论如何,如果太多的数据在内存中缓存,可以考虑将数据用文本或

注意,当我们测量单一ASP页面的性能时,从硬盘上读取数据可能比从数据库中读取慢。但是,缓存能够减少数据库和网络的负载。在高负载的情况下,这将大大提高总体吞吐量。当被缓存的数据是非常复杂的查询,比如多表连接或是一个复杂的查询过程或一个非常大的记录集,缓存的效果将非常明显。

ASP和COM提供了一些工具来建立基于硬盘的缓存方案。ADO Recordset对象的Save和Open方法可以保存和装入到磁盘上。还有一些用来访问文件的组
件: .Scripting.FileSystemObject允许你创建、读取和写入文件。 .MS

最后,将数据表现缓存在硬盘上,比缓存数据本身要好。生成的HTML可以一个.htm或.asp文件保存在硬盘上;超连可以直接指向那些文件。你也可以用一些商业工具,如XBuilder和SQL Server互连网发布特性,来生成和处理HTML文件。另外,也可以用#include将HTML片段包含到ASP文件中;还可以用FileSystemObject来读取HTML文件。

技巧4:避免在Application或Session对象中缓存COM对象
虽然在Application或Session对象中缓存数据是一个好注意,但缓存COM对象可能带来严重的后果。在Application或Session对象中缓存常用COM对象非常诱人,但非常不幸,很多COM对象,包括那些用VB 6.0或早期版本写的组件,如果被缓存到
Application或Session对象中将会导致严重的性能瓶颈。

特别地,所有非Agile的组件被缓存到Session或Application中时,都将产生性能瓶颈。Agile组件是指聚合了Free-threaded marshaler(FTM)并且线程模型是Both
(ThreadingModel=Both),或线程模型是Neutral(Netural新出现在Windows 2000
和COM+中)的组件。下面的组件都是非Agile的:


自由线程模型组件(除非他们聚合了FTM)
Apartment线程模型组件
单线程组件

Configured组件(MTS/COM+库和服务包/应用)是非Agile的,除非它们是Neutral线程模型的。Apartment线程模型组件和其他非Agile组件最好是工作在页面范围内(就是说,他们在一个单一ASP页面中创建和销毁)。

IIS 4.0中,线程模型是Both的组件被看作是Agile的,但在IIS 5.0中,他们不再满足Agile的条件。组件线程模型必须是Both的,并且聚合了FTM,才被看作Agile的。如果试图将一个用Server.CreateObject创建的非Agile组件存储到
Application对象中时,IIS 5.0将会抛出一个错误。

当ADO组件被标记为自由线程模型时,ADO记录集对象可以安全地存储。可以用
Makfre15.bat,一般是放在\\Program Files\Common\System\ADO这个文件夹里,将ADO组件标记为自由线程模型。有一点要注意:当用Access作后台数据库时,ADO不能被标记为自由线程模型。词典(Dictionary)组件也是Agile对象。

技巧5:不要缓存数据库连接
缓存ADO Connection对象是一个不好的策略。如果一个Connection对象被存储在
Application对象中并被所有页面使用,所有页面就会争着使用这个连接。如果
Connection对象被存储在Session对象中,就要为每个用户创建一个数据库连接,这就消减了连接池的作用,并且增大了Web服务器和数据库服务器的压力。可以用在每个使用ADO的ASP页创建和释放ADO对象来替代缓存数据库连接;因为IIS内建了数据库连接池,所以这种方法非常有效。

既然有连接的记录集保存了一个数据库连接的引用,因此也不应该在Application或Session对象中保存有连接的记录集。但是,你可以安全的缓存无连接的记录集,因为它并不包含到数据连接的引用。要想挂断一个记录集,可以采取如下两个步骤:

    Set rs = Server.CreateObject("ADODB.RecordSet")
    rs.CursorLocation = adUseClient  ' 第一步

    rs.Open strQuery, strProv

    ' 将记录集同数据提供者和数据源挂断
    rs.ActiveConnection = Nothing    '第二步

技巧6:正确地使用Session对象
我们已经提到了在Application和Session中缓存数据的好处,下面我们将说一些
Session对象的缺点。在繁忙的站点上使用Session有一些不利的地方。繁忙是指这个站点每秒钟要处理数以百计的页面请求或同时连接数以千计的并发用户。这个技巧对那些必须要水平伸缩的站点--就是说,这些站点用多个服务器来实现负载平衡或容错--非常重要。对小的站点,如公司内网,Session相对与他消耗的资源来说,还是值得一用的。

ASP自动为每个访问Web服务器的拥护创建一个Session对象。每个Session大约消耗10K的资源,并使所有的请求都慢了一点。这个Session在超时周期内一直存在,这个周期一般是20分钟。对于Session来说最大的问题不是性能而是伸缩能力。Session不是跨Web服务器的;一旦一个Session在某个服务器上创建,它的数据都保存在那儿。
这意味着如果你要在多个Web服务器环境中使用Session,你必须设计一套能使用户总是访问它的Session对象所在的Web服务器的策略;即将一个用户粘到一个Web服务器上。如果Web服务器崩溃,因为Session不是永久保存在磁盘上的饿,所以全部“粘”
在其上的用户的Session状态都将丢失。实现“粘Session(sticky session)”的策略包括硬件和软件方案,如Windows 2000 Advanced Server中的Network Load
Balancing和Cisco的Local Director。当然,这些方案并不完美,都要损失一些可伸缩性。Application对象也不是跨服务器的,如果你想在多服务器间共享和更新Application数据,你必须使用一个后台数据库。但无论如何,只读Application数据在多服务器环境中还是十分有用的。

绝大多数任务优先(mission-critical)的站点都想在至少两台Web服务器上发布--如果没有比延长正常运行时间更重要的理由的话。因此,在设计阶段,你就要实现“粘Session”,或是简单地避免Session和其他将用户状态保存在一个独立Web服务器上的状态管理技术。

如果不使用Session,就将它们关闭;可以通过Internet Service Manager(参看ISM文档)关闭你的应用的Session功能。如果决定使用Session,就要用一些方法将他们对性能的影响减到最小。可以将不需要Session的内容(如帮助窗口等)移到一个的关闭了Session的ASP应用中。如果某个单一页面不需要Session,可以将下面的语句放在页面的顶部来禁止Session功能:

<% @EnableSessionState=False %>

使用该语句还有一个原因是Session在帧中会产生一个有趣的问题。ASP保证任何时候一个会话只有一个请求,这就导致如果浏览器同时请求多个页面,同一时刻将只有一个ASP请求能够访问Session;这避免了访问Session对象时产生的多线程问题;但很不幸,一个帧中的多个页面只能顺序的生成,一个接着一个,而不是兵法。用户可能会为多个帧等待较长时间。所以如果帧中的某个页面没有使用Session,就在页面顶部放置<% @EnableSessionState=False %>语句。


作为使用Session对象的替代,还有很多其他的方法来管理会话状态。对小规模的状态(小于4KB),推荐使用Cookies,QueryString变量和隐藏表单变量。对大量数据,如购物信息,一个后台数据库可能是很好的选择。

技巧7:将代码封装到COM对象中
如果有很多VBScript或JScript代码,可以通过将代码封装到COM对象中来提高性能。
编译过的代码通常比解释代码运行得快。COM对象可以通过“前期绑定”来访问其他COM对象,这比脚本使用的“后期绑定”更高效。

下面是将代码码封装到COM对象中的优点(不仅仅是性能):


COM对象可以很好地将商业逻辑同表现逻辑分离
COM对象使代码可重用
用VB,C或VJ写的代码比ASP代码更易调试

COM对象也有不足,包括开发周期长和需要不同的编程经验等。有一点尤需注意,封装少量ASP代码可能在性能上适得其反;这种情况下,创建和调用COM对象的代价超过了编译代码性能上的好处。如何组合ASP代码和COM组件代码来产生最佳性能,往往是个令人头疼的问题。注意,同Windows NT 4.0/IIS 4.0相比,Windows 2000/IIS 5.0在脚本和ADO性能上已经大大提高。

技巧8:对资源晚获取,早释放
通常情况下,晚获取和早释放资源是最好的。这不仅适用于COM对象,也适用于文件句柄和其他资源。ADO连接和记录集是这项优化策略的主要对象。当使用完一个
Recordset对象,应该立即将它释放,而不应等到页面结束。将VBScript变量设成Nothing是最好的方法。同时,释放相关的Command和Connection对象(别忘了在将Connection对象设成Nothing之前调用Close()方法)。

技巧9:进程外执行以性能换取可靠性
ASP和MTS/COM+都有选项让你来用可靠性换取性能。当建立和发布你的应用时,你应该理解这项交易的内幕。

ASP选项
ASP应用有三种运行方法可选择。在IIS 5.0中,引入“分离级别(isolation
level)”这个术语来描述这些选项。三种分离级别分别是:低(Low),中
(Medium)和高(High)。

低分离级 这种级别被所有版本的IIS支持,并且速度也是最快的。它在
Inetinfo.exe--主要的IIS进程--中运行ASP。如果ASP应用崩溃,IIS也将崩溃。
(在IIS 4.0中,网管必须用诸如InetMon之类的工具来监视IIS,一旦IIS停止,运行批处理文件。IIS 5.0引入了“可靠的重启(reliable restart)”,会自动重新启动失败的服务器。
中分离级 从IIS 5.0开始引入的新级别,指进程外运行,即ASP运行在IIS进程之外。
在中分离级中,所有的ASP应用共享一个进程空间。把多个进程外应用在同个空间中运行,减少了进程的树木。中分离级是IIS 5.0的默认级别。
高分离级 IIS 4.0和IIS 5.0都支持。高分离级也是进程外的。如果ASP崩溃了,Web服务器并不崩溃。ASP应用会在下个ASP请求到达的时候自动重启。每个被配置为高分离级的ASP应用有自己的进程空间;这将每个ASP应用保护起来。它的缺点是对每个ASP应用需要一个分离的进程;这增加了许多资源消耗。
哪种选项是最好的。在IIS 4.0中,进程外运行将使性能急剧下降;在IIS 5.0中,许多改进使进程外ASP应用的代价降到最低。事实上,在许多测试中,IIS 5.0中的ASP进程外应用比II4 4.0中的进程内运行都快。但无论如何,在任何平台上,还是进程内(低分离级)运行能带来最佳的性能。然而,在相对低点击率或低最大吞吐量的情况下,低分离级不会带来任何益处;因此,除非每个Web服务器需要应付成千上百的页面请求,不然你不会需要用低分离级。通常,需要在多个配置下进行测试,才能决定使用哪种配置。
注意:当在进程外运行ASP应用时(中或低分离级),ASP应用运行在NT 4上的MTS中或Windows 2000的COM+中;就是说,在NT4中,ASP应用运行在Mtx.exe中;在
Windows 2000中,ASP应用运行在DllHost.exe中。你可以在任务管理器中看到这些进程在运行。

COM选项
COM组件也有三种配置选项,但不完全对应于ASP的选项。COM组件可以是“无配置的(Unconfigured)”、作为一个库应用(Library Application)或是作为一个服务应用(Server Application)。“无配置的”意味着组件不注册到COM+中,组件将在调用者进程空间中运行;即“进程内”。库应用也是进程内的,但可以从COM+的服务,如安全、事务和上下文支持,中获益。服务应用则被配置成运行在自己的进程空间内。

“无配置”比库应用有一点优越性;而库应用比服务应用在性能上更优越。这是因为库应用和ASP是在同一个进程内的,而服务应用是运行在自己的进程空间里的。进程间调用比进程内调用的代价高。同样,在进程间传递如记录集这样的数据,需要在两个进程间复制所有的数据。

缺陷!当使用COM服务应用时,要想在ASP和COM间传送数据,必须保证对象实现了“按值排列(marshall-by-valu)”,或者说MBV。实现了MBV的对象将自身从一个进程复制到另一个进程。这比下面的方法好:对象留在创建者进程,其他进程重复调用创建进程来使用对象。无连接ADO记录集是MBV,有连接记录集就不是。
Scripting.Dictonary对象没有实现MBV,不能在进程之间传递。最后,对VB程序员的一个提示:MBV不是通过用ByVal来传递参数。MBV是原始组件作者实现的。

怎样做?
推荐的用可靠性换取性能的配置:

在IIS 4.0上,用ASP的低分离级,并使用MTS服务包。
在IIS 5.0SHANG,用ASP的中分离级,使用COM+的库应用。
技巧10:使用Option Explicit
在.asp文件中使用Option Explicit。该指示放在.asp文件的顶部,强制开发者在使用任何变量之前必须定义它。许多程序员认为这有助于调试程序,因为它消除了打字错误的可能(如将My

另外一点可能更加重要:已定义变量比未定义的变量快。ASP每次是用名字来引用未定义变量的;而另一方面,每个已定义变量有一个序号,ASP用这个序号来引用已定义变量。既然Option Explicit强制变量定义,就保证了所有的变量都是已定义的,访问速度就更快了。

技巧11:在子过程和函数中使用本地变量
本地变量是那些在子过程和函数中定义的变量。在函数和子过程中,访问本地变量比访问全局变量更快。使用本地变量也使代码更干净,因此尽量使用本地变量吧。

技巧12:将常用数据复制到脚本变量中
当访问ASP中的COM对象时,应该将常用对象数据复制到脚本变量中。着将减少COM方法调用。而COM方法调用代价相对比访问脚本数据更高。当访问Collection和
Dictonary对象时,这项技术也能消减高昂的查询代价。

通常,当准备不止一次访问一个对象数据时,应该将这个数据放当一个脚本对象中。
这项优化的主要目标是Request变量(Form和QueryString变量)。例如,你的站点传递一个叫UserID的QueryString变量,假定在一个特定页UserID被引用十次。在ASP页面的顶部,将UserID的值赋给一个变量,来替代十次的调用Request
("UserID"),将接生9次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的一个成员。这触发一次COM方法调用
3. 变量blash被解析为Foo.bar的一个成员。同样,这也触发一次COM方法调用
4. 变量qaz被解析为Foo.bar.blash的一个成员。对,这也触发一次COM方法调用
5. 调用 Foo.bar.blah.qaz(1)。一个或多个COM方法调用。获取图片?
6. 重复步骤1到步骤3来解析baz。系统不知道调用qaz是否会改变对象模型,所以步骤1到步骤3又执行了一次,来解析baz
7. 解析出baz是Foo.bar.blah的一个成员,执行属性put.
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

技巧13:避免使用可变数组
尽量避免使用可变数组。既然关心性能,最好还是在数组初始化的时候就设置好它可能的最大大小。当然,这不是说你明知不需要几M的内存,但还是应该给数组分配了那么多。

下面的代码是一个无理使用Redim的示范:

<%
Dim MyArray()
Redim MyArray(2)
MyArray(0) = "hello"
MyArray(1) = "good-bye"
MyArray(2) = "farewell"
...
Redim Preserve MyArray(5)
MyArray(3) = "more stuff"
MyArray(4) = "even more stuff"
MyArray(5) = "yet more stuff"
%>

简单地将数组初始化的时候定义成正确的大小(这里应该是5)远比Redim数组使它变大好。你可能会浪费一些内存(如果你最后没有用完所有的元素),但得到的是速度!

技巧14:使用响应缓冲(Response Buffering)
打开“响应缓冲”就可以缓冲整个页面的输出,这样可以减少向浏览器写的次数,提高了总体性能。每次写浏览器都要耗费一定的时间和资源,因此减少写浏览器次数能提高性能;同时,TCP/IP协议发送少的大块数据比发送多的小块数据效率更高。

有两种方法可以打开响应缓冲。首先,可以使用Internet Service Manager来打开整个应用的响应缓冲。这是推荐的方法。在IIS 4.0和IIS 5.0中,新建一个应用时,响应缓冲缺省是打开的。第二种方法:针对每个独立的ASP页面,可以通过在页面顶部放置如下代码来打开响应缓冲:

<% Response.Buffer = True %>

这行语句必须在所有缓冲数据写之前执行(就是说,在所有HTML和通过
Response.Cookies设置Cookie之前)。通常,最好是为整个应用打开响应缓冲;这样你就不用在每个ASP页面顶部写上面那条语句了。

Response.Flush

对于响应缓冲,因为用户在看到东西之前必须等待整个页面生成,所以用户可能够感觉到ASP页面响应比较慢(虽然整体响应时间缩短了);对一个运行时间较长的页面,可以同过Response.Buffer = False 来关掉响应缓冲;但更好的策略是使用
Reponse.Flush方法。这个方法把所有已经由ASP生成的HTML输出到浏览器中。例
如,一个1,000行的大表,在写完100行之后,ASP可以调用Response.Flush来强制把结果写到浏览器中,这样,用户就可以在其余行生成之前先看到100行数据。这个技术能让你两全其美—响应缓冲和渐进式地在浏览器表现数据。

(注意,在上面的1,000行表的例子中,很多浏览器在遇到</table>标记之前可能并不画出整个表。如果想让浏览器逐步显示出数据,可以将一个大表分成多个小表,然后对每个小表调用Response.Flush。新版本的IE会在下载完整个表之前显示表,并且如果指定了表的列宽,显示的速度会更快。)

另外,当产生一个非常大的页面时,响应缓冲可能会消耗掉许多的服务器内存。这个问题也可以通过使用Response.Flush来解决。

技巧15:脚本大块化和Response.Write语句
VBScript语法<% = 表达式 %>把“表达式”的值写到ASP输出流中;但如果响应缓冲没有打开,每个这样的语句都会想浏览器写数据,就把网络流分成很多小的包。这样会慢。同样,零星的小段脚本和HTML导致频繁的在脚本引擎和HTML之间切换,降低了性能。因此,应该使用以下技巧:把小块内嵌表达式改成调用Response.Write。例如,在下面的例子中,每行的每个字段都向响应流中写数据,并且每行都在VBScript和HTML中切换:

<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>

下面是更有效的代码,每行只向响应流中写一次。所有的代码包含在一个VBScript块中:

<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.Write对性能的提高。

(在特定情况下,生成表的嵌套循环(While Not rs.EOF...)可以用GetString函数来替换)

技巧16:在进入长时运算之前使用Resonse.IsClientConnected
如果用户感到不耐烦,他们可能在ASP页面计算他们的请求之前离开这个页面。如果他们点击刷新或是跳到服务器上的另一个页面,新的请求将位于ASP请求队列尾部,而中断的请求却在请求队列的中部;通常服务器在高负载情况下可能发生这种情况(服务器有很长的请求队列,同时请求次数也很多);而这种情况又使服务器的负载情况变得更加恶劣。如果用户已经断掉连接,没有必要再执行这个ASP页面(尤其当这是一个很慢、很耗资源的页面时);Response.IsClientConnected属性能检查出这种情
况;如果属性返回False,就应该调用Resonse.End来结束剩余的页面。事实上,
IIS 5.0使这种检查规律划--无论什么时候ASP准备执行一个新的请求,他先检查请求队列有多长;如果队列已经超过3秒钟,ASP就会检查客户端是否连接;如果客户端已经断开,ASP立即终止这个请求。可以使用AspQueueConnectionTestTime设置来调整3秒的超时。

如果有一个非常耗时的页面要执行,也可以在页面中检查
Response.IsClientConnected。当响应缓冲打开时,在页面运行中使用
Response.Flush也能给用户操作正在执行的感觉。

注意:在IIS 4.0上,除非你先执行了Response.Write,否则
Response.IsClientConnected的结果有可能不正确;如果响应缓冲已经打开,还必须先执行Response.Flush。在IIS 5.0上,就没有这个必要了,
Response.IsClientConnected工作得很正常。无论任何情况,
Response.IsClientConnected总要消耗一些时间,因此,只应该在执行耗时至少超过500ms的页面中执行。首要原则是,不要在一个紧密的循环中反复调用这个属性。

技巧17:用<OBJECT>标记来实例化对象
如果你想在Global.asa中引用一个不在所有的代码路径中使用的对象(特定的服务器 - 或应用 - 范围对象),使用<object runat=server id=objname>标记定义比用Server.CreateObject方法定义更为合适一点。因为Server.CreateObject立即创建对象,但如果过你以后不使用这个对象,则浪费了资源。<object id=objname>只是声明objname,但objname并没有真正创建;objname在第一次使用时才创建。

技巧18:为ADO和其他组件使用类型库声明
当使用ADO时,开发者往往通过包含adovbs.txt来访问ADO常量。这个文件必须包含在每个使用常量的页面里;而常量文件还想相当的大,大大增加了处理每页耗费的时间和资源。


IIS 5.0引入了绑定组件类型库的能力;允许只引用类型库一次,然后就可以在每个ASP页面中使用。每个页面不用在为编译常量文件而消耗资源;组件开发者也不用为ASP准备VBScript包含文件了。

可以在Global.asa中放入如下语句,来访问ADO类型库:

<!-- METADATA NAME="Microsoft ActiveX Data Objects 2.5 Library"
              TYPE="TypeLib" UUID="{00000205-0000-0010-8000-
00AA006D2EA4}" -->


<!-- METADATA TYPE="TypeLib"
              FILE="C:\Program Files\Common
Files\system\ado\msado15.dll" -->

技巧19:在循环中避免进行字符串连接
很多人喜欢用如下的循环生成字符串:

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

这种方法有一些问题。第一个就是在循环中连接字符串会使时间成二次方
(quadratic)成长;或者说,运行这个循环的时间同记录的字段数目平方成正比。
下面简单的例子能更清楚地看见本质:

s = ""
For i = Asc("A") to Asc("Z")
    s = s & Chr(i)
Next

在第一次循环中,s等于"A";在第二次循环中,VBScript必须重新分配s的空间,并把字符串"AB"赋给s;在第三次循环中,又重新分配s的空间,重新赋值。在第N(26)次循环中,VBScript重新分配并复制了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 += "some string",不要使用 s = s + "some string"。

技巧20:使用Server.Transfer代替Response.Redirect
Response.Redirect告诉浏览器请求另外一页。这个函数经常用来把用户跳转到登录页面或错误页面。既然redirect强制产生新的页面请求,结果就是浏览器和Web服务器间做了两次交互,Web服务器不得不多处理一次额外的请求。IIS 5.0引入了一个新的函数:Server.Transfer;这个函数直接把运行权交给同一个服务器上的另一个页面;避免了额外的浏览器到Web服务器的交互,提高了性能。

技巧21:在目录URL的尾部加上斜杠(/)
如果省略了尾部的斜杠,浏览器回发送一个请求给服务器,被告知它的请求是一个目录;然后浏览器再发送一个二次请求,不过这次URL尾部加上了斜杠,然后服务器再次响应浏览器。如果一开始就给URL加上斜杠,便可以省去无用的请求;当然,为了用户友好性,你可以在显示名字时省略尾部斜杠。

例如,按如下的写法:

<a href="http://www.asplot.net/faq/" title="ASPLot
FAQ">http://www.asplot.net/faq</a>

技巧22:避免使用服务器端变量
访问服务器端变量将使站点给服务器发送一个特殊请求,收集所有的服务器端变量,不仅仅是你所访问的那一个。


 

原标题:全面优化ASP应用程序的性能

关键词:ASP

ASP
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。