简介
“Active Server Page (ASP)”应用程序的成功常常取决于对体系结构和设计这两方面的取舍。考虑到 ASP 技术的范围之广和当前应用程序固有的复杂性,这种取舍是非常困难的。本文中,我将为您提供一些特定的指导方针,以助您成功开发基于 ASP 的应用程序。
从过去成功的开发模式经验中,我们总结出以下原则。
我已将指导方针整理成一组开发原则。在评估解决方案和技术时,可以应用以下原则帮助您做出决策。以下原则是我长期以来从成功的开发模式所得的经验积累。
原则 1:采用标准方法
建立命名约定并使目录结构标准化,可以帮助您大大提高 ASP 应用程序的可读性和可维护性。虽然目前尚无 ASP 应用程序的正式标准,许多开发人员还是建立了一些通用方式。在此,我将与您共享一些更为通用的方式。
因为 ASP 技术依靠脚本引擎进行工作,而且脚本具有类型不严密的天性,命名约定也很模糊。在类型非常严密的语言中,变量将按照它的实际类型进行声明。在使用 ASP 技术时,通常按照处理变量的方式(而不是其实际数据类型)在 ASP 代码中声明变量。例如,在使用“Visual Basic(R) Scripting Edition (VBScript)”时,尽管所有的 VBScript 变量都是 Variant,你还是会将成功标志声明为 bSuccess(b 代表布尔型),而不是 vSuccess(v 代表 Variant)。
下表是一些通行的命名约定。
变量前缀:
数据库对象的变量前缀:
范围及前缀的用法:
Knowledge Base (KB) 中的一篇文章“Q110264 INFO: Microsoft Consulting Services Naming Conventions for Visual Basic”(英文)对命名约定提供了真知灼见。
尽可能采用目录结构为您的各个应用程序部件提供始终如一的位置。您应用程序的实际目录结构当然由您自己决定,但通常是将图像、文档、include 文件和组件分别放置在单独的目录中。以下是简单 ASP 应用程序目录结构示例。
目录结构示例:
\SimpleAspApp \Docs \Images \Includes
一个好的目录结构允许您有选择地应用 NTFS 权限。您还可以从 ASP 应用程序内部使用相对路径。例如,可以使用以下代码,从位于 SimpleAspApp 目录的 default.asp 页,引用 Includes 目录中的 include 文件 top.asp:
./includes/top.asp
注意我的 include 文件的扩展名是 .asp,而不是 .inc。这样做是出于安全方面的考虑,而且使用 .asp 扩展名(而不是 .inc),还能够在 Visual InterDev(R) 中使用彩色编码。
有关结构化 ASP 应用程序的其他一些提示和技巧,请参阅文章“ASP Conventions”(英文)。
原则 2:设计为在服务下运行
ASP 将在服务下运行。设计 ASP 应用程序时,您马上会面临在桌面应用程序中不会遇到的安全环境和线程问题。在桌面环境中,通常只处理作为交互式用户运行的单线程执行,而且有权访问当前的桌面系统。在“Internet 信息服务 (IIS)”中,模拟不同用户环境的多个客户机线程调用您的应用程序,而且您的应用程序被限于“系统”桌面。
这对您来说意味着什么?请学习 IIS 的安全模式。还要提醒您:仅因为某些东西能在 Visual Basic IDE 下能够正常运行,并不意味着它就能在 ASP 技术中安全运行。Visual Basic IDE 并没有准确地模拟运行时环境。常见的设计错误包括:在 ASP 技术中使用需要用户界面的 .OCX 控件,使用对线程来说不安全的组件,和使用要求特殊的用户上下文的组件。要避免的一个最简单的问题,就是从应用程序中试图访问 HKEY_CURRENT_USER (HKCU) 注册表项(例如,不要调用 Visual Basic 的 GetSetting 和 SaveSetting 函数,它们都依赖于 HKCU)。同样,不要出现需要用户进行人机交互的消息框或其他对话框。
以下文章是有关 ASP 技术中的安全和验证问题的相当不错的入门读物:
- “Authentication and Security for Internet Developers”(英文)
- “Q172925 INFO: Security Issues with Objects in ASP and ISAPI Extensions”(英文)
原则 3:封装商务逻辑
ASP 技术通过生成 HTML 输出提供了表示服务。简而言之,它会生成用户界面。您需要将商务逻辑从 ASP 表示脚本中分隔开来。即使您不使用 COM 组件将商务逻辑从 ASP 代码中分隔开来,至少也要将商务逻辑分隔到函数和 include 文件中,以提高可维护性、可读性和可重用性。在需要排除故障和隔离问题时,您还能体会模块化设计方法的好处。
调用脚本内部调用函数和方法,可避免代码乱作一团,并能在 ASP 应用程序中添加结构。下面举例说明从 ASP 代码中,将逻辑分离到方法调用中:
lt;% Main() MyBizMethod() ... Sub Main() GetData() DisplayData() End Sub %>
在使用包含 ASP 功能的技术时,可以应用这一原则。下面举一个使用 Visual Basic WebClass 时的例子,说明如何使用这一原则:
- 因为 WebClass 本身引用 ASP 代码生成 HTML,所以您不要将商务逻辑直接置于 WebClass 内。因为这是您的表示层,不在 MTS/COM+ 下直接运行 WebClass。
- 从 WebClass,可以调用能运行在 MTS/COM+ 中的单独商务组件。
- 您可以决定创建自己的、具有对 ASP 引用的 COM 组件,而不是依赖于 WebClass 框架结构和额外的 WebClass 运行时开销 — 您也可以使用 ASP 脚本直接将商务组件自动化。
原则 4:尽晚获取资源,尽早释放资源
常见的问题是,从桌面系统到服务器的过渡。许多具有桌面系统背景的开发人员从来没有为服务器的一些问题和资源共享担心过。在传统的桌面应用程序中,连接到服务器是个耗时的过程。为了改善用户的体验,通常采用尽早获取资源和推迟释放资源的方法。例如,许多应用程序会在它的整个运行时间内始终连接着数据库。
这种方式在传统的桌面应用程序中能够正常工作其原因是用户数量非常明确,容易加以控制,并且后端与前端紧密连接。然而,对于当前的 Web 应用程序,这种方式已经不可行了,其原因是有限的服务器资源将面对越来越多的用户。为了使您的应用程序能够应付用户的增加,您需要尽晚获取资源,尽早释放资源。
共用有助于增加这一方式的有效性。通过共用,多个用户能够共享资源,而且等待时间最少,对服务器的影响也最小。例如,在处理数据库时,ODBC 连接共用和 OLEDB 资源共用可以实现从共用池中选择连接,最大程度地减少连接数据库的开销。
有关共用 ADO 的详细信息,请参阅“Pooling in Microsoft Data Access Components”(英文)。
原则 5:使用数据库维护复杂的状态
尽管 HTTP 协议是无状态的,ASP 开发人员还是会经常使用 ASP 功能内置的状态保持机制。例如,使用 ASP 技术内置的 Application 对象,开发人员所保存的资源能够为应用程序的所有用户共享。通过使用 ASP 内置的 Session 对象,开发人员只为单个用户保存资源。
尽管听起来在 ASP 技术的 Session 对象中保存信息是一个非常方便的保持状态的方式,然而这一方式付出的代价太大,而且它也可能成为对可伸缩性的最大的限制因素之一。应用程序的可伸缩性本质上是随着用户数目的增长能够继续保持其性能的能力。而对于每一用户,在会话超时或被放弃之前,Session 对象都会消耗服务器的资源。会话还会将您捆绑到一台服务器上,从而限制您利用 Web 集群的功能。请尽可能不要使用 ASP Session 对象进行状态管理。如果您完全没有使用会话,您就可以禁用 Web 应用程序的 Session 状态(请参阅 IIS 文档)。否则,您可以使用下述语句,针对每一页禁用 Session 状态:
<%@ENABLESESSIONSTATE=False %>
对于一些简单的数据,您可以使用 QueryString cookie 或隐藏的窗体域保持 ASP 请求间的状态。然后,对于更为复杂的信息,通常推荐您使用数据库。一般所采用的方式是生成某一特有的标识符,然后发送到每一个发出请求的客户机,并保存为隐藏的窗体域。在随后的请求中,这一特有的标识符被用于在数据库中查找与该用户相关的状态信息。这一方式提供了更高的可伸缩性和更为简洁明了的代码。
有关使用 QueryString cookie 和隐藏的窗体域的详细信息,请参阅“Q175167 HOWTO: Persisting Values Without Sessions”(英文)。
原则 6:使用 Server.CreateObject 创建对象
在创建 ASP 技术的对象时,您可以选择 <OBJECT> 标记、Server.CreateObject 和 CreateObject 三种方式。每项技术的行为略有不同。尽管在 IIS 4.0 中,使用 <OBJECT> 标记或 CreateObject 比 Server.CreateObject 略具性能优势,我们一般还是推荐使用 Server.CreateObject, 以便于 ASP 应用程序认知您的对象。(注意在 IIS 5.0 中,前两项与 Server.CreateObject 相比,已经没有性能优势。)
<OBJECT> 标记仅在调用第一个方法时才会创建组件,因此能够节省资源。Server.CreateObject 使用 ASP 技术内置的 Server 对象创建组件。实质上,它只是执行了 CoCreateInstance,但是 ASP 却能够认知这一对象。同时,还将调用 ASP 技术的传统的 OnStartPage 和 OnEndPage。(注意最好在 IIS 4.0 或者更高版本中使用 ObjectContext)。如果您只是使用 CreateObject,您将越过 ASP 技术而直接使用 Scripting 引擎。
以下是一个可能出现的例外情况:当您通过防火墙进行调用时,您可能需要调用 CreateObject 而不是 Server.CreateObject。详细信息,请参阅“Q193230 - PRB: Server.CreateObject Fails when Object is Behind Firewall”(英文)。
原则 7:提供丰富的疑难解答信息
确保在您所有的 ASP 应用程序中都包含了错误处理过程。而且,确保您提供了有用的诊断信息。我还没有碰到有哪个人抱怨错误信息太具有说明性了。请确保在错误日志中包含以下信息:
- 用户上下文(如果您正在使用组件,您可以调用 GetUserName)
- 线程 ID(在组件中,可以调用 GetCurrentThreadId)<
- 时间
- 完整的错误信息(包括编号、来源和说明)
- 参数值
因为将在 ASP 下运行,您可能希望将这些信息写到文件或 NT 的事件日志。您还可以创建记录关键的应用程序事件的应用程序事件日志,以备诊断应用程序错误时使用。
以下文章提供了有关错误处理技术的详细信息:
- “Bulletproofing Your ASP Components”(英文),Charles Alexander 著
- “Fitch & Mather Stocks: Web Application Design”(英文)
- “Handling and Avoiding Web Page Errors, Part 1: The Basics”(英文)
- “Handling and Avoiding Web Page Errors, Part 2: Run-Time Errors”(英文)
- “Handling and Avoiding Web Page Errors, Part 3: An Ounce of Prevention”(英文)
原则 8:测试性能、可伸缩性和可靠性
浏览器并不是准确的测试方式,它只能向您展示应用程序可能的用途。请针对您的应用程序设置特定的性能目标,并使用 Web Application Stress Tool 等负载工具进行压力测试。您需要自己决定您的环境所能接受的条件,以下是一些帮助您启动测试过程的通用指导方针:
- 通过测试 ASP 每秒钟的请求数对性能进行测试,并建立一个最小的阈值。一般情况下,不执行数据库访问的简单 ASP 页每秒钟至少应返回 30 页。调用组件或访问数据库的页每秒钟至少返回 25 页。
- 向应用程序不停地追加用户,直到每秒钟的请求数低于预先设置的阈值,用这种方式测试可伸缩性。
- 从 Web 集群中移去机器,并检查错误和故障情况,以便测试可靠性。
将测试环境与实际运行的环境相匹配,甚至防火墙也不例外。这听起来代价很高,但我曾经听说过开发人员因为没有考虑到防火墙,而丢失了工作。
有关使用 Web Application Stress Tool 测试 ASP 应用程序的详细信息,请参阅“I Can't Stress It Enough -- Load Test Your ASP Application”(英文)。
原则 9:增加隔离性
使用隔离功能保护您的应用程序过程能够极大地增强服务器的稳定性。谈到 Internet 应用程序,是否使用隔离功能的后果可能会有巨大的差别:一个是应用程序崩溃,一个是服务器当机。保护主 IIS 进程 (InetInfo.exe) 通常会排在优先级列表的较高位置。在您使用组件时,这一点尤为突出。
通常所采用的保护主 ISS 进程的技术是使 Web 应用程序运行在各自的内存空间中。在 Internet Services Manager 中,您可以针对每一个 Web 设置这一选项。虽然因对进程进行编组而开销的系统资源会对性能有些微的影响,但对应用程序所起的保护作用值得付出这一代价。 在 IIS 4.0 下,您可以采用进程内 (in-process) 和进程外(out-of-process,OOP)两种方式运行应用程序。OOP 应用程序会运行在新的 Mtx.exe 实例中。在 IIS 5.0 下,您还能使用其他的隔离选项。可以将隔离级别设置为“低”(对 Inetinfo.exe 来说是进程内应用程序)、“中”(DllHost.exe 共享实例)或“高”(Dllhost.exe的非共享实例)。
除了将 Web 应用程序隔离在它们自己的内存空间中之外,您可能还希望隔离不信任的组件。不信任的组件通常是在实际环境中没有通过测试时间的考验的组件。您可以在 Server 包中运行这些组件,这样它们会运行在新的 Dllhost.exe 实例中。
一般而言,如果要在性能和保护措施之间采取中庸之道,方式如下:在“高”隔离状态运行 Web 应用程序,在库包中运行组件。这种方式最大限度地减少了编组开支,同时在进程之间提供了最强的保护作用。
详细信息,请参阅文章“Server Reliability Through Process Isolation”(英文)。
原则 10:不要滥用线程共用组
在 IIS 4.0 下,针对每个受 MTS 管理的处理器,ASP 的默认共用组是 10 个线程。在 IIS 5.0 中,默认值是 20。这就意味着每一线程都是一份潜在的宝贵资源,能够处理多个客户机请求。您同样需要避免调用会出现阻塞的方法,如进行大的数据库调用。如果您有要执行这种操作的工作,它将阻止 ASP 应用程序将响应快速返回到客户机,则请考虑使用队列功能。例如,在 NT 4.0 中,可以使用 MSMQ。在 Windows 2000 中,可以使用 Queued Components(排队组件)。
在会话中不要存储 Single-threaded Apartment (STA) 组件,这种方式的一个共同缺陷是会填满会话范围中的 Visual Basic 对象。会将用户锁定到某一线程,与线程共用组的目的背道而驰。潜在的用户会被阻塞在其他用户的后面,等待创建他们组件的线程变得有效。您应该采用别的方式,设计能基于每一页进行创建和破坏的无状态组件。
快速提示:确保已在服务器上禁用了 ASP Script Debugging 功能(使用 Internet Services Manager)。如果启用了 ASP Script Debugging,则 ASP 的执行过程将被锁定到某一线程。
详细信息,请参阅以下文章:
- “Q243544 INFO: Component Threading Model Summary Under Active Server Page”(英文)
- “Q191979 PRB: VB Component Not Marked Apartment Produces ASP 0115 Error”(英文)
- “Q243548 INFO: Design Guidelines for VB Components Under ASP”(英文)
- “Q243543 INFO: Do Not Store STA Objects in Session or Application”(英文)
- “Tuning Internet Information Server Performance”(英文)
摘要
创建 ASP 应用程序需要相当宽广的知识面。ASP 应用程序所面临的一个挑战是目前没有通用的规则(这也正是乐趣的一部分)。另外一个问题是许多开发人员接触 Internet 开发之前是从事桌面系统的开发工作。通过在您的 ASP 开发工作中应用上述规则,您有希望避免犯下代价巨大的错误,并能开发出相当不错的 ASP 应用程序。