英文地址:http://www.terracotta.org/confluence/display/docs/Clustering+the+Spring+Framework
http://www.infoq.com/articles/spring-web-flow-terracotta
中文地址: http://www.infoq.com/cn/articles/spring-web-flow-terracotta
简单将重点摘录:
Terracotta for Spring是基于Spring应用的运行时,它为Spring 应用提供了透明的高性能集群支持,对应用代码和部署及配置流程影响都很小。它通过在应用下面的堆级别进行集群而不是直接集群应用。
这让开发者能够开发与无状态方式不同的单节点有状态Spring应用。这使得在需要扩展的应用开始设计时不考虑集群。而在应用需要扩展或者要保证搞可用性和故障恢复时,他们只需要在Terracotta 配置文件中定义哪些Spring应用上下文中的beans需要进行集群。Terracotta for Spring 使得应用能够被自动和透明的集群,还保证在集群间的语义和单节点一样。
对于Spring Web Flow来说,这实际上更简单。用户为了获得web应用的状态和持续仓库的集群能力, 只需要在Terracotta配置文件中把特定的web应用声明为启用“session-support”。(详细内容见下面的章节“声明式配置”)
从宏观上看,Terracotta for Spring提供了:
HTTP session状态的集群。保证Spring Web Flow中的用户状态和扩展仓库或放入HTTP session的其它状态的高可用性和故障恢复能力。Spring bean的集群。Springbean的生命周期语义和域在集群间被保存,它们在“逻辑”上相同的ApplicationContext中。目前能被集群的bean类型是singleton和session scoped. 用户可以声明式配置哪个application contexts中的哪个bean需要被集群。透明集群POJO。不需要修改已有的代码,甚至不需要源代码。应用基于很少的声明式XML 配置文件,在载入期透明的生效。Terracotta for Spring不需要实现Serializable, Externalizable或其它接口的类。能这样实现的原因它并没有使用序列化,而只是将实际的差量和已经改变了的数据传输给当前需要的节点(lazily)。虚拟内存管理。它也提供分布式垃圾收集和虚拟堆功能。比如,由于物理内存在需要时被换入换出,它能在一个4G RAM的机器上运行需要200G堆的Web应用。 这也意味着你不需要关心Spring Web Flow 会话数据的大小是否超过了物理堆大小。Terracotta for Spring 使用aspect-oriented技术来在类载入时适配应用。在这个阶段它扩展了应用以保证Java语义在集群间被正确的维护,包括对象引用,进程调用和垃圾收集等
我们在前面提到Terracotta并没有使用serialization。这意味着任何Spring Web Flow 维护的会话都可以在集群间共享。这也意味着Terracotta并没有把会话状态的全部对象图发给所有节点,而是把图分解成纯粹的数据,并在网络间传输实际的“差量”和改变--数据的“原始信息”在其它节点上。
因为有一个记录节点间相互引用的中心调度器(见下文),它能以lazy的方式工作,而只把改变传送到引用了这些“dirty”数据的节点。这需要使用局部引用。如果负载均衡配置为使用"session粘滞" 就更有效率,因为这意味着很多数据可能永远不会脱离实际的session而不用复制到其它节点。
这个架构是中心辐射的,也就是有一个管理客户端的中心调度器。在这里客户端就是你配有Terracotta for Spring 运行时的普通应用。调度器不是单点失败的,但你可以配置一组备用调度器,并在主调度器崩溃时选择一个来接替。你也可以独立于客户端,对调度器进行集群扩展。
这里我们使用一个叫Sellitem的示例应用来推动讨论并展示给大家:
如何使用Spring Web Flow来构建一个有状态基于会话的web应用。如何使用Terracotta for Spring来声明式集群有状态应用。Sellitem示例应用可以在Spring Web Flow的发布版本中找到。(更多信息见文章末尾的“Resources” 章节)
Sellitem是展示结合了有条件转移、会话域、流程执行转向和延续性的示例应用。用户在几个页面间导航,可以定义货物的价格、可以销售的货物数量、折扣比率、送货详情(如果需要)和最后查看所有信息。
我们不会通读应用的所有源码,而是主要介绍一些关键概念,如怎么配置Spring Web Flow输出的不同标准服务(Bean)和怎么定义页面流转(使用针对页面流转的Spring Web FlowDSL的XML版本)。
应用的入口是一个标准的Spring MVC DispatcherServlet,它在web.xml中注册并在web application context中映射到*.htm。
<servlet> <servlet-name>sellitem</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/sellitem-servlet-config.xml /WEB-INF/sellitem-webflow-config.xml </param-value> </init-param></servlet><servlet-mapping> <servlet-name>sellitem</servlet-name> <url-pattern>*.htm</url-pattern></servlet-mapping>DispatcherServlet配置在Spring的配置文件sellitem-servlet-config.xml和sellitem-webflow-config.xml中。 sellitem-servlet-config.xml中有一个映射到"/pos.htm"的控制器,它将所有该URL的请求转发到Spring Web Flow系统(它的入口是一个流程执行器):
<bean name="/pos.htm" class="org.springframework.webflow.executor.mvc.FlowController"> <property name="flowExecutor" ref="flowExecutor" /></bean>Spring flowExecutor bean配置使用一个flowRegistry bean来执行"/WEB-INF/flows/"目录中的基于XML的流转定义。
<flow:executor id="flowExecutor" registry-ref="flowRegistry"/><flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/flows/**-flow.xml" /></flow:registry>剩余的逻辑在我们已经注册的flowRegistry bean的流转定义中。(参照前面的'配置flow executor和flow registry beans章节)。
在深入流转实现细节前,我们先看一下页面流转的状态图(如下图)。
从上面我们可以看到流转在结束前经过了几个步骤,在决定销售是否需要送货时有一个决策状态。
一个很好的针对上面导航规则的初始流转定义实现如下:
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd"> <var name="sale" class="org.springframework.webflow.samples.sellitem.Sale"/> <start-state idref="enterPriceAndItemCount"/> <view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> <transition on="submit" to="enterCategory"/> </view-state> <view-state id="enterCategory" view="categoryForm"> <transition on="submit" to="requiresShipping"/> </view-state> <decision-state id="requiresShipping"> <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="finish"/> </decision-state> <view-state id="enterShippingDetails" view="shippingDetailsForm"> <transition on="submit" to="finish"/> </view-state> <end-state id="finish" view="costOverview"/></flow>我们从上面的定义可以看到,实际状态与状态图中的状态对应,状态转换与图中的箭头对应。"sale" bean是流转开始时分配的流转变量实例。它持有了Sale相关的属性。
上面的定义展示了所有的导航逻辑,但还没有实现任何应用行为。特别是在用户提交时更新Sale Bean的逻辑还没有实现。另外后台的sale处理逻辑还没有定义。
实现了所有必需行为的完整Spring Web Flow定义如下:
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd"> <var name="sale" class="org.springframework.webflow.samples.sellitem.Sale"/> <start-state idref="enterPriceAndItemCount"/> <view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> <render-actions> <action bean="formAction" method="setupForm"/> </render-actions> <transition on="submit" to="enterCategory"> <action bean="formAction" method="bindAndValidate"> <attribute name="validatorMethod" value="validatePriceAndItemCount"/> </action> </transition> </view-state> <view-state id="enterCategory" view="categoryForm"> <transition on="submit" to="requiresShipping"> <action bean="formAction" method="bind"/> </transition> </view-state> <decision-state id="requiresShipping"> <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> </decision-state> <view-state id="enterShippingDetails" view="shippingDetailsForm"> <transition on="submit" to="processSale"> <action bean="formAction" method="bind"/> </transition> </view-state> <action-state id="processSale"> <bean-action bean="saleProcessor" method="process"> <method-arguments> <argument expression="flowScope.sale"/> </method-arguments> </bean-action> <transition on="success" to="finish"/> </action-state> <end-state id="finish" view="costOverview"> <entry-actions> <action bean="formAction" method="setupForm"/> </entry-actions> </end-state> <import resource="sellitem-beans.xml"/></flow>在定义导航逻辑之外,也定义了适时调用恰当应用行为的action。这包括处理用户提交事件和调用后台处理器来处理sale的逻辑。
当进入展示表单的视图状态时,流转调用一个FormAction command bean来进行表单的装配和提交逻辑。在提交时,FormAction把用户的请求参数绑定到相应的sale属性上并同时验证它们。
<bean id="formAction" class="org.springframework.webflow.action.FormAction"> <property name="formObjectName" value="sale"/> <property name="validator"> <bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/> </property></bean>Spring Web Flow全部的代码、文档和10个示例应用(包括sellitem)都可以在Spring网站上找到http://www.springframework.org/webflow。
现在我们已经看过了如何使用Spring Web Flow来实现一个有状态web应用。接下来让我们更高兴的看如何给我们的示例应用启用高可用性和故障恢复,如何使用Terracotta for Spring进行集群来提供透明容错性和在节点间共享状态。
听上去很难? 你会发现这实际上很简单。
Sellitem示例应用使用一个Sale类的实例来保存当前销售的会话数据;同样Spring Web Flow executor repository 也使用HTTP session 来保存所有的会话数据。
要使用Terracotta for Spring,我们需要确保为给定的web应用启用了HTTP session集群,包括所有可能被保存在 HTTP session中(或者能被保存在HTTP session中的实例引用)的类,以便于检测。这里是Terracotta for Spring的tc-config.xml配置文件的一个例子:
<application> <spring> <jee-application name="swf-sellitem"> <session-support>true</session-support> <instrumented-classes> <include> <class-expression> org.springframework.webflow.samples.sellitem.Sale </class-expression> </include> </instrumented-classes> </jee-application> </spring></application>这里我们为swf-sellitem WAR文件启用了HTTP session集群并配置了Sale类。
就是这样,我们已经做了很多了。
我们唯一需要做的就是在应用中启用Terracotta for Spring运行时。这可以通过修改Tomcat web服务器的启动脚本并在脚本最前面加入下面的环境变量完成:
set JAVA_OPTS=-Xbootclasspath/p:"%DSO_BOOT_JAR%"set JAVA_OPTS=%JAVA_OPTS% -Dtc.install-root="%TC_INSTALL_DIR%"set JAVA_OPTS=%JAVA_OPTS% -Dtc.config="%LOCAL_DIR%/tc-config.xml"这里面:
DSO_BOOT_JAR环境变量指向jar的根目录(能在Terracotta for Spring安装的根目录的common/lib/dso-boot下找到)。TC_INSTALL_DIR环境变量指向Terracotta for Spring安装的根目录。LOCAL_DIR指向包含tc-config.xml的目录.。Sellitem应用预配置了Terracotta for Spring集群的代码可以在下面的'Resources'章节找到。也包括了开箱即用的Tomcat集群配置。
注解:Spring应用上下文中的集群bean可以在服务(bean)级别配置,并依赖于Terracotta for Spring的 auto-include检测机制.例如,大多数情况下我们不需要关心引入哪个类,而只需要在tc-config.xml文件中定义希望集群的bean的名称。
Spring Web Flow 给包括文章中看到的这种简单应用到有很多页面流转的大型企业应用,都提供了构建基于会话的有状态应用的有力手段。 Terracotta for Spring给你的Spring Web Flow提供了高可用性。
简而言之, Terracotta for Spring 提供了:
给基于Spring Web Flow的应用包括普通Spring的应用提供容错性。不需要实现java.lang.Serializable,在多个节点间分布的应用中透明共享状态。在多个分布式节点进行资源调用。在多个分布式节点间保持了Pass-by-reference语义。声明式配置基本上不用修改现有代码 (除了以前是无状态Spring应用,现在需要变成有状态)Spring Web Flow和Terracotta for Spring结合在一起,给你提供了构建有状态、基于会话、可扩展和高可用性web应用的新方式。
Spring Web Flow 主页、下载和文档:http://www.springframework.org/webflow 。示例应用:在文章中使用的Sellitem应用包含在Spring Web Flow发布版本中。Sellitem应用在线演示:http://spring.ervacon.com/swf-sellitem/。Terracotta for Spring Terracotta for Spring是提供JVM级集群开源项目Open Terracotta的一部分: http://terracotta.org/。下载:http://terracotta.org/confluence/display/orgsite/Download示例应用:Sellitem应用包含了Terracotta for Spring简化版本的发布包,它已经预配置了且能直接运行(甚至打包了Tomcat和负载均衡)。文档:http://terracotta.org/confluence/display/docs1/Spring+Quick+Start。查看英文原文:Web Applications with Spring Web Flow and Terracotta for Spring
作者简介:Jonas Bonér在Terracotta Inc.工作,他关注于战略、产品开发与架构和传播技术。他是AspectWerkz AOP框架的创始人,也是Eclipse AspectJ 5 项目和很多其它开源项目的贡献者。他经常做AOP、JVM-level集群和其它新兴技术的发言。
Eugene Kuleshov是一个独立顾问。他有12年的软件设计和开发经验,专注于应用安全、企业集成(EAI)和面向消息中间件。他积极参与了很多开源社区的项目。
译者简介:张俊,网名Pesome,上海交通大学软件工程硕士,多年JavaEE开发经验,积极参与国内开源组织。个人用Spring、Hibernate等开源软件搭建www.openfans.net网站,以Web 2.0的形式介绍开源软件,希望为中国的开源软件事业做点贡献。与InfoQ中文站分享内容,请邮件至。
Chatopera博客 聊天机器人 机器学习 智能客服 北京华夏春松科技有限公司,为企业交付智能客服系统、智能对话机器人、机器人客服、Chatbot。https://www.chatopera.com