• |
尽可能地使用池进程的 ASP 页面以及组件。如果 IIS 服务器是一个单使用的服务器(您的应用程序是唯一的一个),而且您确信 ASP 页面以及 MTS 组件的健壮性,那么它们都在 IIS 进程中运行是可以接受的。注意:如果运行在 IIS 进程中,ASP 页面或组件的问题就可能造成整个服务器的瘫痪。在 5.0 以及更高版本的 IIS 中,提供了一种被称为进程外入池的进程独立新方法。标记为池的应用程序并不共享 IIS 进程中的空间,但它们也不在自己的进程空间中。所有池应用程序共享一个进程,这样不仅保护了 IIS 进程,而且也改善了程序性能。
全面地分析整个服务器是很重要的。您也许会认为在每个应用程序中、或者在每个虚拟 Web 站点进程内、或者在池进程中运行每个 ASP 页面会比在进程外运行这些页面获得更好的性能。其实并不尽然。严格地说,性能方程中有几千个变量。确保最优性能的唯一办法是在应用程序开发周期里计划性能测试。例如,我曾经有一个大型网站,确实通过将一些应用程序移到进程之外获得了实质的性能改善。
如果应用程序和 IIS 服务器之间的进程独立特别重要,那么可以在一个独立的进程中运行 IIS 应用程序,但是要使用 MTS 组件的库程序包。这通常是一个很好的折衷。组件中出现的问题可能会造成应用程序的崩溃,但即使它们处于两个不同的进程,这样的问题也会以某种形式传回到 ASP。此外,这样的配置总是优于在 IIS 进程中运行 ASP 以及在服务器进程中运行 MTS 组件。
|
• |
监视这些对象的引用。传递对象的指针也许会使编程更容易,但这会牺牲性能。这对于网络链接来说尤其如此,但是跨进程引用同样也会使性能受到影响。记录集是人们经常希望传递的对象。如果需要传递记录集,尤其是传递给客户端,可以使用客户批量记录集,它是 ActiveX 数据对象 (ADO) 和远程数据服务 (RDS) 的一部分。这个不连接的记录集可以根据值来封送,也可以通过 HTTP 来封送。
|
• |
监视属性 Get 和 Set 调用。用一个方法调用得到一组相关的属性通常比单独的属性 Get 和 Set 调用更有效。每次调用都需要一次往返行程,所以使方法调用和往返行程的次数最少,就可以使性能最优。换句话说,对象包含少量含较多参数的方法通常比对象包含很多属性和含较少参数的方法更有效。
|
• |
避免委托。 Visual Basic 的 Implements 关键字使得程序员可以委托调用以实现简单形式的继承。这有利于代码重用,但要注意不要滥用它。委托可能会快速降低程序的性能,特别是在像 MTS 这样的无状态的环境中,对象会不断地被创建和释放。
|
• |
避免在事务性组件中进行运行时目录查询。由于系统表通常很小,运行时查询就可能造成严重的数据库阻塞。这种情况可能明显也可能不明显。例如,要避免构建访问系统表的 SQL 语句是很容易的,但是要了解数据库 API 或对象中的某个标记何时产生方法去访问系统表是困难的。您必须在部署应用程序之前检查 ODBC、数据库日志和这一行为标志的跟踪记录。
|
• |
留心共享数据。共享属性访问管理器、会话对象以及应用程序对象都有共享数据。但是,单元线程的对象绝对不可存储到它们中,在存储其他数据时也要注意不可滥用它们。接口封送和线程同步(分别)都能够影响性能。
|
• |
避免访问未调整的 COM 服务器。例如,我曾经见过 MTS 应用程序在存储数据之前调用 Microsoft Word 对它进行格式化,等等。Word 和其他这样未调整的 COM 服务器并不是按照最优的思路开发的,所以通常其性能决不是最优的。
|
• |
避免存储状态。在几种情况下存储状态是有益的,但是在大多数情况下,存储状态会损害可伸缩性并导致出现其他问题。
|
• |
不要从 ASP 页面直接向机器外调用。参见知识库文章 Q159311,可从 http://www.microsoft.com/support/ 下载,文章讨论了使用 IIS 机器上的中间 MTS 包,并继而调用远程服务器。这样做的实际原因是出于安全性考虑,第一次听说这种做法时,我曾想:“这是多么可怕的性能恶梦啊”。可是,在我对它进行了仔细研究并试过几个例子之后,我意识到可以用它来改善性能。
ASP 页面将使用组件的 IDispatch 接口。这导致每个方法调用都被调用两次。如果能够在早期绑定中间组件,就可以限制网络调用为每个方法调用一次。而且,您可以设计中间组件容纳引用、缓存数据等等。这样做可以使通过网络存取数据的频率和数量最小。
|
• |
在事务性组件中使事务持续时间最短。MTS 为第一个事务性组件实例化的对象启动一个 Microsoft 分布式事务协调器 (DTC) 事务。然后该事务就可以贯穿整个活动。即使这个事务实例化,资源管理器事务也要等到有程序试图第一次访问它时才会启动。因此,应该尽可能迟地启动这个事务,尽可能长时间地延迟实际的资源管理器访问,并尽可能快地结束这个事务。
|
• |
在每次方法调用里进行 SetComplete/SetAbort 操作。无状态是进入 MTS 的方式。但并不是说在一个活动里应该有多个对象。它只是意味着您应当只对事务中任何给定的对象进行调用。如果使用次数增加,跨越多个方法的活动可能会进入到一个死锁状态。 此外,这样做可以为了性能简化执行路径,也有利于消除可能的故障点(特别是对于远程组件)。
|
• |
经常检查 Retain in Memory 和 Unattended Execution。没有这些标志,VB 运行时就会不断地卸载和重载。这样不利于线程安全。肯定会进入挂起和崩溃状态。
|
• |
在启动前进行示例测试来比较方法、组件和工具。 这样可以在早期给您性能和问题的概念。例如,使用 ODBC API 可以产生很快的代码,但是增加开发成本是否值得?或者用一点额外的硬件,仔细开发一些 ADO 代码是否更有意义? MTS 性能小组正着手发布一些性能数据,但是应用程序的简单原型也可以提供有价值的信息。
|
• |
考虑未来。应用程序需要伸缩到什么程度?需要变得更分布吗?需要位置透明吗?这些类型的问题都会影响开发。例如,如果应用程序不需要特别的伸缩性并总是在进程内运行,传递对象引用和执行很多属性 Get 和 Set 也许并不是问题。如果应用程序需要变更,性能和可靠性可能都会由于这样的设计而受损害。
|
• |
以不少于计划在相似的硬件上部署和运行程序的用户数(模拟)进行测试。在整个开发周期中都以这种方式进行测试。应用程序经常以单用户模式或者也许有几个客户来进行开发和测试。然后应用程序在重负载情况下测试 — 或者更糟糕的是大批量地发布给用户,最后以失败告终。
|
• |
使用一致和通用的错误处理机制。错误处理机制通常是一种亡羊补牢之举。这在桌面应用程序中通常不会产生问题,但是在关键任务的多层分布式应用程序中,未仔细筹划和测试的错误处理会导致严重问题。在以后的文章里,会涉及到错误处理技术。
|
• |
要小心第三方控件和对象库。虽然它们也许能提供一些较酷的功能或核心逻辑,但很多控件都还没有对 MTS 进行测试,另一些控件也从未在高强度服务器环境里测试过。应该确保供应商在这方面做过测试或者在发现错误以后能够做出响应。
|
• |
避免回调。服务器调用客户端返回通常是一个注定要发生的问题。此外,MTS 编程模型不能很好地支持回调。在 Visual Basic 程序里,回调可以通过传递对象引用给服务器显式地进行,或者用 WithEvents 关键字隐式地进行。WithEvents 关键字使用连接点,因此使回调在幕后进行。
|
• |
使用 RDS 组件和与记录集对象分离的 ADO ClientBatch。这样做可以将行集合传给客户端,甚至可以通过 HTTP 直接调用业务对象。IIS、MTS,以及 ASP 的结合,使您不用牺牲 Web 应用程序的任何优势,就可以构建和传统客户端/服务器解决方案一样健壮、一样功能完整的 Web 应用程序。
|
• |
不要在会话或应用变量中存储 MTS 组件。MTS 把所有组件都看作是单元线程的,因此将它们存储在会话或应用程序变量中会造成性能和可伸缩性的问题。要了解更多信息,请参见 IIS 产品文档中的 Scripter's Reference。
|
• |
不要在 ASP 脚本中使用 CreateObject() 来创建 MTS 对象。使用 Server.CreateObject()。 Server.CreateObject() 能遍历上下文并提供改良的安全性检查。只有您完全理解这样做的不同之处,而且真正需要在不同的活动中运行 MTS 组件时,才使用 CreateObject。更多信息请参见 IIS 产品文档的 Scripter's Reference。
|
• |
只要可能,用值 (ByValue) 来传递参数。这比用引用 (ByRef) 来传递参数更有效。只有在您需要返回数据给客户端时才使用 ByRef 来传递参数。确保所有返回数据给脚本(传引用)的方法参数都是 Variant 类型。Visual Basic Scripting Edition (VBScript) 无法使用其他类型的参数来返回数据。
|
• |
使用 New 关键字时要小心。当用于在同一项目下创建对象时,New 关键字不会使用 COM 创建方法。因此,使用 New 创建的对象不是 MTS 对象,即使它们安装在 MTS 中。
|
• |
确保使用二进制兼容性。同样,对于 Visual Basic 项目,还必须设置线程模型为 Apartment。
|
• |
不要在客户机上开发服务器组件。在客户机上开发乍听起来似乎很方便,但是经历了几次“编译组件,在客户机上注销组件,复制到服务器,在 MTS 上刷新组件,再输出包以生成客户端输出可执行文件,在客户机上运行客户端的输出可执行文件,然后运行客户端程序”过程,再在此过程中处理所有的问题,您就会想在服务器上开发了。
|
• |
避免用组件代码来实现安全性。例如,用 IsCallerInRole 可以实现方法级安全性。但这意味着安全性管理没有完全独立于组件的实现。像角色名改变这样的管理性改变就必须使组件代码发生变化。
|
• |
编译时打开符号调试信息。否则,您就无法从 Dr. Watson 的转储中得到有用的信息。像 100% CPU 负载、进程挂起(无响应进程)、进程崩溃等情况只有当获得符号调试信息时才能解决。
|
• |
打开一个新的 Visual Basic ActiveX DLL 项目。
|
• |
重命名 ProjectName 为 prjMTSDebug,并且重命名 Class1 为clsMTSDebug。
|
• |
设置 clsMTSDebug 的事务属性为 1. No Transactions 。
|
• |
为 clsMTSDebug 添加如下代码:
Public Function Sum( Val1 As Integer, Val2 As Integer) As Integer
Sum = Val1 + Val2
End Function
|
• |
编译这个 DLL。
|
• |
设置此项目为二进制兼容。(同时,设置 Retain in Memory 和 UnattendedExecution 并选中 Create Symbolic Information 复选框)。
|
• |
创建一个名为 MTSDebug 的新 MTS 包。
|
• |
将 DLL 添加到包里。
|
• |
在 VB IDE 中按下 F5 运行项目。接受默认设置并点击 OK。
|
• |
创建并添加该 ASP 到您的一个虚拟目录中。
Dim Obj
Set Obj = Server.CreateObject("prjMTSDebug.clsMTSDebug")
Response.Write Obj.Sum(2,3)
Set Obj = Nothing
|
• |
放置一个断点到 Sum 函数中(在 Visual Basic IDE 中)。
|
• |
在浏览器中运行这个 ASP 页面。程序在断点处停止。
|