




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、petshop4.0 详解之一(系统架构设计)前言:PetShop 是一个范例,微软用它来展示.Net企业系统开发的能力。业界有许多.Net与J2EE之争,许多数据是从微软的PetShop和Sun的 PetStore而来。这种争论不可避免带有浓厚的商业色彩,对于我们开发人员而言,没有必要过多关注。然而PetShop随着版本的不断更新,至现在基于.Net 2.0的PetShop4.0为止,整个设计逐渐变得成熟而优雅,却又很多可以借鉴之处。PetShop是一个小型的项目,系统架构与代码都比较简单,却 也凸现了许多颇有价值的设计与开发理念。本系列试图对PetShop作一个全方位的解剖,依据的代码是P
2、etShop4.0,可以从链接 一、PetShop的系统架构设计在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或成为领域层)、表示层,如图所示:图一:三层的分层式结构数据访问层:有时候也称为是持久层,其功能主要是负责数据库的访问。简单的说法就是实现对数据表的Select,Insert,Update, Delete的操作。如果要加入ORM1 / 73的元素,那么就会包括对象和数据表之间的mapping,以及对象实体的持久化。在PetShop的数据访问层中, 并没有使用ORM,从而导致了代码量的增加,可以
3、看作是整个设计实现中的一大败笔。业务逻辑层:是整个系统的核心,它与这个系统的业务(领域)有关。以PetShop为例,业务逻辑层的相关设计,均和网上宠物店特有的逻辑相关,例如查询宠物,下订单,添加宠物到购物车等等。如果涉及到数据库的访问,则调用数据访问层。表示层:是系统的UI部分,负责使用者与整个系统的交互。在这一层中,理想的状态是不应包括系统的业务逻辑。表示层中的逻辑代码,仅与界面元素有关。在PetShop中,是利用ASP.Net来设计的,因此包含了许多Web控件和相关逻辑。分层式结构究竟其优势何在?Martin Fowler在Patterns of Enterprise Applicatio
4、n Architecture一书中给出了答案:1、开发人员可以只关注整个结构中的其中某一层;2、可以很容易的用新的实现来替换原有层次的实现;3、可以降低层与层之间的依赖;4、有利于标准化;5、利于各层逻辑的复用。概括来说,分层式设计可以达至如下目的:分散关注、松散耦合、逻辑复用、标准定义。一个好的分层式结构,可以使得开发人员的分工更加明确。一旦定义好各层次之间的接口,负责不同逻辑设计的开发人员就可以分散关注,齐头并进。例如 UI人员只需考虑用户界面的体验与操作,领域的设计人员可以仅关注业务逻辑的设计,而数据库设计人员也不必为繁琐的用户交互而头疼了。每个开发人员的任务得到了确认,开发进度就可以迅
5、速的提高。松 散耦合的好处是显而易见的。如果一个系统没有分层,那么各自的逻辑都紧紧纠缠在一起,彼此间相互依赖,谁都是不可替换的。一旦发生改变,则牵一发而动全 身,对项目的影响极为严重。降低层与层间的依赖性,既可以良好地保证未来的可扩展,在复用性上也是优势明显。每个功能模块一旦定义好统一的接口,就 可以被各个模块所调用,而不用为相同的功能进行重复地开发。进行好的分层式结构设计,标准也是必不可少的。只有在一定程度的标准化基础上,这个系统才是可扩展的,可替换的。而层与层之间的通信也必然保证了接口的标准化。“金无足赤,人无完人”,分层式结构也不可避免具有一些缺陷:1、降低了系统的性能。这是不言而喻的。
6、如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。前面提到,PetShop的表示层是用ASP.Net设计的,也就是说,它应是一个BS系统。在.Net中,标准的BS分层式结构如下图所示:图二:.Net中标准的BS分层式结构随着PetShop版本的更新,其分层式结构也在不断的完善,例如PetShop2.0,就没有采用标准的三层式结构,如图三:图三:PetShop 2.0的体系
7、架构从 图中我们可以看到,并没有明显的数据访问层设计。这样的设计虽然提高了数据访问的性能,但也同时导致了业务逻辑层与数据访问的职责混乱。一旦要求支持的数 据库发生变化,或者需要修改数据访问的逻辑,由于没有清晰的分层,会导致项目作大的修改。而随着硬件系统性能的提高,以及充分利用缓存、异步处理 等机制,分层式结构所带来的性能影响几乎可以忽略不计。PetShop3.0纠正了此前层次不明的问题,将数据访问逻辑作为单独的一层独立出来:图四:PetShop 3.0的体系架构PetShop4.0基本上延续了3.0的结构,但在性能上作了一定的改进,引入了缓存和异步处理机制,同时又充分利用了ASP.Net 2.
8、0的新功能MemberShip,因此PetShop4.0的系统架构图如下所示:图五:PetShop 4.0的体系架构比较3.0和4.0的系统架构图,其核心的内容并没有发生变化。在数据访问层(DAL)中,仍然采用DAL Interface抽象出数据访问逻辑,并以DAL Factory作为数据访问层对象的工厂模块。对于DAL Interface而言,分别有支持MS-SQL的SQL Server DAL和支持Oracle的Oracle DAL具体实现。而Model模块则包含了数据实体对象。其详细的模块结构图如下所示:图六:数据访问层的模块结构图可以看到,在数据访问层中,完全采用了“面向接口编程”思想
9、。抽象出来的IDAL模块,脱离了与具体数据库的依赖,从而使得整个数据访问层利于数据库迁移。DALFactory模块专门管理DAL对象的创建,便于业务逻辑层访问。SQLServerDAL和OracleDAL模块均实现IDAL模块 的接口,其中包含的逻辑就是对数据库的Select,Insert,Update和Delete操作。因为数据库类型的不同,对数据库的操作也有所不同,代码也会因此有所区别。此外,抽象出来的IDAL模块,除了解除了向下的依赖之外,对于其上的业务逻辑层,同样仅存在弱依赖关系,如下图所示:图七:业务逻辑层的模块结构图图七中BLL是业务逻辑层的核心模块,它包含了整个系统的核心业务。在
10、业务逻辑层中,不能直接访问数据库,而必须通过数据访问层。注意图中对数据访问业务的调用,是通过接口模块IDAL来完成的。既然与具体的数据访问逻辑无关,则层与层之间的关系就是松散耦合的。如果此时需要修改数据访问层的具体实现,只要不涉及到IDAL的接口定义,那么业务逻辑层就不会受到任何影响。毕竟,具体实现的SQLServerDAL和OracalDAL根本就与业务逻 辑层没有半点关系。因为在PetShop 4.0中引入了异步处理机制。插入订单的策略可以分为同步和异步,两者的插入策略明显不同,但对于调用者而言,插入订单的接口是完全一样的,所以 PetShop 4.0中设计了IBLLStrategy模块。
11、虽然在IBLLStrategy模块中,仅仅是简单的IOrderStategy,但同时也给出了一个范例和信息,那就是在业务逻辑的处理中,如果存在业务操作的多样化,或者是今后可能的变化,均应利用抽象的原理。或者使用接口,或者使用抽象类,从而脱离对具 体业务的依赖。不过在PetShop中,由于业务逻辑相对简单,这种思想体现得不够明显。也正因为此,PetShop将核心的业务逻辑都放到了一个模块 BLL中,并没有将具体的实现和抽象严格的按照模块分开。所以表示层和业务逻辑层之间的调用关系,其耦合度相对较高:图八:表示层的模块结构图在图五中,各个层次中还引入了辅助的模块,如数据访问层的Messaging模块
12、,是为异步插入订单的功能提供,采用了MSMQ (Microsoft Messaging Queue)技术。而表示层的CacheDependency则提供缓存功能。这些特殊的模块,我会在此后的文章中详细介绍。petshop4.0 详解之二(数据访问层之数据库访问设计)在系列一中,我从整体上分析了PetShop的架构设计,并提及了分层的概念。从本部分开始,我将依次对各层进行代码级的分析,以求获得更加细致而 深入的理解。在PetShop 4.0中,由于引入了ASP.Net 2.0的一些新特色,所以数据层的内容也更加的广泛和复杂,包括:数据库访问、Messaging、MemberShip、P
13、rofile四部分。在系列 二中,我将介绍有关数据库访问的设计。在PetShop中,系统需要处理的数据库对象分为两类:一是数据实体,对应数据库中相应的数据表。它们没有行为,仅用于表现对象的数据。这些实体类都被放到Model程序集中,例如数据表Order对应的实体类OrderInfo,其类图如下: 这些对象并不具有持久化的功能,简单地说,它们是作为数据的载体,便于业务逻辑针对相应数据表进行读/写操作。虽然这些类的属性分别映射了数据表的列,而每一个对象实例也恰恰对应于数据表的每一行,但这些实体类却并不具备对应的数据库访问能力。由于数据访问层和业务逻辑层都将对这些数据实体进行操作,因此程序
14、集Model会被这两层的模块所引用。第二类数据库对象则是数据的业务逻辑对象。这里所指的业务逻辑,并非业务逻辑层意义上的领域(domain)业务逻辑(从这个意义上,我更倾向于将 业务逻辑层称为“领域逻辑层”),一般意义上说,这些业务逻辑即为基本的数据库操作,包括Select,Insert,Update和Delete。由于 这些业务逻辑对象,仅具有行为而与数据无关,因此它们均被抽象为一个单独的接口模块IDAL,例如数据表Order对应的接口IOrder: 将数据实体与相关的数据库操作分离出来,符合面向对象的精神。首先,它体现了“职责分离”的原则。将数据实体与其行为分开,使得两者之间依赖减
15、弱, 当数据行为发生改变时,并不影响Model模块中的数据实体对象,避免了因一个类职责过多、过大,从而导致该类的引用者发生“灾难性”的影响。其次,它体 现了“抽象”的精神,或者说是“面向接口编程”的最佳体现。抽象的接口模块IDAL,与具体的数据库访问实现完全隔离。这种与实现无关的设计,保证了系统 的可扩展性,同时也保证了数据库的可移植性。在PetShop中,可以支持SQL Server和Oracle,那么它们具体的实现就分别放在两个不同的模块SQLServerDAL、OracleDAL中。以Order为例,在SQLServerDAL、OracleDAL两个模块中,有不同的实现,但它们同时又都实
16、现了IOrder接口,如图: 从数据库的实现来看,PetShop体现出了没有ORM框架的臃肿与丑陋。由于要对数据表进行Insert和Select操作,以SQL Server为例,就使用了SqlCommand,SqlParameter,SqlDataReader等对象,以完成这些操作。尤其复杂的是 Parameter的传递,在PetShop中,使用了大量的字符串常量来保存参数的名称。此外,PetShop还专门为SQL Server和Oracle提供了抽象的Helper类,包装了一些常用的操作,如ExecuteNonQuery、ExecuteReader等方法。在没有ORM的情况下,使用
17、Helper类是一个比较好的策略,利用它来完成数据库基本操作的封装,可以减少很多和数据库操作有关的代码,这体现了 对象复用的原则。PetShop将这些Helper类统一放到DBUtility模块中,不同数据库的Helper类暴露的方法基本相同,只除了一些特殊 的要求,例如Oracle中处理bool类型的方式就和SQL Server不同,从而专门提供了OraBit和OraBool方法。此外,Helper类中的方法均为static方法,以利于调用。 OracleHelper的类图如下: 对于数据访问层来说,最头疼的是SQL语句的处理。在早期的CS结构中,由于未采用三层式架构设计,数据访问
18、层和业务逻辑层是紧密糅合在一起的,因 此,SQL语句遍布与系统的每一个角落。这给程序的维护带来极大的困难。此外,由于Oracle使用的是PL-SQL,而SQL Server和Sybase等使用的是T-SQL,两者虽然都遵循了标准SQL的语法,但在很多细节上仍有区别,如果将SQL语句大量的使用到程序中,无 疑为可能的数据库移植也带来了困难。最好的方法是采用存储过程。这种方法使得程序更加整洁,此外,由于存储过程可以以数据库脚本的形式存在,也便于移植和修改。但这种方式仍然有缺陷。 一是存储过程的测试相对困难。虽然有相应的调试工具,但比起对代码的调试而言,仍然比较复杂且不方便。二是对系统的更新带来障碍
19、。如果数据库访问是由程序 完成,在.Net平台下,我们仅需要在修改程序后,将重新编译的程序集xcopy到部署的服务器上即可。如果使用了存储过程,出于安全的考虑,必须有专门 的DBA重新运行存储过程的脚本,部署的方式受到了限制。我曾经在一个项目中,利用一个专门的表来存放SQL语句。如要使用相关的SQL语句,就利用关键字搜索获得对应语句。这种做法近似于存储过程的调 用,但却避免了部署上的问题。然而这种方式却在性能上无法得到保证。它仅适合于SQL语句较少的场景。不过,利用良好的设计,我们可以为各种业务提供不同 的表来存放SQL语句。同样的道理,这些SQL语句也可以存放到XML文件中,更有利于系统的扩
20、展或修改。不过前提是,我们需要为它提供专门的SQL语句 管理工具。SQL语句的使用无法避免,如何更好的应用SQL语句也无定论,但有一个原则值得我们遵守,就是“应该尽量让SQL语句尽存在于数据访问层的具体实现中”。当然,如果应用ORM,那么一切就变得不同了。因为ORM框架已经为数据访问提供了基本的Select,Insert,Update和Delete 操作了。例如在NHibernate中,我们可以直接调用ISession对象的Save方法,来Insert(或者说是Create)一个数据实体对 象:public void Insert(OrderInfo order)
21、60; ISession s = Sessions.GetSession(); ITransaction trans = null; try trans = s.BeginTransaction(); s.Save( order); trans.Commit();
22、0; finally s.Close(); 没有SQL语句,也没有那些烦人的Parameters,甚至不需要专门去考虑事务。此外,这样的设计,也是与数据库无关的,NHibernate可以通过Dialect(方言)的机制支持不同的数据库。唯一要做的是,我们需要为OrderInfo定义hbm文件。当然,ORM框架并非是万能的,面对纷繁复杂的业务逻辑,它并不能完全消灭SQL语句,以及替代复杂的数据库访问逻辑,但它却很好的体现了 “80/20(或90/10)法则”(也被
23、称为“帕累托法则”),也就是说:花比较少(10%-20%)的力气就可以解决大部分(80%-90%)的问 题,而要解决剩下的少部分问题则需要多得多的努力。至少,那些在数据访问层中占据了绝大部分的CRUD操作,通过利用ORM框架,我们就仅需要付出极少数 时间和精力来解决它们了。这无疑缩短了整个项目开发的周期。还是回到对PetShop的讨论上来。现在我们已经有了数据实体,数据对象的抽象接口和实现,可以说有关数据库访问的主体就已经完成了。留待我们的还有两个问题需要解决:1、数据对象创建的管理2、利于数据库的移植在PetShop中,要创建的数据对象包括Order,Product,Category,Inv
24、entory,Item。在前面的设计中,这些对 象已经被抽象为对应的接口,而其实现则根据数据库的不同而有所不同。也就是说,创建的对象有多种类别,而每种类别又有不同的实现,这是典型的抽象工厂模式 的应用场景。而上面所述的两个问题,也都可以通过抽象工厂模式来解决。标准的抽象工厂模式类图如下: 例如,创建SQL Server的Order对象如下:PetShopFactory factory = new SQLServerFactory();IOrder = factory.CreateOrder();要考虑到数据库的可移植性,则factory必须作为一个全局变量,并在主程序运行时被实例化。
25、但这样的设计虽然已经达到了“封装变化”的目的,但 在创建PetShopFactory对象时,仍不可避免的出现了具体的类SQLServerFactory,也即是说,程序在这个层面上产生了与 SQLServerFactory的强依赖。一旦整个系统要求支持Oracle,那么还需要修改这行代码为:PetShopFactory factory = new oracleFactory();修改代码的这种行为显然是不可接受的。解决的办法是“依赖注入”。“依赖注入”的功能通常是用专门的IoC容器提供的,在Java平台下,这样的容 器包括Spring,PicoContainer等。而在.Net平台下,最常见的则
26、是Spring.Net。不过,在PetShop系统中,并不需要专 门的容器来实现“依赖注入”,简单的做法还是利用配置文件和反射功能来实现。也就是说,我们可以在web.config文件中,配置好具体的 Factory对象的完整的类名。然而,当我们利用配置文件和反射功能时,具体工厂的创建就显得有些“画蛇添足”了,我们完全可以在配置文件中,直接指向 具体的数据库对象实现类,例如PetShop.SQLServerDAL.IOrder。那么,抽象工厂模式中的相关工厂就可以简化为一个工厂类了,所以 我将这种模式称之为“具有简单工厂特质的抽象工厂模式”,其类图如下: DataAccess类完全取代
27、了前面创建的工厂类体系,它是一个sealed类,其中创建各种数据对象的方法,均为静态方法。之所以能用这个类达到抽象工厂的目的,是因为配置文件和反射的运用,如下的代码片断所示:public sealed class DataAccess / Look up the DAL implementation we should be using private static readonly string path = ConfigurationManager.AppSettings”WebDAL”; private s
28、tatic readonly string orderPath = ConfigurationManager.AppSettings”OrdersDAL”; public static PetShop.IDAL.IOrder CreateOrder() string className = orderPath + “.Order”; return (PetShop.IDAL.IOrd
29、er)Assembly.Load(orderPath).CreateInstance(className); 在PetShop中,这种依赖配置文件和反射创建对象的方式极其常见,包括IBLLStategy、CacheDependencyFactory 等等。这些实现逻辑散布于整个PetShop系统中,在我看来,是可以在此基础上进行重构的。也就是说,我们可以为整个系统提供类似于“Service Locator”的实现:public static class ServiceLocator private static readonly string da
30、lPath = ConfigurationManager.AppSettings”WebDAL”; private static readonly string orderPath = ConfigurationManager.AppSettings”OrdersDAL”; / private static readonly string orderStategyPath = ConfigurationManager.AppSettings”OrderStrategyAssembly”; public static object
31、 LocateDALObject(string className) string fullPath = dalPath + “.” + className; return Assembly.Load(dalPath).CreateInstance(fullPath); public static object LocateDALOrderObject(string className) string fullPath = orderPath + “.” + className;
32、160;return Assembly.Load(orderPath).CreateInstance(fullPath); public static object LocateOrderStrategyObject(string className) string fullPath = orderStategyPath + “.” + className; return Assembly.Load(orderStategyPath).CreateInstance(fullPath); /那么和所谓“依赖注
33、入”相关的代码都可以利用ServiceLocator来完成。例如类DataAccess就可以简化为:public sealed class DataAccess public static PetShop.IDAL.IOrder CreateOrder() return (PetShop.IDAL.IOrder)ServiceLocator. LocateDALOrderObject(”Order”); 通过ServiceLocator,将所
34、有与配置文件相关的namespace值统一管理起来,这有利于各种动态创建对象的管理和未来的维护。petshop4.0 详解之三(PetShop数据访问层之消息处理)PetShop数据访问层之消息处理。MSMQMessaging模块中,Order对象实现了IMessaging模块中定义的接口 IOrder,同时它还继承了基类PetShopQueue,其定义如下:public class order:PetShopQueue, PetShop.IMessaging.IOrder方法的实现代码如下: public new orderInfo Receive() / This method invol
35、ves in distributed transaction and need Automatic Transaction type base.transactionType = MessageQueueTransactionType.Automatic; return (OrderInfo)(Message)base.Receive().Body; petshop4.0 详解之四(PetShop之ASP.NET缓存)如果对微型计算机硬件系统有足够的了解,那么我们对于Cache这个名词一定是耳熟能详的。在CPU以及主板的芯片中,都引入了这种名为高速缓冲存 储器(Cache)的技术。因为Cach
36、e的存取速度比内存快,因而引入Cache能够有效的解决CPU与内存之间的速度不匹配问题。硬件系统可以利用 Cache存储CPU访问概率高的那些数据,当CPU需要访问这些数据时,可以直接从Cache中读取,而不必访问存取速度相对较慢的内存,从而提高了 CPU的工作效率。软件设计借鉴了硬件设计中引入缓存的机制以改善整个系统的性能,尤其是对于一个数据库驱动的Web应用程序而言,缓存的利用是不可或缺 的,毕竟,数据库查询可能是整个Web站点中调用最频繁但同时又是执行最缓慢的操作之一,我们不能被它老迈的双腿拖缓我们前进的征程。缓存机制正是解决这 一缺陷的加速器。4.1
37、 ASP.NET缓存概述作为.Net框架下开发Web应用程序的主打产品,ASP.NET充分考虑了缓存机制。通过某种方法,将系统需要的数据对象、Web页面存储在内存 中,使得Web站点在需要获取这些数据时,不需要经过繁琐的数据库连接、查询和复杂的逻辑运算,就可以“触手可及”,如“探囊取物”般容易而快速,从而提 高整个Web系统的性能。ASP.NET提供了两种基本的缓存机制来提供缓存功能。一种是应用程序缓存,它允许开发者将程序生成的数据或报表业务对象放入缓存中。另外一种缓存机制是页输出缓存,利用它,可以直接获取存放在缓存中的页面,而不需要经过繁杂的对该页面的再次处理。应用程序缓存其实现原理说来平淡
38、无奇,仅仅是通过ASP.NET管理内存中的缓存空间。放入缓存中的应用程序数据对象,以键/值对的方式存储,这便于用户在访问缓存中的数据项时,可以根据key值判断该项是否存在缓存中。放入在缓存中的数据对象其生命周期是受到限制的,即使在整个应用程序的生命周期里,也不能保证该数据对象一直有效。ASP.NET可以对应用程序缓 存进行管理,例如当数据项无效、过期或内存不足时移除它们。此外,调用者还可以通过CacheItemRemovedCallback委托,定义回调方法 使得数据项被移除时能够通知用户。在.Net Framework中,应用程序缓存通过System.Web.Caching.Cache类实现
39、。它是一个密封类,不能被继承。对于每一个应用程序域, 都要创建一个Cache类的实例,其生命周期与应用程序域的生命周期保持一致。我们可以利用Add或Insert方法,将数据项添加到应用程序缓存中,如 下所示:Cache"First" = "First Item"Cache.Insert("Second", "Second Item");我们还可以为应用程序缓存添加依赖项,使得依赖项发生更改时,该数据项能够从缓存中移除:string dependencies = "Second"Cache.Ins
40、ert("Third", "Third Item",new System.Web.Caching.CacheDependency(null, dependencies);与之对应的是缓存中数据项的移除。前面提到ASP.NET可以自动管理缓存中项的移除,但我们也可以通过代码编写的方式显式的移除相关的数据项:Cache.Remove("First");相对于应用程序缓存而言,页输出缓存的应用更为广泛。它可以通过内存将处理后的ASP.NET页面存储起来,当客户端再一次访问该页面时,可以省去 页面处理的过程,从而提高页面访问的性能,以及Web
41、服务器的吞吐量。例如,在一个电子商务网站里,用户需要经常查询商品信息,这个过程会涉及到数据库访 问以及搜索条件的匹配,在数据量较大的情况下,如此的搜索过程是较为耗时的。此时,利用页输出缓存就可以将第一次搜索得到的查询结果页存储在缓存中。当用 户第二次查询时,就可以省去数据查询的过程,减少页面的响应时间。页输出缓存分为整页缓存和部分页缓存。我们可以通过OutputCache指令完成对Web页面的输出缓存。它主要包含两个参数: Duration和VaryByParam。Duration参数用于设置页面或控件进行缓存的时间,其单位为秒。如下的设置表示缓存在60秒内有效:<% OutputCac
42、he Duration=“60“ VaryByParam=“none“ %>只要没有超过Duration设置的期限值,当用户访问相同的页面或控件时,就可以直接在缓存中获取。使用VaryByParam参数可以根据设置的参数值建立不同的缓存。例如在一个输出天气预报结果的页面中,如果需要为一个ID为txtCity的TextBox控件建立缓存,其值将显示某城市的气温,那么我们可以进行如下的设置:<% OutputCache Duration=”60” VaryByParam=”txtCity” %>如此一来,ASP.NET会对txtCity控件的值进行判断,只有输入的值与缓存值相同,
43、才从缓存中取出相应的值。这就有效地避免了因为值的不同而导致输出错误的数据。利用缓存的机制对性能的提升非常明显。通过ACT(Application Center Test)的测试,可以发现设置缓存后执行的性能比未设置缓存时的性能足足提高三倍多。引入缓存看来是提高性能的“完美”解决方案,然而“金无足赤,人无完人”,缓存机制也有缺点,那就是数据过期的问题。一旦应用程序数据或者页面结果 值发生的改变,那么在缓存有效期范围内,你所获得的结果将是过期的、不准确的数据。我们可以想一想股票系统利用缓存所带来的灾难,当你利用错误过期的数据 去分析股市的风云变幻时,你会发现获得的结果真可以说是“失之毫厘,谬以千里
44、”,看似大好的局面就会像美丽的泡沫一样,用针一戳,转眼就消失得无影无踪。那么我们是否应该为了追求高性能,而不顾所谓“数据过期”所带来的隐患呢?显然,在类似于股票系统这种数据更新频繁的特定场景下,数据过期的糟糕表 现甚至比低效的性能更让人难以接受。故而,我们需要在性能与数据正确性间作出权衡。所幸的是,.Net Framework 2.0引入了一种新的缓存机制,它为我们的“鱼与熊掌兼得”带来了技术上的可行性。.Net 2.0引入的自定义缓存依赖项,特别是基于MS-SQL Server的SqlCacheDependency特性,使得我们可以避免“数据过期”的问题,它能够根据数据库中相应数据的变化,通
45、知缓存,并移除那 些过期的数据。事实上,在PetShop 4.0中,就充分地利用了SqlCacheDependency特性。4.2 SqlCacheDependency特性SqlCacheDependency特性实际上是通过System.Web.Caching.SqlCacheDependency类来体现的。 通过该类,可以在所有支持的SQL Server版本(7.0,2000,2005)上监视特定的SQL Server数据库表,并创建依赖于该表以及表中数据行的缓存项。当数据表或表中特定行的数据发生更改时,具有依赖项的数据项就会失效,并自动从 Cache中删除该项,从而保证了缓存中不再保留过期
46、的数据。由于版本的原因,SQL Server 2005完全支持SqlCacheDependency特性,但对于SQL Server 7.0和SQL Server 2000而言,就没有如此幸运了。毕竟这些产品出现在.Net Framework 2.0之前,因此它并没有实现自动监视数据表数据变化,通知ASP.NET的功能。解决的办法就是利用轮询机制,通过ASP.NET进程内的一个线程以指 定的时间间隔轮询SQL Server数据库,以跟踪数据的变化情况。要使得7.0或者2000版本的SQL Server支持SqlCacheDependency特性,需要对数据库服务器执行相关的配置步骤。有两种方法配
47、置SQL Server:使用aspnet_regsql命令行工具,或者使用SqlCacheDependencyAdmin类。4.2.1 利用aspnet_regsql工具aspnet_regsql工具位于WindowsMicrosoft.NETFramework版本文件夹中。如果直接双击该工具的执行文件,会弹出一个向导对话框,提示我们完成相应的操作:图4-1 aspnet_regsql工具如图4-1所示中的提示信息,说明该向导主要用于配置SQL Server数据库,如membership,profiles等信息,如果要配置SqlCacheDependency
48、,则需要以命令行的方式执行。以 PetShop 4.0为例,数据库名为MSPetShop4,则命令为:aspnet_regsql -S localhost -E -d MSPetShop4 -ed以下是该工具的命令参数说明:-? 显示该工具的帮助功能;-S 后接的参数为数据库服务器的名称或者IP地址;-U 后接的参数为数据库的登陆用户名;-P 后接的参数为数据库的登陆密码;-E 当使用windows集成验证时,使用该
49、功能;-d 后接参数为对哪一个数据库采用SqlCacheDependency功能;-t 后接参数为对哪一个表采用SqlCacheDependency功能;-ed 允许对数据库使用SqlCacheDependency功能;-dd 禁止对数据库采用SqlCacheDependency功能;-et 允许对数据表采用SqlCacheDependency功能;-dt 禁止对数据表采用SqlC
50、acheDependency功能;-lt 列出当前数据库中有哪些表已经采用sqlcachedependency功能。以上面的命令为例,说明将对名为MSPetShop4的数据库采用SqlCacheDependency功能,且SQL Server采用了windows集成验证方式。我们还可以对相关的数据表执行aspnet_regsql命令,如:aspnet_regsql -S localhost -E -d MSPetShop4 -t Item -etaspnet_regsql -S localhost -E -d MSPetShop4 -t Product -e
51、taspnet_regsql -S localhost -E -d MSPetShop4 -t Category -et当执行上述的四条命令后,aspnet_regsql工具会在MSPetShop4数据库中建立一个名为 AspNet_SqlCacheTablesForChangeNotification的新数据库表。该数据表包含三个字段。字段tableName记 录要追踪的数据表的名称,例如在PetShop 4.0中,要记录的数据表就包括Category、Item和Product。notificationCreated字段记录开始追踪的时间。 changeId作为一个类型为int的字段,用于记
52、录数据表数据发生变化的次数。如图4-2所示:图4-2 AspNet_SqlCacheTablesForChangeNotification数据表除此之外,执行该命令还会为MSPetShop4数据库添加一组存储过程,为ASP.NET提供查询追踪的数据表的情况,同时还将为使用了 SqlCacheDependency的表添加触发器,分别对应Insert、Update、Delete等与数据更改相关的操作。例如Product数 据表的触发器:Create TRIGGER dbo.Product_AspNet_SqlCacheNotification_Trigger ON Product
53、60; FOR Insert, Update, Delete AS BEGIN SET NOCOUNT ON EXEC dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure N'Product'END其中,AspNet_SqlCacheUpdateChangeIdStoredProcedure即是工具添加的一组存储过程中的一个。当对 Product数据表执行Insert、Updat
54、e或Delete等操作时,就会激活触发器,然后执行 AspNet_SqlCacheUpdateChangeIdStoredProcedure存储过程。其执行的过程就是修改 AspNet_SqlCacheTablesForChangeNotification数据表的changeId字段值:Create PROCEDURE dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure tableN
55、ame NVARCHAR(450) AS BEGIN Update dbo.AspNet_SqlCacheTablesForChangeNotification WITH (ROWLOCK)
56、SET changeId = changeId + 1 Where tableName = tableName END GO4.2.2 利用SqlCacheDependencyAdmin类我们也可以利用编程的方式来来管理数据库对SqlCacheDepen
57、dency特性的使用。该类包含了五个重要的方法:DisableNotifications为特定数据库禁用 SqlCacheDependency对象更改通知DisableTableForNotifications为数据库中的特定表禁用SqlCacheDependency对象更改通知EnableNotifications为特定数据库启用SqlCacheDependency对象更改通知EnableTableForNotifications为数据库中的特定表启用SqlCacheDependency对象更改通知GetTablesEnabledForNotifications返回启用了SqlCacheDe
58、pendency对象更改通知的所有表的列表表4-1 SqlCacheDependencyAdmin类的主要方法假设我们定义了如下的数据库连接字符串:const string connectionStr = "Server=localhost;Database=MSPetShop4"那么为数据库MSPetShop4启用SqlCacheDependency对象更改通知的实现为:protected void Page_Load(object sender, EventArgs e) if (!IsPostBack)
59、60; SqlCacheDependencyAdmin.EnableNotifications(connectionStr); 为数据表Product启用SqlCacheDependency对象更改通知的实现则为:SqlCacheDependencyAdmin.EnableTableForNotifications(connectionStr, "Product");如果要调用表4-1中所示的相
60、关方法,需要注意的是访问SQL Server数据库的帐户必须具有创建表和存储过程的权限。如果要调用EnableTableForNotifications方法,还需要具有在该表上创建SQL Server触发器的权限。虽然说编程方式赋予了程序员更大的灵活性,但aspnet_regsql工具却提供了更简单的方法实现对SqlCacheDependency的配 置与管理。PetShop 4.0采用的正是aspnet_regsql工具的办法,它编写了一个文件名为InstallDatabases.cmd的批处理文件,其中包含了对 aspnet_regsql工具的执行,并通过安装程序去调用该文件,实现对SQL
61、 Server的配置。4.3 在PetShop 4.0中ASP.NET缓存的实现PetShop作为一个B2C的宠物网上商店,需要充分考虑访客的用户体验,如果因为数据量大而导致Web服务器的响应不及时,页面和查询数据迟迟 得不到结果,会因此而破坏客户访问网站的心情,在耗尽耐心的等待后,可能会失去这一部分客户。无疑,这是非常糟糕的结果。因而在对其进行体系架构设计时, 整个系统的性能就显得殊为重要。然而,我们不能因噎废食,因为专注于性能而忽略数据的正确性。在PetShop 3.0版本以及之前的版本,因为ASP.NET缓存的局限性,这一问题并没有得到很好的解决。PetShop 4.0则引入了SqlCa
62、cheDependency特性,使得系统对缓存的处理较之以前大为改观。4.3.1 CacheDependency接口PetShop 4.0引入了SqlCacheDependency特性,对Category、Product和Item数据表对应的缓存实施了SQL Cache Invalidation技术。当对应的数据表数据发生更改后,该技术能够将相关项从缓存中移除。实现这一技术的核心是 SqlCacheDependency类,它继承了CacheDependency类。然而为了保证整个架构的可扩展性,我们也允许设计者建立自定义的 CacheDependency类,
63、用以扩展缓存依赖。这就有必要为CacheDependency建立抽象接口,并在web.config文件中进行配 置。在PetShop 4.0的命名空间PetShop.ICacheDependency中,定义了名为IPetShopCacheDependency接口,它仅包含了一个接口方法:public interface IPetShopCacheDependency AggregateCacheDependency GetDependency();AggregateCacheDependency是.Net Framework 2.0新增的一个类,它负责监视依赖项对象的集合。当这个集合中的任意一个依赖项对象发生改变时,该依赖项对象对应的缓存对象都将被自动移除。AggregateCacheDependency类起到了组合CacheDependency对象的作用,它可以将多个CacheDependency 对象甚至于不同类型的CacheDependency对象与缓存项建立关联。由于PetShop需要为Category、Product和Item数据表 建立依赖项,因而IPetShopCacheDependency的接口方法GetDependen
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 财经记者考试试题及答案
- 物理考试试题大全及答案
- 新化考试试题及答案
- 传统电商考试试题及答案
- 二级绘图师考试试题及答案
- 2025加盟连锁超市合同协议书模板
- 2025-2030中国制冷设备行业全景调研与发展战略研究咨询报告
- 江苏省无锡市江阴市六校2024-2025学年高二下学期4月期中联考试题 历史 含答案
- 2025办公楼租赁合同
- 浙江省衢温“5+1”联盟2023-2024学年高二上学期期中联考历史无答案
- 企业文化与员工认同培训课件
- 古寺庙重建可行性报告
- 老年护理的专科发展课件
- 人工智能对经济的影响
- 大班语言优质课课件PPT《青蛙歌》
- 预防校园欺凌法治知识竞答题库及答案
- 意大利(百得)TBG 系列燃烧机说明书
- 污水处理设施运维服务投标方案(技术方案)
- 《交通运输概论》 课件全套 第1-7章 绪论、公路运输系统-综合运输系统
- 大学生就业创业法律实务智慧树知到课后章节答案2023年下上海建桥学院
- 不确定的危机下做确定的业绩
评论
0/150
提交评论