技巧 9:进程外的执行将牺牲可靠性
ASP 和 MTS/COM+ 都有允许您以可靠性换取性能的配置选项。当建立和部署应用程序时,应当理解这种交换。
ASP 选项
ASP 应用程序可以配置为以三种方式之一运行。在 IIS 5.0 中引入了术语“隔离级”来描述这些选项。三个隔离级值分别是低、中和高:
低级隔离。该隔离级在所有版本的 IIS 中受到支持,并且是最快的。它在主 IIS 进程 Inetinfo.exe 中执行 ASP。如果 ASP 应用程序崩溃,则 IIS 也将崩溃。(要在 IIS 4.0 下重新启动 IIS,Web 站点管理员需要使用工具,如 InetMon,来监视站点,如果服务器失败,将运行批处理文件来重新启动服务器。而 IIS 5.0 则引入了可靠的重新启动,它将自动重新启动失败的服务器。)
中级隔离。IIS 5.0 引入了这个新隔离级,它称为进程外的,这是因为 ASP 运行在 IIS 进程之外。在中级隔离中,所有被配置按“中级”运行的 ASP 应用程序,将共享单个进程空间。这将减少在一个服务器上运行多个进程外的 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 进程外应用程序,要比 IIS 4.0 中的进程内应用程序运行得更快。无论如何,进程内(低隔离级)在两种平台上仍然产生了最好的性能。但是,如果您的命中率相对较低或最大吞吐量较低,选择低隔离级不会有太大的好处。所以,除非您需要每个 Web 服务器每秒处理数百或数千个页面,否则没有必要选择低隔离级。同样,应当测试多种配置并判断哪种情形最适合您。
注意: 当您进程外运行 ASP 应用程序(中级或高级隔离)时,则在 NT4 上它们将运行在 MTS 中,而在 Windows 2000 上它们将运行在 COM+ 中。即,在 NT4 上它们运行在 Mtx.exe 中,而在 Windows 2000 上它们运行在 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 是由原始组件创作者实现的。
怎么办?
如果您想要以性能与可靠性的合理交换来完成您的配置,我们的推荐如下:
在 IIS 4.0 上,使用 ASP 的低隔离级别,并使用“MTS 服务器包”。
在 IIS 5.0 上,使用 ASP 的中隔离级别,并使用“COM+ 库应用程序”。
这些是很一般的准则;通常让公司以中或高隔离级别运行 ASP,而单一目的的 Web 服务器可运行于低隔离级别。请权衡折中并自行决定满足需求的配置。
技巧 10:显式使用选项
在 .asp 文件中显式使用选项 Explicit。置于 .asp 文件开头的这一指令,强制开发人员声明所有要使用的变量。许多开发人员认为这有助于调试应用程序,因为它避免了错误键入变量名称而不经意地新建变量(例如,MyXLMString=... 而非 MyXMLString=)。
也许更重要的是,声明的变量比未声明的变量快。实际上,脚本运行时,在每次使用未声明变量时按照名称引用。而声明的变量,在编译或运行时分配了序号。这样,声明的变量按照该序号引用。由于选项 Explicit 强制变量声明,因此保证声明了所有变量而实现快速访问。
技巧 11:在子例程和函数中使用局部变量
局部变量是在子例程和函数中声明的变量。在子例程和函数中,局部变量访问要快于全局变量访问。使用局部变量还可以使代码更加清晰,因此尽可能使用局部变量。
技巧 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 ' 对 blah 做一次解析
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 编程同样有效。