摘要
最初研究 .NET Pet Shop 的目的是用 Microsoft .NET 实现 Sun 主要的 J2EE 蓝图应用程序 Sun Java Pet Store 同样的应用程序功能。 根据用 .NET 实现的 Sun J2EE 最佳实践示例应用程序,各方面的客户可以直接地对 Microsoft 的 .NET 技术与基于 J2EE 的应用程序服务器进行比较,同时了解构建基于Web的应用程序中用到的各种建议的设计模式之间的异同。 .NET Pet Shop 应用程序现在已经是第三版了,旨在显示构建企业级 n 层应用程序(可能需要支持多种数据库平台和部署模型)的 .NET 最佳实践。 根据社区对 .Net Pet Shop 2.0 的反馈,.NET Pet Shop 3.0 遵照 MSDN 上发布的 Microsoft《说明性体系结构指导》进行了重新设计。 第三版还完全符合了 Middleware 公司的应用程序服务器基准测试规范,将作为 Microsoft 参加今年春天即将进行的 Middleware Application Server Benchmark 的产品: 这是Middleware 公司举办的第二轮测试活动,旨在比较 .NET 和 J2EE 平台在构建和承载企业级 Web 应用程序方面的可伸缩性。
返回页首
Java Pet Store 是 什么?
Java Pet Store 是按 Sun 公司维护的 J2EE 蓝图开发的分布式应用程序的一个参考实现。 示例应用程序最初的开发目的是帮助开发人员和架构师理解如何使用和利用 J2EE 技术,以及各个 J2EE 平台组件是如何配合的。 Java Pet Store 演示软件包括构建应用程序所需的Enterprise Java Beans (EJB) 体系结构、 Java Server Pages (JSP) 技术、标记库和 servlet 的完整的源代码及文档。 此外, Java Pet Store 蓝图应用程序还通过具体示例说明了一些模型和设计模式。
完整的 Java Pet Store 包括三个示例应用程序:
• Java Pet Store: J2EE 蓝图主应用程序。
• Java Pet Store 管理器: Java Pet Store 的管理器模块
• Blueprints Mailer: 在小一些的包中给出一些 J2EE 蓝图设计指南的一个小应用程序。
Java Pet Store 的最初版本旨在处理以下数据库: Oracle、Sybase 和Cloudscape 。 IBM 已经开发了一个 DB2 版本的应用程序。 该应用程序可以从 Java 2 Platform Enterprise Edition Blueprints 公开获得。 主应用程序 Java Pet Store 是一个电子商务应用程序,可以通过它在线购买宠物。 启动应用程序后,可以浏览和搜索各种类型的宠物,从狗到爬行动物。
使用 Java Pet Store 的典型会话方案如下:
主页 — 这是用户第一次启动应用程序时加载的主页。
类别查看 — 有五大类: 鱼、狗、爬行动物、猫和鸟。 每一类都有几个相关的产品。 如果选择鱼作为类别,可以看到天使鱼等等内容。
产品 — 如果现在选择一个产品,应用程序将显示产品的所有类型。 通常产品类型是雄或者雌。
产品详情 — 每种产品类型(分别用不同项目表示)有详细的视图显示产品说明、产品图像、价格和库存数量。
购物车 — 用户可以通过它操作购物车(添加、删除和更新行项目)。
结帐 — 结帐页面以只读视图显示购物车。
登录重定向 — 当用户选择结帐页面上的“Continue”时,如果还没有登录,将重定向到登录页面。
登录验证 — 通过站点的身份验证以后,用户被重定向到信用卡和记帐地址表单。
定单确认 — 显示记帐地址和送货地址。
定单提交 — 这是定单处理流程的最后一步。 定单现在将提交到数据库。
Microsoft .NET Pet Shop
.NET Pet Shop 的目标是把注意力仅仅放在 Java Pet Store 上(管理和Mailer 组件没有在 .NET 中实现)。 除了重现 Java Pet Store 应用程序的功能之外,还增加了两项目标:
• 比较 .NET 和 J2EE 通过最佳实践实现的真实应用程序中代码和代码大小上的异同。
• 提供用 .NET 和 J2EE 实现的典型的设计良好的应用程序能够支持多少用户的数据。
.NET Pet Shop 的整体逻辑体系结构如图 3 所示,设计的中心是在表示层使用 ASP.NET Web 窗体,与逻辑中间层中的 C# 业务组件通信。 业务组件继而通过 ADO.NET 和 SQL Server 名为数据访问应用块 (DAAB) (可以从此链接了解更多 DAAB 信息并下载完整的 DAAB 源代码)的帮助器类访问后端数据库。 数据访问功能完全抽象到数据访问层 (DAL) 中,与业务逻辑层 (BLL) 相分离。 .NET Pet Shop 3.0 中的新颖之处在于,我们为 Oracle 9i 和 SQL Server 2000 数据库都引入了 DAL 层。 相应 DAL 层的类加载将根据 Web.Config 中的应用程序配置设置在运行时动态生成。注意 .NET Pet Shop 3.0 使用了两个后端数据库,定单处理中要涉及跨两个数据库的分布式事务。 使用简单的 Web.Config 应用程序设置,用户可以对 .Net Pet Shop 进行部署,使用一个或者多个后端数据库,还可以自由地将 SQL Server 和 Oracle 后端数据库与由 .NET 服务的组件通过 COM+ 企业服务处理的分布式事务混合。
图 4 说明了 Microsoft .NET Pet Shop 物理上是怎样部署的。 这里使用网络负载平衡 (NLB) 或者可能是硬件实现的负载平衡技术将入站的网络通信量分到了两台应用程序服务器上。 在网络请求达到群集中的一台机器时,针对该请求的所有工作都会在这台特定机器上进行。 业务逻辑和数据访问组件将以程序集的形式安装在两台服务器上,它们本质上是完全相同的。 如果负载平衡软件配置为使用“Sticky IP”,则每台服务器都有自己的会话-状态存储,因为要保证第二个请求返回到实现第一个请求的那台服务器。 如果解决方案所需的容错要求更高,两台应用程序服务器可以共享一个公共会话-状态存储比如 SQL Server 或者一台专用的会话服务器(图中没有显示)。 会话-状态存储的类型和位置由每个站点‘web.config’文件里‘system.web’元素‘sessionState’子节点中的值决定。
业务需求
作为 Pet Shop 3 体系结构文档的一部分,我们给出了 .NET Pet Shop 的业务需求,这样开发人员和客户就可以理解我们在做应用程序的设计决策时进行的一些选择。
Pet Shop 应用程序的功能性需求是什么?
• 应用程序应该使客户能够按类和通过关键字搜索浏览公司目录。
• 应用程序应该为客户提供一种通过一个购物车模型就能购买多个商品项的机制。
• 应用程序应该提供简单的安全模型,这样客户必须先进行登录,才允许购买购物车的内容。
• 应用程序旨在支持高容量的企业级电子商务解决方案;因此应用程序应该展示以下方面:
• 高性能,通过所支持用户数和用户响应时间进行衡量
• 通过增加更多处理器来扩展的能力
• 通过增加更多机器组成群集的分布式扩展能力
• 在大型企业级系统中,应用程序可能需要访问多个数据库,因此应用程序应该支持分布式事务。
• 应用程序应该考虑灵活的部署策略。 默认时应用程序的设计方案是要部署到两台机器上,一台是应用程序服务器,一台是数据库服务器,但是应该能够扩展在其他部署模型下工作。 应用程序应该支持多个数据库供应商。 这里我们选择了 Microsoft SQL Server 和Oracle。
• 应用程序应该容易维护,这是通过应用程序中的代码行数来衡量的。
返回页首
应用程序数据模型
.NET Pet Shop 中使用的数据库架构是直接从 Java Pet Store 移植而来的。 Java Pet Store 支持几种数据库供应商格式,因此我们选取了 Sybase 应用程序的架构,并在一个 Microsoft SQL Server 2000 实例中创建。 这不需要改变 Sybase 版本的架构。 而创建 Oracle 版本的 .NET Pet Shop 时,我们直接采用了 Java Pet Store 数据库原来的 Oracle 实现。
数据库 有如下整体表结构,参见表 1:
表 1. Pet Shop 中的数据库表
表名 用途
Account
代表基本客户信息
BannerData
存储广告条信息
Category
目录类别( Fish, Dogs, Cats 等)
Inventory
产品库存状态
Item
各个产品的细节
LineItem
定单细节
Orders
客户下的定单。 定单包括一个或多个行项目
OrderStatus
定单状态
Product
目录产品,每个产品可有一或多类型(项目)。 通常类型可能是雄或雌。
Profile
客户的用户配置情况
Signon
客户登录表
Supplier
有关供应商信息
在 .NET Pet Shop 版本 2 中,应用程序改为要创建一个方案,其中完成定单处理必须使用分布式事务。 为了适应分布式事务方案,Orders、OrderStatus 和 LineItem 表都分到不同的可能安装在不同机器上的数据库实例。 我们在 .NET Pet Shop 的第三版中保持了这个分布式设计模式。
Pet Shop 表的设计可以做什么更改?
应用程序中使用的架构可以做一些更改;然而,这些更改并不是为了与 Java Pet Store 参考实现提供的架构一致。 这些更改已列于表 2 中:
表 2. Pet Shop架构中可能的改进
更改 原因
表中不存储 HTML
因为可能要使用不同客户端应用程序类型,数据库仅存储图像文件名而不是图像标记,部署客户端类型可以更灵活。
对客户密码使用单向加密算法
帮助使应用程序更安全,因为即使系统任何部分的安全受到威胁,读取密码仍然很困难。 这是要求设施必须重置密码为新值。
加密信用卡信息
在系统安全受到威胁时防治对信用卡信息的访问。 做出的其他更改还有将表设为只能允许通过一个存储过程进行写入访问;因此黑客访问数据库将不得不需要另一套凭据来读取数据。
返回页首
.NET Pet Shop 2.0 体系结构
.NET Pet Shop 2.0 被设计成部署在物理上两层的部署环境中,并且在应用程序的一些部分的实现中利用了这一事实。 应用程序由以下部分构成:一个用 ASP.NET Web 窗体 (用“代码隐藏”将应用程序 HTML 和用户接口代码分离)创建的 Web 层。 一个包含控制应用程序逻辑的业务组件(通过自定义版本的 Microsoft 数据库访问应用程序块 (DAAB) 与 SQL Server 数据库通信)的中间层。 为了支持分布式事务,一些中间层业务组件是用企业服务实现的。 对于 Microsoft .NET,这是一种支持分布式事务的建议方式。 然而,并非所有类都要扩展 ServicedComponent 类,因为将所有类都实现为企业服务的组件是有性能开销的。 发布第一次 Middleware 应用程序服务器基准测试中使用的 .NET Pet Shop 2.0 时,我们收到了许多反馈,认为体系结构应该优化,以更适应于大规模的企业。 反馈比较集中的方面包括:
• 创建完全抽象的数据层,无需在中间层导入数据特定的类。
• 为 Oracle 实现数据访问层,可以透明地使用与 SQL Server 版本一样的业务层和 UI 层。
• 将 Web 会话状态从业务逻辑层和数据层中完全提取出来,这样应用程序的后端组件可以在物理上分布到 Web 服务器之外的其他计算机上,或者从其他类型的客户端比如基于 Windows的客户端和基于 Web– 的客户端重用。
• 将应用程序模块整个分为多个命名空间和物理程序集。
• 其他各方面的反馈。
返回页首
.NET Pet Shop 3.0 体系结构
应用领域
表 3. Pet Shop 解决方案中的应用领域
范围 用途 .NET 实现
用户接口组件
捕获来自用户的数据输入,显示后端系统返回的数据。 它们还处理简单的定位。 参见用户接口组件。
ASP.NET Web 窗体,用户控件和服务器控件。 这些构造能够清晰地分离设计者的 HTML 和 UI 代码比如按钮的事件处理程序。
用户接口处理
用后端业务对象控制用户定位和处理流程。 还要处理用户会话数据的管理。 参考用户处理组件。
这些是用 C# 类实现的。 会话状态管理由 ASP.NET 处理。
业务组件
实现应用程序业务逻辑的组件
这些是用 C# 类实现的
业务实体
在应用程序各层之间传递数据的瘦数据类。 参见业务实体组件。
这些是用 C# 类实现的,每个字段都以属性的形式公开。 每个类都标记为“serializable”,启用进程间传输。
数据访问层组件
处理与后端数据存储区的交互,包括数据库、消息处理系统等。
这些组件处理与后端数据存储区的交互,包括数据库、消息处理系统等,是用四个 C# 项目实现的:
• 一组接口类,要公开的每个数据访问方法都有。
• SQL Server 接口的实现。
• Oracle 9i 接口的实现。
• 一组加载正确实现的工厂类, SQL Server 或 Oracle。
Microsoft Visual Studio .NET 解决方案
图 9 显示了 Microsoft Visual Studio .NET 解决方案对 .NET Pet Shop 应用程序的布局。 应用程序的每个元素或层都有自己的项目,这样解决方案可以管理和清晰地定义应用程序中使用的新类应该放在哪里,旧类又可以在哪里找到。
表 4 中列出了每个项目的目的:
表 4. Pet Shop 解决方案中的 Visual Studio 项目
项目 用途
BLL
业务逻辑组件存放之处
ConfigTool
用来加密连接字符串和创建事件日志源的管理应用程序
DALFactory
用来确定加载哪一个数据库访问程序集的类
IDAL
每个 DAL 实现都要实现的一组接口
Model
瘦数据类或业务实体
OracleDAL
Oracle 特定的 Pet Shop DAL 实现,使用了 IDAL 接口
Post-Build
运行编译后操作的项目,比如将程序集添加到 GAC 或 COM+
Pre-Build
将程序集从 GAC 删除或从 COM+ 注销程序集的项目
SQLServerDAL
Microsoft SQL Server 特定的 Pet Shop DAL 实现,使用了 IDAL 接口
Utility
一组帮助器类,包括 DPAPI 的包装
Web
Web 页和控件
Solution Items
用来构建应用程序的杂项,比如用来签署应用程序程序集的 Pet Shop.snk 密钥文件
数据库可移植性
这一版本 Microsoft .NET Pet Shop 的关键需求之一是提供支持 Oracle 和 SQL Server 数据库的应用程序实现。 在设计应用程序的数据库访问机制时,我们可以选择应该使用哪一个数据库提供程序;可以使用通用的 OLE-DB 托管提供程序或者数据库特定的优化了性能的 .NET 托管提供程序,比如 .NET 框架1.1 中提供的 SQL Server 和 Oracle 托管提供程序。 应用程序的关键需求之一是创建一个高性能的解决方案,因此我们选择用数据库本身的 .NET 托管提供程序构建应用程序。 关于托管提供程序和通用 OLE-DB 提供程序之间的性能差异分析,读者可以参考 Using .NET Framework Data Provider for Oracle to Improve .NET Application Performance,该文档说明了厂商特定的提供程序能够比等价的 OLE-DB 提供程序性能好两到三倍。 在选择数据库特定的访问类时进行的考虑是,我们需要为每个要支持的数据库平台写一个单独的数据访问层,因此应用程序将包含更多代码。 虽然两个数据访问层共享很多公共代码,但还是要明显地分别针对具体数据库(Oracle 或 SQL Server 2000)。
为了简化数据库访问类的使用,我们选择 GoF (译注:指 Erich Gamma 等著《设计模式》一书)概述的工厂设计模式,通过反射动态在运行时加载正确的数据访问对象。 工厂设计模式是这样实现的: 创建一个 C# 接口,其中对于数据库访问类必须公开的每个方法都要声明一个方法。 对于每一个要支持的数据库,都创建一个实现数据库特定代码的具体类,以执行接口也称“协定”中的每一项操作。 为了支持运行时确定加载哪一个具体类,需要创建第三个类,也就是工厂类,它从配置文件中读入一个值以确定应该使用反射加载哪一个程序集。 通过 .NET 的反射命名空间,可以加载某个特定程序集并用该程序集创建某个对象的实例。 为了使应用程序更安全,为版本控制提供更好的支持,我们可以在应用程序配置文件(也就是这里的 web.config. )中添加要加载的程序集文件的“证据”,这意味着 .NET 框架将只加载我们在编译期间签过名而且有正确版本号的程序集。 图 10 说明了业务逻辑类、工厂类和数据库访问类是如何相互操作的。 这一创建的解决方案最重要的优势是数据库访问类可以在业务逻辑类之后编译,只要数据访问类实现了 IDAL 接口。 这意味着,如果要创建应用程序的 DB2 版本,我们不需要改动业务逻辑层(或者 UI 层)。 创建 DB2 兼容版本的步骤如下:
1.创建 DB2 的数据库访问类,它应该实现 IDAL 接口。
2.将 DB2 访问类编译成一个程序集。
3.测试和部署新的数据程序集到一台运行中的服务器上。
4.更改配置文件,指向新的数据库访问类。
无需更改或重新编译业务逻辑组件。
存储过程
通常我们都建议客户使用存储过程来访问数据库中的表。 原因如下:
• 存储过程提供了封装查询的一种简洁机制。
• 修改查询可以在不改变数据访问代码的情况下进行。
• DBA 可以很容易地看到正在执行什么 SQL 语句。
• 存储过程一般更安全,对数据库访问的控制也更容易。
• 使用存储过程,可通过在存储过程中发送多个请求,避免与客户端的多次往返行程。
• 存储过程与中间层生成的 SQL 相比,通常能提供最佳性能。
• 存储过程提供了极好的封装 XML 查询和 XML 输入参数的方式。
存储过程的缺点在于,它们往往是专有的,不能跨平台移植。
然而,要想最大程度地利用在数据库软件和硬件上已经花费的投资,开发人员往往对应用程序中使用的 SQL 针对具体数据库引擎进行优化,无论 SQL 是在存储过程中还是在中间层生成的。 这一点有一个很好的例子,就是唯一编号或者标识编号的生成,因为所有数据库执行此操作时都支持自己的特殊机制,所以用来生成唯一编号的 SQL 就往往是特定于所用数据库的。 一般总是有替代方案的,但是它们的执行速度都比不上专有解决方案。
对于 .NET Pet Shop,我们有意识地没有在应用程序使用存储过程,因为这在 Middleware 基准测试中会被看作是 .NET 解决方案一种不太公平的优势。 实际上,这方面的性能差异很小,因为应用程序相对比较简单,大多数 SQL 语句的执行计划都缓存在数据库中了。 但是, Middleware 基准测试规范不允许使用存储过程,哪怕只是包装简单的 SQL 语句,因此 .NET Pet Shop 3.0 没有使用存储过程。
缓存
最有效的提高数据库驱动的应用程序性能的方式,是避免对每次请求都访问数据库。 ASP.NET 提供了各种缓存机制以提高大多数应用程序中的性能。 ASP.NET 中使用缓存的两种主要方式是输出缓存和数据缓存。
.NET 缓存选项
页面级输出缓存接收来自 ASP.NET Web 页的响应,并将整个页面存入缓存中。 页面缓存被设计成工作在 Web 层和中间层之间,缓存中间层方法的结果/数据,或在两层应用程序中缓存数据库调用结果。 第一版的 .NET Pet Shop 同时提供了一个页面级输出缓存版本和一个非缓存版本。 第三版只支持数据缓存,但是可以很容易地改为支持输出缓存。 对于 Windows Server 2003 和 IIS 6.0,有些输出缓存的页面(那些 VaryByParm="none" 而且有 Cache 'Anywhere' 指令的)还能在内核级进行缓存,Internet 客户端访问就更快了。 无论如何,任何输出缓存的页面(内核或者非内核缓存)Windows 应用程序服务器都可以在资源 (CPU) 消耗少得多的情况下,极快速地进行服务,因为实际上重新创建页面无需进行处理。
ASP.NET 输出缓存
最早的 .NET Pet Shop 应用程序,版本 1.5,还使用了一种页面级输出缓存的变种,也就是部分页面缓存或称片段缓存来缓存页面的不同区域。 例如,缓存每个页面顶部的头信息。 然而,头信息取决于正在登录的用户(因此两个不同版本的页面都要缓存)。 ASP.NET 很容易允许这种操作,使用‘OutputCache’指令中的 VaryByCustom 属性即可。 使用 VaryByCustom 需要重写 GetVaryByCustomString 方法以获取头信息的自定义缓存。
ASP.NET 数据和对象缓存
对象缓存(缓存 API)允许使用 .NET 框架在内部缓存引擎中存储方法调用或者数据库查询的结果。 由于已经深入了应用程序工作管道,数据缓存可能无法像输出缓存那样提供同样的性能提升,因为对每个请求仍然必须动态构造 HTML 页面。 但是,通过在中间层存储非易失数据,已经在完全动态的页面和减少数据库负载之间取得了很好的折衷。 例如,要在两个 Web 页中以不同方式显示同样的数据,或者在同一个应用程序不同页面中以不同方式使用已经缓存的对象或数据。
ASP.NET 缓存监视
监视 ASP.NET 缓存系统中发生了什么,有几种方式。 首要的方法是使用 Perfmon,但是还可以使用 SQL Server 跟踪检查访问数据库的时间,这根据具体情况而定。 为了在 Perfmon 中监视缓存,ASP.NET Application 性能对象下选择 Output Cache Entries 和 Cache API Entries 计数器,将它们添加到 Perform 图。 还可以在 Perfmon 中监视周转率和点击率,检查缓存中是否使用了某个项目。
表 5. .NET 缓存选项摘要
缓存类型 优点 限制
输出缓存
提供了最佳性能
整个页面输出都缓存
片段缓存
(缓存用户控件)
实现很简单。 整个页面输出都缓存
有时间失效限制
缓存 API
页面中不同用户控件可以有不同的缓存超时
缓存控件可以跨页面共享。 需要分别缓存每个用户控件
Pet Shop Middleware 基准测试缓存规则
Middleware 公司为基准测试应用程序定义了严格规则:什么能够缓存,怎样缓存。 大体而言,页面级输出缓存是禁止的,但是应用程序中的一些地方允许中间层数据缓存。 以下数据允许缓存,不需要每次请求都从数据库刷新: 类别信息,产品信息,项目信息(库存数据除外)和某些帐户信息。 对于库存数据,无论何时项目添加到购物车或者用户定位到‘ItemDetails’页面时,库存中的当前量应该反映最新值。 对于帐户信息,用户名和密码对于每一个登录企图都要进行验证,用作计帐信息的地址也应该始终从数据库刷新,以确保它在结帐处理中是最新的。 这里也就意味着,可以使用设置了给定期限的数据/对象缓存将大多数产品搜索和浏览操作的结果缓存在中间层中。
基准测试的第二条规则是不应该允许页面级输出缓存;应始终要求 Web 服务器重新为页面创建要呈现的 HTML。 这是为了测试应用程序服务器生成动态页面的能力,而不是测试服务器从缓存拖出 HTML 有多快。
Pet Shop 3.0 缓存实现
对于这一版本的应用程序,我们使用 ASPX 页面后代码中的数据缓存,来缓存中间层(业务逻辑层)请求的结果。 以下示例代码说明了如何访问 ASP.NET 缓存 API 从缓存检索信息。 该示例基于 Pet Shop 应用程序中的类别查询代码,category.aspx.cs。 在 Pet Shop 中,用户可以在五个预定义宠物类别中选择一个,查看该类别中的动物列表。 代码所做的第一件事是检查数据是否已经缓存,这是通过用类别 id 作为键查找数据缓存中的一个元素来实现的。 如果这个元素不存在,要么是因为数据还没有缓存,要么是当前缓存已经过期,将返回 null。 如果元素存在,就从缓存中拖出数据,并转换为合适的类型。 如果数据不在缓存中,调用中间层查询数据。 中间层查询的结果然后添加到缓存中,期限为从现在开始 12 小时。 或者,可以根据固定时间、对另一缓存项目的依赖,或者通过提供一个可以用来清除缓存的回调函数使缓存过期。
// Get the category from the query string
string categoryKey =
WebComponents.CleanString.InputText(Request["categoryId"],50);
// Check to see if the contents are in the Data Cache
if(Cache[categoryKey] != null){
// If the data is already cached, then used the cached copy
products.DataSource = (IList)Cache[categoryKey];
}else{
// If the data is not cached,
// then create a new products object and request the data
Product product = new Product();
IList productsByCategory = product.GetProductsByCategory(categoryKey);
// Store the results of the call in the Cache
// and set the time out to 12 hours
Cache.Add(categoryKey, productsByCategory, null, DateTime.Now.AddHours(12), Cache.NoSlidingExpiration , CacheItemPriority.High, null);
products.DataSource = productsByCategory;
}
// Bind the data to the control
products.DataBind();
返回页首
小结
.NET Pet Shop 的体系结构已经进行了改进,能够在部署选择方面提供更灵活的解决方案,应用程序能够更加容易地进行自定义,适应业务模型中的变化。 尽管有所有这些变化,Pet Shop 的性能与 2.0 版实现大致相同,说明了 Microsoft .NET 框架在 J2EE 之外提供了另一种可行的企业级解决方案。 通过 .NET Pet Shop 和 J2EE Pet Store,架构师和开发人员可以使用功能完全一样、说明了各自平台上最佳编程实践的参考应用程序,一对一地比较 .NET 和 J2EE。 即将进行的 Middleware 基准测试将测试新的 .NET Pet Shop 3.0 实现,将比较它与两个新 J2EE 实现——一个基于 CMP 2.0,另一个基于纯粹的 JSP/Servlet 体系结构(不使用 EJB)的性能。 测试将由 Middleware 公司进行,在运行着各种 J2EE 应用程序服务器的服务器端发布。 将组成一个 J2EE 专家评委会监督这一基准测试,以确保对被测试产品的公平性以及所有实现都符合规范和最佳准则。 此外,已经邀请了主要的 J2EE 应用程序服务器供应商,加上 Microsoft 来参与这一基准测试,他们将对规范进行评价,提供供测试的实现,并现场进行微调和优化,参与测试过程。 Microsoft 选择全面参与第二轮测试。 可以到这里 了解 Middleware Application Server 测试第一轮的细节,测试基于两个基准测试应用程序:早期的 .NET Pet Shop 2.0 和一个基于 BMP 的 J2EE 实现,或者下载完整的报告。
返回页首
附录 A: 从版本 2 到版本 3 的更改
项目 说明 原因
1
创建数据库访问层 (DAL)。
可以将业务逻辑从数据库访问代码中完全分离出来,因此可以无需更改业务逻辑代码,即可更换数据库供应商。
2
为所有瘦数据类(模型)创建公共的项目。
模型项目只包含保存数据的瘦类,可以用在应用程序的每一层,作为传输数据的容器。所有模型类都通过 [Serializable] 标记支持序列化,以添加对群集和任何未来物理部署中变化的增强支持。
3
在 Components 项目中删除对 System.Web 的引用。
这是一种好的设计实践,因为可以使中间层组件用于不同类型的 UI。
4
将一些自定义服务器控件改为 Web 用户控件;然而 Pager 控件仍然保留为自定义服务器控件。
标头和标题控件含有许多 HTML/UI 内容,因此如果实现为 Web 用户控件更易维护。 Pager 控件处理的是查询字符串的操作,视图状态中数据的使用,因此作为自定义服务器控件更合适。
5
为 Oracle 创建了 DAL 实现。
为了支持 Oracle 数据库,创建了特定的 Oracle DAL,可以使用 Oracle 特定的驱动程序
6
DAL 层现在实现了工厂模式 [GoF] 以加载相应的供应商特定的 DAL。
为了隐藏后端所使用的数据库,使用工厂方法将接口返回到要用到的 DAL 层。
7
模型类中的所有公共字段都转换为属性。
这样字段的存储机制就可以隐藏起来,如果要看什么函数修改了某些数据,这提供了一个很好的在代码中添加断点的地方。
8
创建数据库访问层 (DAL)。
可以将业务逻辑从数据库访问代码中完全分离出来,因此可以无需更改业务逻辑代码,即可更换数据库供应商。
9
将所有静态方法改为实例方法。
根据评论反馈进行的修改
10
将 assemblyinfo.cs 中的版本号改为与部署匹配的具体版本。
在 DALFactory 对象中加载程序集时,允许指定证据。
11
在 Visual Studio .NET 解决方案中添加构建前和构建后的步骤。
这样,在项目编译后,就可以对具体程序集运行 gacutil 和 regsvcs 实用工具。
12
将订购业务组件改名,根据其执行的功能而非实现机制。
对其他开发人员而言理解组件的功能更直观。
13
创建配置工具辅助应用程序的设置。
需要以管理员组成员的身份创建应用程序事件日志源。 确保完成最简单的方式是提供一个简单的工具,可以在部署后运行。
14
将代码修改为允许帐号和产品数据库以及定单数据库使用不同的 DAL。
对用户请求的响应应该能够使用混合数据库部署模型。
15
页面中添加页面输出缓存;但是基准测试时要删除。
有用户请求说明如何重写 VaryByCustom 函数使标题和标头控件等页面能够在 default.aspx 这样的页面中缓存。
16
将处理流程控件类添加到 Web 应用程序中。
根据评论反馈做的修改,为帐号或购物车这样的 Web 区域提供单一位置来控制定位和状态管理。
最初研究 .NET Pet Shop 的目的是用 Microsoft .NET 实现 Sun 主要的 J2EE 蓝图应用程序 Sun Java Pet Store 同样的应用程序功能。 根据用 .NET 实现的 Sun J2EE 最佳实践示例应用程序,各方面的客户可以直接地对 Microsoft 的 .NET 技术与基于 J2EE 的应用程序服务器进行比较,同时了解构建基于Web的应用程序中用到的各种建议的设计模式之间的异同。 .NET Pet Shop 应用程序现在已经是第三版了,旨在显示构建企业级 n 层应用程序(可能需要支持多种数据库平台和部署模型)的 .NET 最佳实践。 根据社区对 .Net Pet Shop 2.0 的反馈,.NET Pet Shop 3.0 遵照 MSDN 上发布的 Microsoft《说明性体系结构指导》进行了重新设计。 第三版还完全符合了 Middleware 公司的应用程序服务器基准测试规范,将作为 Microsoft 参加今年春天即将进行的 Middleware Application Server Benchmark 的产品: 这是Middleware 公司举办的第二轮测试活动,旨在比较 .NET 和 J2EE 平台在构建和承载企业级 Web 应用程序方面的可伸缩性。
返回页首
Java Pet Store 是 什么?
Java Pet Store 是按 Sun 公司维护的 J2EE 蓝图开发的分布式应用程序的一个参考实现。 示例应用程序最初的开发目的是帮助开发人员和架构师理解如何使用和利用 J2EE 技术,以及各个 J2EE 平台组件是如何配合的。 Java Pet Store 演示软件包括构建应用程序所需的Enterprise Java Beans (EJB) 体系结构、 Java Server Pages (JSP) 技术、标记库和 servlet 的完整的源代码及文档。 此外, Java Pet Store 蓝图应用程序还通过具体示例说明了一些模型和设计模式。
完整的 Java Pet Store 包括三个示例应用程序:
• Java Pet Store: J2EE 蓝图主应用程序。
• Java Pet Store 管理器: Java Pet Store 的管理器模块
• Blueprints Mailer: 在小一些的包中给出一些 J2EE 蓝图设计指南的一个小应用程序。
Java Pet Store 的最初版本旨在处理以下数据库: Oracle、Sybase 和Cloudscape 。 IBM 已经开发了一个 DB2 版本的应用程序。 该应用程序可以从 Java 2 Platform Enterprise Edition Blueprints 公开获得。 主应用程序 Java Pet Store 是一个电子商务应用程序,可以通过它在线购买宠物。 启动应用程序后,可以浏览和搜索各种类型的宠物,从狗到爬行动物。
使用 Java Pet Store 的典型会话方案如下:
主页 — 这是用户第一次启动应用程序时加载的主页。
类别查看 — 有五大类: 鱼、狗、爬行动物、猫和鸟。 每一类都有几个相关的产品。 如果选择鱼作为类别,可以看到天使鱼等等内容。
产品 — 如果现在选择一个产品,应用程序将显示产品的所有类型。 通常产品类型是雄或者雌。
产品详情 — 每种产品类型(分别用不同项目表示)有详细的视图显示产品说明、产品图像、价格和库存数量。
购物车 — 用户可以通过它操作购物车(添加、删除和更新行项目)。
结帐 — 结帐页面以只读视图显示购物车。
登录重定向 — 当用户选择结帐页面上的“Continue”时,如果还没有登录,将重定向到登录页面。
登录验证 — 通过站点的身份验证以后,用户被重定向到信用卡和记帐地址表单。
定单确认 — 显示记帐地址和送货地址。
定单提交 — 这是定单处理流程的最后一步。 定单现在将提交到数据库。
Microsoft .NET Pet Shop
.NET Pet Shop 的目标是把注意力仅仅放在 Java Pet Store 上(管理和Mailer 组件没有在 .NET 中实现)。 除了重现 Java Pet Store 应用程序的功能之外,还增加了两项目标:
• 比较 .NET 和 J2EE 通过最佳实践实现的真实应用程序中代码和代码大小上的异同。
• 提供用 .NET 和 J2EE 实现的典型的设计良好的应用程序能够支持多少用户的数据。
.NET Pet Shop 的整体逻辑体系结构如图 3 所示,设计的中心是在表示层使用 ASP.NET Web 窗体,与逻辑中间层中的 C# 业务组件通信。 业务组件继而通过 ADO.NET 和 SQL Server 名为数据访问应用块 (DAAB) (可以从此链接了解更多 DAAB 信息并下载完整的 DAAB 源代码)的帮助器类访问后端数据库。 数据访问功能完全抽象到数据访问层 (DAL) 中,与业务逻辑层 (BLL) 相分离。 .NET Pet Shop 3.0 中的新颖之处在于,我们为 Oracle 9i 和 SQL Server 2000 数据库都引入了 DAL 层。 相应 DAL 层的类加载将根据 Web.Config 中的应用程序配置设置在运行时动态生成。注意 .NET Pet Shop 3.0 使用了两个后端数据库,定单处理中要涉及跨两个数据库的分布式事务。 使用简单的 Web.Config 应用程序设置,用户可以对 .Net Pet Shop 进行部署,使用一个或者多个后端数据库,还可以自由地将 SQL Server 和 Oracle 后端数据库与由 .NET 服务的组件通过 COM+ 企业服务处理的分布式事务混合。
图 4 说明了 Microsoft .NET Pet Shop 物理上是怎样部署的。 这里使用网络负载平衡 (NLB) 或者可能是硬件实现的负载平衡技术将入站的网络通信量分到了两台应用程序服务器上。 在网络请求达到群集中的一台机器时,针对该请求的所有工作都会在这台特定机器上进行。 业务逻辑和数据访问组件将以程序集的形式安装在两台服务器上,它们本质上是完全相同的。 如果负载平衡软件配置为使用“Sticky IP”,则每台服务器都有自己的会话-状态存储,因为要保证第二个请求返回到实现第一个请求的那台服务器。 如果解决方案所需的容错要求更高,两台应用程序服务器可以共享一个公共会话-状态存储比如 SQL Server 或者一台专用的会话服务器(图中没有显示)。 会话-状态存储的类型和位置由每个站点‘web.config’文件里‘system.web’元素‘sessionState’子节点中的值决定。
业务需求
作为 Pet Shop 3 体系结构文档的一部分,我们给出了 .NET Pet Shop 的业务需求,这样开发人员和客户就可以理解我们在做应用程序的设计决策时进行的一些选择。
Pet Shop 应用程序的功能性需求是什么?
• 应用程序应该使客户能够按类和通过关键字搜索浏览公司目录。
• 应用程序应该为客户提供一种通过一个购物车模型就能购买多个商品项的机制。
• 应用程序应该提供简单的安全模型,这样客户必须先进行登录,才允许购买购物车的内容。
• 应用程序旨在支持高容量的企业级电子商务解决方案;因此应用程序应该展示以下方面:
• 高性能,通过所支持用户数和用户响应时间进行衡量
• 通过增加更多处理器来扩展的能力
• 通过增加更多机器组成群集的分布式扩展能力
• 在大型企业级系统中,应用程序可能需要访问多个数据库,因此应用程序应该支持分布式事务。
• 应用程序应该考虑灵活的部署策略。 默认时应用程序的设计方案是要部署到两台机器上,一台是应用程序服务器,一台是数据库服务器,但是应该能够扩展在其他部署模型下工作。 应用程序应该支持多个数据库供应商。 这里我们选择了 Microsoft SQL Server 和Oracle。
• 应用程序应该容易维护,这是通过应用程序中的代码行数来衡量的。
返回页首
应用程序数据模型
.NET Pet Shop 中使用的数据库架构是直接从 Java Pet Store 移植而来的。 Java Pet Store 支持几种数据库供应商格式,因此我们选取了 Sybase 应用程序的架构,并在一个 Microsoft SQL Server 2000 实例中创建。 这不需要改变 Sybase 版本的架构。 而创建 Oracle 版本的 .NET Pet Shop 时,我们直接采用了 Java Pet Store 数据库原来的 Oracle 实现。
数据库 有如下整体表结构,参见表 1:
表 1. Pet Shop 中的数据库表
表名 用途
Account
代表基本客户信息
BannerData
存储广告条信息
Category
目录类别( Fish, Dogs, Cats 等)
Inventory
产品库存状态
Item
各个产品的细节
LineItem
定单细节
Orders
客户下的定单。 定单包括一个或多个行项目
OrderStatus
定单状态
Product
目录产品,每个产品可有一或多类型(项目)。 通常类型可能是雄或雌。
Profile
客户的用户配置情况
Signon
客户登录表
Supplier
有关供应商信息
在 .NET Pet Shop 版本 2 中,应用程序改为要创建一个方案,其中完成定单处理必须使用分布式事务。 为了适应分布式事务方案,Orders、OrderStatus 和 LineItem 表都分到不同的可能安装在不同机器上的数据库实例。 我们在 .NET Pet Shop 的第三版中保持了这个分布式设计模式。
Pet Shop 表的设计可以做什么更改?
应用程序中使用的架构可以做一些更改;然而,这些更改并不是为了与 Java Pet Store 参考实现提供的架构一致。 这些更改已列于表 2 中:
表 2. Pet Shop架构中可能的改进
更改 原因
表中不存储 HTML
因为可能要使用不同客户端应用程序类型,数据库仅存储图像文件名而不是图像标记,部署客户端类型可以更灵活。
对客户密码使用单向加密算法
帮助使应用程序更安全,因为即使系统任何部分的安全受到威胁,读取密码仍然很困难。 这是要求设施必须重置密码为新值。
加密信用卡信息
在系统安全受到威胁时防治对信用卡信息的访问。 做出的其他更改还有将表设为只能允许通过一个存储过程进行写入访问;因此黑客访问数据库将不得不需要另一套凭据来读取数据。
返回页首
.NET Pet Shop 2.0 体系结构
.NET Pet Shop 2.0 被设计成部署在物理上两层的部署环境中,并且在应用程序的一些部分的实现中利用了这一事实。 应用程序由以下部分构成:一个用 ASP.NET Web 窗体 (用“代码隐藏”将应用程序 HTML 和用户接口代码分离)创建的 Web 层。 一个包含控制应用程序逻辑的业务组件(通过自定义版本的 Microsoft 数据库访问应用程序块 (DAAB) 与 SQL Server 数据库通信)的中间层。 为了支持分布式事务,一些中间层业务组件是用企业服务实现的。 对于 Microsoft .NET,这是一种支持分布式事务的建议方式。 然而,并非所有类都要扩展 ServicedComponent 类,因为将所有类都实现为企业服务的组件是有性能开销的。 发布第一次 Middleware 应用程序服务器基准测试中使用的 .NET Pet Shop 2.0 时,我们收到了许多反馈,认为体系结构应该优化,以更适应于大规模的企业。 反馈比较集中的方面包括:
• 创建完全抽象的数据层,无需在中间层导入数据特定的类。
• 为 Oracle 实现数据访问层,可以透明地使用与 SQL Server 版本一样的业务层和 UI 层。
• 将 Web 会话状态从业务逻辑层和数据层中完全提取出来,这样应用程序的后端组件可以在物理上分布到 Web 服务器之外的其他计算机上,或者从其他类型的客户端比如基于 Windows的客户端和基于 Web– 的客户端重用。
• 将应用程序模块整个分为多个命名空间和物理程序集。
• 其他各方面的反馈。
返回页首
.NET Pet Shop 3.0 体系结构
应用领域
表 3. Pet Shop 解决方案中的应用领域
范围 用途 .NET 实现
用户接口组件
捕获来自用户的数据输入,显示后端系统返回的数据。 它们还处理简单的定位。 参见用户接口组件。
ASP.NET Web 窗体,用户控件和服务器控件。 这些构造能够清晰地分离设计者的 HTML 和 UI 代码比如按钮的事件处理程序。
用户接口处理
用后端业务对象控制用户定位和处理流程。 还要处理用户会话数据的管理。 参考用户处理组件。
这些是用 C# 类实现的。 会话状态管理由 ASP.NET 处理。
业务组件
实现应用程序业务逻辑的组件
这些是用 C# 类实现的
业务实体
在应用程序各层之间传递数据的瘦数据类。 参见业务实体组件。
这些是用 C# 类实现的,每个字段都以属性的形式公开。 每个类都标记为“serializable”,启用进程间传输。
数据访问层组件
处理与后端数据存储区的交互,包括数据库、消息处理系统等。
这些组件处理与后端数据存储区的交互,包括数据库、消息处理系统等,是用四个 C# 项目实现的:
• 一组接口类,要公开的每个数据访问方法都有。
• SQL Server 接口的实现。
• Oracle 9i 接口的实现。
• 一组加载正确实现的工厂类, SQL Server 或 Oracle。
Microsoft Visual Studio .NET 解决方案
图 9 显示了 Microsoft Visual Studio .NET 解决方案对 .NET Pet Shop 应用程序的布局。 应用程序的每个元素或层都有自己的项目,这样解决方案可以管理和清晰地定义应用程序中使用的新类应该放在哪里,旧类又可以在哪里找到。
表 4 中列出了每个项目的目的:
表 4. Pet Shop 解决方案中的 Visual Studio 项目
项目 用途
BLL
业务逻辑组件存放之处
ConfigTool
用来加密连接字符串和创建事件日志源的管理应用程序
DALFactory
用来确定加载哪一个数据库访问程序集的类
IDAL
每个 DAL 实现都要实现的一组接口
Model
瘦数据类或业务实体
OracleDAL
Oracle 特定的 Pet Shop DAL 实现,使用了 IDAL 接口
Post-Build
运行编译后操作的项目,比如将程序集添加到 GAC 或 COM+
Pre-Build
将程序集从 GAC 删除或从 COM+ 注销程序集的项目
SQLServerDAL
Microsoft SQL Server 特定的 Pet Shop DAL 实现,使用了 IDAL 接口
Utility
一组帮助器类,包括 DPAPI 的包装
Web
Web 页和控件
Solution Items
用来构建应用程序的杂项,比如用来签署应用程序程序集的 Pet Shop.snk 密钥文件
数据库可移植性
这一版本 Microsoft .NET Pet Shop 的关键需求之一是提供支持 Oracle 和 SQL Server 数据库的应用程序实现。 在设计应用程序的数据库访问机制时,我们可以选择应该使用哪一个数据库提供程序;可以使用通用的 OLE-DB 托管提供程序或者数据库特定的优化了性能的 .NET 托管提供程序,比如 .NET 框架1.1 中提供的 SQL Server 和 Oracle 托管提供程序。 应用程序的关键需求之一是创建一个高性能的解决方案,因此我们选择用数据库本身的 .NET 托管提供程序构建应用程序。 关于托管提供程序和通用 OLE-DB 提供程序之间的性能差异分析,读者可以参考 Using .NET Framework Data Provider for Oracle to Improve .NET Application Performance,该文档说明了厂商特定的提供程序能够比等价的 OLE-DB 提供程序性能好两到三倍。 在选择数据库特定的访问类时进行的考虑是,我们需要为每个要支持的数据库平台写一个单独的数据访问层,因此应用程序将包含更多代码。 虽然两个数据访问层共享很多公共代码,但还是要明显地分别针对具体数据库(Oracle 或 SQL Server 2000)。
为了简化数据库访问类的使用,我们选择 GoF (译注:指 Erich Gamma 等著《设计模式》一书)概述的工厂设计模式,通过反射动态在运行时加载正确的数据访问对象。 工厂设计模式是这样实现的: 创建一个 C# 接口,其中对于数据库访问类必须公开的每个方法都要声明一个方法。 对于每一个要支持的数据库,都创建一个实现数据库特定代码的具体类,以执行接口也称“协定”中的每一项操作。 为了支持运行时确定加载哪一个具体类,需要创建第三个类,也就是工厂类,它从配置文件中读入一个值以确定应该使用反射加载哪一个程序集。 通过 .NET 的反射命名空间,可以加载某个特定程序集并用该程序集创建某个对象的实例。 为了使应用程序更安全,为版本控制提供更好的支持,我们可以在应用程序配置文件(也就是这里的 web.config. )中添加要加载的程序集文件的“证据”,这意味着 .NET 框架将只加载我们在编译期间签过名而且有正确版本号的程序集。 图 10 说明了业务逻辑类、工厂类和数据库访问类是如何相互操作的。 这一创建的解决方案最重要的优势是数据库访问类可以在业务逻辑类之后编译,只要数据访问类实现了 IDAL 接口。 这意味着,如果要创建应用程序的 DB2 版本,我们不需要改动业务逻辑层(或者 UI 层)。 创建 DB2 兼容版本的步骤如下:
1.创建 DB2 的数据库访问类,它应该实现 IDAL 接口。
2.将 DB2 访问类编译成一个程序集。
3.测试和部署新的数据程序集到一台运行中的服务器上。
4.更改配置文件,指向新的数据库访问类。
无需更改或重新编译业务逻辑组件。
存储过程
通常我们都建议客户使用存储过程来访问数据库中的表。 原因如下:
• 存储过程提供了封装查询的一种简洁机制。
• 修改查询可以在不改变数据访问代码的情况下进行。
• DBA 可以很容易地看到正在执行什么 SQL 语句。
• 存储过程一般更安全,对数据库访问的控制也更容易。
• 使用存储过程,可通过在存储过程中发送多个请求,避免与客户端的多次往返行程。
• 存储过程与中间层生成的 SQL 相比,通常能提供最佳性能。
• 存储过程提供了极好的封装 XML 查询和 XML 输入参数的方式。
存储过程的缺点在于,它们往往是专有的,不能跨平台移植。
然而,要想最大程度地利用在数据库软件和硬件上已经花费的投资,开发人员往往对应用程序中使用的 SQL 针对具体数据库引擎进行优化,无论 SQL 是在存储过程中还是在中间层生成的。 这一点有一个很好的例子,就是唯一编号或者标识编号的生成,因为所有数据库执行此操作时都支持自己的特殊机制,所以用来生成唯一编号的 SQL 就往往是特定于所用数据库的。 一般总是有替代方案的,但是它们的执行速度都比不上专有解决方案。
对于 .NET Pet Shop,我们有意识地没有在应用程序使用存储过程,因为这在 Middleware 基准测试中会被看作是 .NET 解决方案一种不太公平的优势。 实际上,这方面的性能差异很小,因为应用程序相对比较简单,大多数 SQL 语句的执行计划都缓存在数据库中了。 但是, Middleware 基准测试规范不允许使用存储过程,哪怕只是包装简单的 SQL 语句,因此 .NET Pet Shop 3.0 没有使用存储过程。
缓存
最有效的提高数据库驱动的应用程序性能的方式,是避免对每次请求都访问数据库。 ASP.NET 提供了各种缓存机制以提高大多数应用程序中的性能。 ASP.NET 中使用缓存的两种主要方式是输出缓存和数据缓存。
.NET 缓存选项
页面级输出缓存接收来自 ASP.NET Web 页的响应,并将整个页面存入缓存中。 页面缓存被设计成工作在 Web 层和中间层之间,缓存中间层方法的结果/数据,或在两层应用程序中缓存数据库调用结果。 第一版的 .NET Pet Shop 同时提供了一个页面级输出缓存版本和一个非缓存版本。 第三版只支持数据缓存,但是可以很容易地改为支持输出缓存。 对于 Windows Server 2003 和 IIS 6.0,有些输出缓存的页面(那些 VaryByParm="none" 而且有 Cache 'Anywhere' 指令的)还能在内核级进行缓存,Internet 客户端访问就更快了。 无论如何,任何输出缓存的页面(内核或者非内核缓存)Windows 应用程序服务器都可以在资源 (CPU) 消耗少得多的情况下,极快速地进行服务,因为实际上重新创建页面无需进行处理。
ASP.NET 输出缓存
最早的 .NET Pet Shop 应用程序,版本 1.5,还使用了一种页面级输出缓存的变种,也就是部分页面缓存或称片段缓存来缓存页面的不同区域。 例如,缓存每个页面顶部的头信息。 然而,头信息取决于正在登录的用户(因此两个不同版本的页面都要缓存)。 ASP.NET 很容易允许这种操作,使用‘OutputCache’指令中的 VaryByCustom 属性即可。 使用 VaryByCustom 需要重写 GetVaryByCustomString 方法以获取头信息的自定义缓存。
ASP.NET 数据和对象缓存
对象缓存(缓存 API)允许使用 .NET 框架在内部缓存引擎中存储方法调用或者数据库查询的结果。 由于已经深入了应用程序工作管道,数据缓存可能无法像输出缓存那样提供同样的性能提升,因为对每个请求仍然必须动态构造 HTML 页面。 但是,通过在中间层存储非易失数据,已经在完全动态的页面和减少数据库负载之间取得了很好的折衷。 例如,要在两个 Web 页中以不同方式显示同样的数据,或者在同一个应用程序不同页面中以不同方式使用已经缓存的对象或数据。
ASP.NET 缓存监视
监视 ASP.NET 缓存系统中发生了什么,有几种方式。 首要的方法是使用 Perfmon,但是还可以使用 SQL Server 跟踪检查访问数据库的时间,这根据具体情况而定。 为了在 Perfmon 中监视缓存,ASP.NET Application 性能对象下选择 Output Cache Entries 和 Cache API Entries 计数器,将它们添加到 Perform 图。 还可以在 Perfmon 中监视周转率和点击率,检查缓存中是否使用了某个项目。
表 5. .NET 缓存选项摘要
缓存类型 优点 限制
输出缓存
提供了最佳性能
整个页面输出都缓存
片段缓存
(缓存用户控件)
实现很简单。 整个页面输出都缓存
有时间失效限制
缓存 API
页面中不同用户控件可以有不同的缓存超时
缓存控件可以跨页面共享。 需要分别缓存每个用户控件
Pet Shop Middleware 基准测试缓存规则
Middleware 公司为基准测试应用程序定义了严格规则:什么能够缓存,怎样缓存。 大体而言,页面级输出缓存是禁止的,但是应用程序中的一些地方允许中间层数据缓存。 以下数据允许缓存,不需要每次请求都从数据库刷新: 类别信息,产品信息,项目信息(库存数据除外)和某些帐户信息。 对于库存数据,无论何时项目添加到购物车或者用户定位到‘ItemDetails’页面时,库存中的当前量应该反映最新值。 对于帐户信息,用户名和密码对于每一个登录企图都要进行验证,用作计帐信息的地址也应该始终从数据库刷新,以确保它在结帐处理中是最新的。 这里也就意味着,可以使用设置了给定期限的数据/对象缓存将大多数产品搜索和浏览操作的结果缓存在中间层中。
基准测试的第二条规则是不应该允许页面级输出缓存;应始终要求 Web 服务器重新为页面创建要呈现的 HTML。 这是为了测试应用程序服务器生成动态页面的能力,而不是测试服务器从缓存拖出 HTML 有多快。
Pet Shop 3.0 缓存实现
对于这一版本的应用程序,我们使用 ASPX 页面后代码中的数据缓存,来缓存中间层(业务逻辑层)请求的结果。 以下示例代码说明了如何访问 ASP.NET 缓存 API 从缓存检索信息。 该示例基于 Pet Shop 应用程序中的类别查询代码,category.aspx.cs。 在 Pet Shop 中,用户可以在五个预定义宠物类别中选择一个,查看该类别中的动物列表。 代码所做的第一件事是检查数据是否已经缓存,这是通过用类别 id 作为键查找数据缓存中的一个元素来实现的。 如果这个元素不存在,要么是因为数据还没有缓存,要么是当前缓存已经过期,将返回 null。 如果元素存在,就从缓存中拖出数据,并转换为合适的类型。 如果数据不在缓存中,调用中间层查询数据。 中间层查询的结果然后添加到缓存中,期限为从现在开始 12 小时。 或者,可以根据固定时间、对另一缓存项目的依赖,或者通过提供一个可以用来清除缓存的回调函数使缓存过期。
// Get the category from the query string
string categoryKey =
WebComponents.CleanString.InputText(Request["categoryId"],50);
// Check to see if the contents are in the Data Cache
if(Cache[categoryKey] != null){
// If the data is already cached, then used the cached copy
products.DataSource = (IList)Cache[categoryKey];
}else{
// If the data is not cached,
// then create a new products object and request the data
Product product = new Product();
IList productsByCategory = product.GetProductsByCategory(categoryKey);
// Store the results of the call in the Cache
// and set the time out to 12 hours
Cache.Add(categoryKey, productsByCategory, null, DateTime.Now.AddHours(12), Cache.NoSlidingExpiration , CacheItemPriority.High, null);
products.DataSource = productsByCategory;
}
// Bind the data to the control
products.DataBind();
返回页首
小结
.NET Pet Shop 的体系结构已经进行了改进,能够在部署选择方面提供更灵活的解决方案,应用程序能够更加容易地进行自定义,适应业务模型中的变化。 尽管有所有这些变化,Pet Shop 的性能与 2.0 版实现大致相同,说明了 Microsoft .NET 框架在 J2EE 之外提供了另一种可行的企业级解决方案。 通过 .NET Pet Shop 和 J2EE Pet Store,架构师和开发人员可以使用功能完全一样、说明了各自平台上最佳编程实践的参考应用程序,一对一地比较 .NET 和 J2EE。 即将进行的 Middleware 基准测试将测试新的 .NET Pet Shop 3.0 实现,将比较它与两个新 J2EE 实现——一个基于 CMP 2.0,另一个基于纯粹的 JSP/Servlet 体系结构(不使用 EJB)的性能。 测试将由 Middleware 公司进行,在运行着各种 J2EE 应用程序服务器的服务器端发布。 将组成一个 J2EE 专家评委会监督这一基准测试,以确保对被测试产品的公平性以及所有实现都符合规范和最佳准则。 此外,已经邀请了主要的 J2EE 应用程序服务器供应商,加上 Microsoft 来参与这一基准测试,他们将对规范进行评价,提供供测试的实现,并现场进行微调和优化,参与测试过程。 Microsoft 选择全面参与第二轮测试。 可以到这里 了解 Middleware Application Server 测试第一轮的细节,测试基于两个基准测试应用程序:早期的 .NET Pet Shop 2.0 和一个基于 BMP 的 J2EE 实现,或者下载完整的报告。
返回页首
附录 A: 从版本 2 到版本 3 的更改
项目 说明 原因
1
创建数据库访问层 (DAL)。
可以将业务逻辑从数据库访问代码中完全分离出来,因此可以无需更改业务逻辑代码,即可更换数据库供应商。
2
为所有瘦数据类(模型)创建公共的项目。
模型项目只包含保存数据的瘦类,可以用在应用程序的每一层,作为传输数据的容器。所有模型类都通过 [Serializable] 标记支持序列化,以添加对群集和任何未来物理部署中变化的增强支持。
3
在 Components 项目中删除对 System.Web 的引用。
这是一种好的设计实践,因为可以使中间层组件用于不同类型的 UI。
4
将一些自定义服务器控件改为 Web 用户控件;然而 Pager 控件仍然保留为自定义服务器控件。
标头和标题控件含有许多 HTML/UI 内容,因此如果实现为 Web 用户控件更易维护。 Pager 控件处理的是查询字符串的操作,视图状态中数据的使用,因此作为自定义服务器控件更合适。
5
为 Oracle 创建了 DAL 实现。
为了支持 Oracle 数据库,创建了特定的 Oracle DAL,可以使用 Oracle 特定的驱动程序
6
DAL 层现在实现了工厂模式 [GoF] 以加载相应的供应商特定的 DAL。
为了隐藏后端所使用的数据库,使用工厂方法将接口返回到要用到的 DAL 层。
7
模型类中的所有公共字段都转换为属性。
这样字段的存储机制就可以隐藏起来,如果要看什么函数修改了某些数据,这提供了一个很好的在代码中添加断点的地方。
8
创建数据库访问层 (DAL)。
可以将业务逻辑从数据库访问代码中完全分离出来,因此可以无需更改业务逻辑代码,即可更换数据库供应商。
9
将所有静态方法改为实例方法。
根据评论反馈进行的修改
10
将 assemblyinfo.cs 中的版本号改为与部署匹配的具体版本。
在 DALFactory 对象中加载程序集时,允许指定证据。
11
在 Visual Studio .NET 解决方案中添加构建前和构建后的步骤。
这样,在项目编译后,就可以对具体程序集运行 gacutil 和 regsvcs 实用工具。
12
将订购业务组件改名,根据其执行的功能而非实现机制。
对其他开发人员而言理解组件的功能更直观。
13
创建配置工具辅助应用程序的设置。
需要以管理员组成员的身份创建应用程序事件日志源。 确保完成最简单的方式是提供一个简单的工具,可以在部署后运行。
14
将代码修改为允许帐号和产品数据库以及定单数据库使用不同的 DAL。
对用户请求的响应应该能够使用混合数据库部署模型。
15
页面中添加页面输出缓存;但是基准测试时要删除。
有用户请求说明如何重写 VaryByCustom 函数使标题和标头控件等页面能够在 default.aspx 这样的页面中缓存。
16
将处理流程控件类添加到 Web 应用程序中。
根据评论反馈做的修改,为帐号或购物车这样的 Web 区域提供单一位置来控制定位和状态管理。