UML软件工程组织

The Distributed Cache Pattern[翻译]

内容摘要:

在2001年初,当我与Martin Fowler共同评审何种模式该收录进他的书Patterns of Enterprise Application Architecture中的时候,我们开始寻找描述异步信息的模式。当时我们想将这些模式作为PoEAA章节的一部分,但是我们很快意识到这个主题的内容远远超出了该书的讨论范围。Martin 与 我继而主持召开一系列有关模式的会议,参加者包括Booby Woolf 与Gregor Hohpe,他们负责对本书进行评估,完整的书名是:Patterns of Enterprise Integration: Designing, Building and Deploying Messaging Solutions。书中讲解了一个丰富且强壮的模式语言,此模式语言适用于异步消息,Web Service与企业应用程序的整合工具。

作者:蔡永航 (注:James Shen的学弟) 南京工业大学工商管理三年级学生



但是,当工作组热情高涨地为这个项目而忙碌的时候,我们发现有一些简单的模式不适合其余的模式语言。到目前为止这些最知名的模式与分布式缓存模式都已有丰富的范例。尽管分布式缓存模式不属于企业应用整合这类模式,但是它却是一个有趣并且有用的模式,它应该在高级J2EE设计者的工具箱中占有一席之地。

什么是模式

如果你对模式并不了解,你因该先去阅读上文中讲述的两本书中的一本或者Design Patterns: Elements of Reusable Object-Oriented Software,以了解开发者能够从模式与模式语言中得到什么。简短的说来,模式是一个以高度抽象形式表现的可重用的软件资源——模式是一种针对普遍问题解决方法的描述,这种解决方法在实践中以标准的形式应用了许多次。我们将实际使用的模式样式最初是由Kent Beck先生在他的书Smalltalk Best Practices Patterns中提出的。书中首先以突出的文字表现模式需要解决的问题,接着讨论面临哪些问题的挑战,最后以突出并且是斜体的文字将提出的解决方案附在详细解决方案之后。
好了,我们已经知道什么是模式,并且知道它怎样被表述,现在是时候讨论一下模式了。

分布式缓存

假设你将应用程序放在几台服务器上以获得可扩展性。它使用数据库存放资料,但是许多对于数据库的查询由于相当复杂将花费很长时间。并且你的数据库查询语句已经不能再进一步优化了,因此已经不可能通过对数据库的调整以获得性能上的提升。你试图在每台机器上缓存来自数据库中的数据,但是,你不能做到服务器当前缓存的数据是及时的,因为数据在不断变化,并且每一台机器上缓存的数据与数据库中的数据存在差异,甚至每台服务器中缓存的数据时时刻刻都不一样。
我们怎样做才能同步一系列的分布式缓存呢?例如将更新过后的内容传递至缓存并且通过查询能在位于不同服务器的缓存中得到相同的结果。
许多系统在设计时使用将数据缓存的方法以增加程序性能。例如一个系统使用EJB进行构建,你可能使用Entity Bean Option A 缓存,或者通过singleton模式在内存中保持value object。但是,上述的做法都有一个共同的缺点;例如,在Entity Bean Option A中,一旦一个CMP EJB从数据库中读取,它在内存中保留的值将不会被改变,尽管其在数据库中对应的值已经被改变了。
大多数分布式HttpSession同样是分布式数据缓存的一种类型。由于他们之间的相似性,导致了一个推荐性的用于缓存的API规范,JCache API(JSR-107)诞生。不幸的是,缓存在信息面前不是一个“纪录系统”。在大多数场合下,数据的最终存放地是数据库,因此会产生这么一种情况,存储在数据库中的数据与存储在缓存中的数据不能够同步,产生了差异。当数据库的内容被一台服务器更新后,如果一个查询语句在另外一台服务器的缓存中得到了未更新前的数据,那么它返回的值对于查询者来说便是错误的。


一些系统已经自己建立了一种数据库,这种数据库负责在其内容更新时同步相关的分布式缓存。这么做带来的问题是采用了非标准的解决方法,并且不可移植。一个系统如果使用了ORACLE数据库中的触发器,那么将系统使用的数据库改变为DB2或者SQL Server后系统将不能运行。同样,不是每一个数据库(例如一些开放源代码的数据库,像MySQL)都支持高级的数据库特性,例如触发器。
因此,我们需要一种方案以解决当一台服务器缓存中的内容作出改变时强制其他服务器中的缓存进行更新。
使用发布——订阅消息的方法更新缓存。例如当位于某台机器缓存中的对象作出改变时,这台服务器将通知所有其他服务器的缓存这个对象的内容已经改变了。
如果缓存接收到一个通知,它能够在得到通知的时候选择更新其来自数据库(程序共享)的值或者仅仅简单地将自身标记为“Dirty”,当有任何人再次请求它时便从数据库中读取更新后的值。这种解决方案的结构如下(图1:分布式缓存更新)所示。另一个可选的方案是从得到的消息中更新数据。但是这样做的效果并没有直接从数据库中读取新数据来的好,因为这将需要从消息中得到已经与消息混合了的对象以替代读取数据库的操作,这样做将增加消息代码编写的复杂度,同时还将增加系统中传送消息的开销,因为消息将作为一个完整的实体对象进行传送,而不是作为一个通过队列传送的简单通知。



图1:分布式缓存更新
维持较大缓存粒度是十分重要的,如此在消息系统中传递的消息总量将保持在最小。在许多情况下,可以通过发送单独的通知来完成,通知的内容是关于一个对象图(object graph)的“根”对象的,一旦这个对象图中的任何一个部分发生了改变,这个通知就被发出。在一个事务中你可以延迟通知的发布直到数据库完成了全部更新,以减少发布多余消息的目的。同样的,你可以将放置消息进入队列作为进行数据库更新事务的一部分(例如,将缓存作为一个事务化的客户端),以确保数据库的状态与缓存中的状态相同(图1 表示数据库更新和消息在同一个事务中,消息作为事务上下文的一部分,事务被标记为“Transaction Context 1”)。
你能够通过一些方法减少缓存处理的消息数量,因为有很多消息对于某个缓存来说是不需要处理的,因为这个缓存中并没有包含消息中需要更新的对象。这些需要更新的对象保存在多个主题(Topic)中(每个主题对应一个“根”类型)。缓存通过使用消息选择器(Message Selectors)过滤掉它不感兴趣的对象的更新通知,但是这么做不能减少位于主题中的消息总量——这么做只能减少每个客户端需要处理的消息数量。
需要注意的十分重要的一点是此种解决方案只能应用于那些对所有服务器上的缓存保持同步并不是在任何时候都严格要求一致的情况。这是因为应用此种方案的必要条件是至少需要两个事务,一个事务在“发出通知”的缓存那端,另一个事务在每一个“接收通知”的缓存那儿。在图1中已经体现出来了,通知接收端(Server N)显示执行了一个分离的事务上下文,此事务不同于起始的事务上下文,两个事务并不同时执行。因此,在更新的过程中一条查询语句请求了尚未同步缓存,其结果将返回过期的数据。在缓存的同步中,还存在着消息无法投递的可能性,不正确的更新处理和一些其他的状况,这些都导致此种解决方案不是100%的可靠。但是,在大多数应用程序中,只要最后决定性的内容来自于储存记录的数据库的状态,那么这些不稳定因素便可以忽略。
文中讲述的方法在商业化的Java应用程序服务器中已经得到成功实现。例如,IBM WebSphere Application Server 5.0使用此方法同步HttpSession,Dynamic page与Command缓存,同步工作通过一系列WebSphere的内部框架实现。同样的,WebLogic Application Server 6.1也包括了同步缓存这一特性。
SpiritSoft软件公司出售一个名叫SpiritCache的产品,此产品实现了与JSR 107规范兼容的缓存产品,使用这种规范将能够在许多应用服务器上工作。还有Tangosol销售一个名叫Coherence的产品系列同样实现了这个规范。最后一点,此规范的特定实现被严格限制在EJB Entity缓存上,关于EJB Entity缓存已经在前面的文章中(Rakatine)作为Seppuku pattern介绍过了。

参考资料:

[Brown] Kyle Brown, Choosing the Right EJB Type, IBM WebSphere Developer’s Domain, http://www7.software.ibm.com/vad.nsf/Data/Document2361?OpenDocument&p=1&BCT=66

[EJB], EJB 1.1 Specification, Sun Microsystems, http://java.sun.com/products/ejb/docs.html#specs

[Hohpe] Gregor Hohpe, “Enterprise Integration Patterns”, http://www.enterpriseintegrationpatterns.com/

[JCache], JSR 107; JCache – Java Temporary Caching API, http://www.jcp.org/jsr/detail/107.prt

[Rakatine] Dimitri Rakatine, “The Seppuku Pattern”, The ServerSide.com Newsletter #26, http://www.theserverside.com/patterns/thread.jsp?thread_id=11280

[SpiritSoft] SpiritCache overview, http://www.spiritsoft.com/products/jms_jcache/overview.html

[Woolf] Bobby Woolf and Kyle Brown, “Patterns of System Integration with Enterprise Messaging”, submitted to the PLoP 2002
conference, http://www.messagingpatterns.com/


原文出处: http://www.chenshen.com

 

版权所有:UML软件工程组织