Spring Web Flow是Spring Framework中的web应用组件它提供了一种编写有状态和基于会话的web应用的简便手段Spring Web Flow使得逻辑流程成为Web应用中的一等公民它能让你定义为自包含模块以独立于应用的其它部分来配置和重用
Spring Web Flow引入了几种有状态数据域requestflashflow和conversation等这让你能用新的方式来开发有状态Web应用它也提供了定制应用状态管理的扩展点
Terracotta for Spring是通过在多个JVM集群来给基于Spring的应用提供高可用性的运行时它给Spring Web Flows的所有域都提供了透明的声明式集群服务(普通的Spring beans同样适用)
在这篇文章中我们会首先给你一个Spring Web Flow和Terracotta for Spring的总体介绍然后会向你展示如何联合使用这些技术来进入构建有状态基于会话可扩展和高可用的Web应用的新领域
什么是Spring Web Flow?
Spring Web Flow是Spring Framework中的web应用组件它提供了一种编写有状态和基于会话的web应用的简便手段Spring Web Flow使得逻辑流程成为web应用中的一等公民它能让你定义为自包含模块以独立于应用的其它部分来配置和重用它不依赖于框架从而能够方便的与可选的web应用框架一同使用比如Spring MVCStruts或者JSF等
页面流转使用一种领域定义语言(DSL)来配置这个语言专门开发用来定义和组合页面流转目前的实现方式是XML和Java
Spring Web Flow引入了能满足多种用户案例和需求的几种有状态数据域requestflashflow和conversation这给你开发有状态web应用提供了很大的灵活性和能力
这里是 release中最有趣特性的快速概要
在一个地方而不是把逻辑分散在很多地方来定义应用任务的所有控制逻辑比如一个搜索流程
把简单的流转组合在一起来创建富控制模块
使用自然和面向对象的线性编程模型而不是冗长的的if/else块来定义严格的用户导航规则
但流转结束或过期时自动清除你在流转执行中分配的内存
在使用你选择的基础web框架的Servlet环境中Deploy一个可执行的流转
改变web框架(比如StrutsSpring MVCJSF及其它)而不用修改流转定义
和环境一起改变而不需要修改你的流转定义 比如从JUnit测试到Portlet
开发时在不重启容器的情况下不断完善你的应用导航规则
自动正确响应浏览器按钮(后退前进刷新)而不需要定制编程
在个受管理域中存储任务数据requestflashflow和conversation等每个都有自己的独特语义
脱离容器单独测试流转能在部署前确保应用控制逻辑能正常运作
使用Spring IDE 进行可视化编辑你的流转导航逻辑图
听上去很有趣?到目前为止还仅仅是概念和理论但我们很快会看到这些都能在实践中应用所以请多等一会
企业对扩展性和高可用性的需求
集群在企业应用开发中变得越来越重要开发人员经常会碰到这样的问题
我们如何在一个节点上扩展来提高应用的可扩展性?
如果保证高可用性和消除单点故障如何确保我们能满足客户的SLAs(Service Level Agreement)?
为了支撑业务可预测的扩展性和高可用性是一个生产应用必须展示的运行特性一些企业需要超过%的正常运行时间另一些不要求这么高但所有的应用都需要保证SLA规定的可运行性从预测的角度看开发一个系统在%和%级别是一样困难的
集群一直以来都是难以解决的问题在Spring Web Flow和普通web应用上下文中这尤其意味着保证用户状态的高可用性和故障恢复能力是满足性能且值得信赖的在一个节点出现故障(应用服务器或JVM崩溃)使用session粘滞(这是配置负载均衡最通用的首选方式)是一个开始但我们需要一个有效手段来无缝的将用户状态从一个节点迁移到另一个节点
当我们说集群时意味着什么它和缓存有什么区别?我们使用的集群定义是: 在多个JMV间共享应用状态而缓存可以被定义为 让应用状态靠近执行上下文从这个角度看缓存是集群的一个子集
我们所认为的企业级Web/企业集群最小解决方案集至少需要包括
可扩展性
高可用性
failover
性能
对已有代码的最小影响
简单的部署和配置
可见运行时(监控)
让我们关注一个能解决所有这些看上去无关的需求的解决方案通常解决一个问题有很多方案而且市场上也有很多宣称能给web应用提供高可用性的产品Terracotta就提供了这样一个解决方案
什么是Terracotta for Spring?
Terracotta for Spring是基于Spring应用的运行时它为Spring应用提供了透明的高性能集群支持对应用代码和部署及配置流程影响都很小它通过在应用下面的堆级别进行集群而不是直接集群应用
这让开发者能够开发与无状态方式不同的单节点有状态Spring应用这使得在需要扩展的应用开始设计时不考虑集群而在应用需要扩展或者要保证搞可用性和故障恢复时他们只需要在Terracotta配置文件中定义哪些Spring应用上下文中的beans需要进行集群Terracotta for Spring使得应用能够被自动和透明的集群还保证在集群间的语义和单节点一样
对于Spring Web Flow来说这实际上更简单用户为了获得web应用的状态和持续仓库的集群能力 只需要在Terracotta配置文件中把特定的web应用声明为启用sessionsupport(详细内容见下面的章节声明式配置)
从宏观上看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)
虚拟内存管理它也提供分布式垃圾收集和虚拟堆功能比如由于物理内存在需要时被换入换出它能在一个G RAM的机器上运行需要G堆的Web应用 这也意味着你不需要关心Spring Web Flow会话数据的大小是否超过了物理堆大小
堆层次集群
Terracotta for Spring使用aspectoriented技术来在类载入时适配应用在这个阶段它扩展了应用以保证Java语义在集群间被正确的维护包括对象引用进程调用和垃圾收集等
我们在前面提到Terracotta并没有使用serialization这意味着任何Spring Web Flow维护的会话都可以在集群间共享这也意味着Terracotta并没有把会话状态的全部对象图发给所有节点而是把图分解成纯粹的数据并在网络间传输实际的差量和改变数据的原始信息在其它节点上
因为有一个记录节点间相互引用的中心调度器(见下文)它能以lazy的方式工作而只把改变传送到引用了这些dirty数据的节点这需要使用局部引用如果负载均衡配置为使用session粘滞 就更有效率因为这意味着很多数据可能永远不会脱离实际的session而不用复制到其它节点
这个架构是中心辐射的也就是有一个管理客户端的中心调度器在这里客户端就是你配有Terracotta for Spring运行时的普通应用调度器不是单点失败的但你可以配置一组备用调度器并在主调度器崩溃时选择一个来接替你也可以独立于客户端对调度器进行集群扩展
构建一个高可用的有状态web应用
这里我们使用一个叫Sellitem的示例应用来推动讨论并展示给大家
如何使用Spring Web Flow来构建一个有状态基于会话的web应用
如何使用Terracotta for Spring来声明式集群有状态应用
Sellitem示例应用可以在Spring Web Flow的发布版本中找到(更多信息见文章末尾的Resources章节)
使用Spring Web Flow实现一个有状态Web应用: Sellitem
Sellitem是展示结合了有条件转移会话域流程执行转向和延续性的示例应用用户在几个页面间导航可以定义货物的价格可以销售的货物数量折扣比率送货详情(如果需要)和最后查看所有信息
我们不会通读应用的所有源码而是主要介绍一些关键概念如怎么配置Spring Web Flow输出的不同标准服务(Bean)和怎么定义页面流转(使用针对页面流转的Spring Web FlowDSL的XML版本)
在Spring MVC中配置DispatcherServlet
应用的入口是一个标准的Spring MVC DispatcherServlet它在webxml中注册并在web application context中映射到
sellitem
orgspringframeworkwebservletDispatcherServlet
contextConfigLocation
/WEBINF/sellitemservletconfigxml
/WEBINF/sellitemwebflowconfigxml
sellitem
DispatcherServlet配置在Spring的配置文件sellitemservletconfigxml和sellitemwebflowconfigxml中 sellitemservletconfigxml中有一个映射到/的控制器它将所有该URL的请求转发到Spring Web Flow系统(它的入口是一个流程执行器)
配置flow executor和flow registry beans
Spring flowExecutor bean配置使用一个flowRegistry bean来执行/WEBINF/flows/目录中的基于XML的流转定义
定义页面流转
剩余的逻辑在我们已经注册的flowRegistry bean的流转定义中(参照前面的配置flow executor和flow registry beans章节)
在深入流转实现细节前我们先看一下页面流转的状态图(如下图)
从上面我们可以看到流转在结束前经过了几个步骤在决定销售是否需要送货时有一个决策状态
一个很好的针对上面导航规则的初始流转定义实现如下
xmlns:xsi=instance
xsi:schemaLocation=
webflowxsd>
我们从上面的定义可以看到实际状态与状态图中的状态对应状态转换与图中的箭头对应sale bean是流转开始时分配的流转变量实例它持有了Sale相关的属性
上面的定义展示了所有的导航逻辑但还没有实现任何应用行为特别是在用户提交时更新Sale Bean的逻辑还没有实现另外后台的sale处理逻辑还没有定义
实现了所有必需行为的完整Spring Web Flow定义如下
xmlns:xsi=instance
xsi:schemaLocation=
webflowxsd
在定义导航逻辑之外也定义了适时调用恰当应用行为的action这包括处理用户提交事件和调用后台处理器来处理sale的逻辑
Form绑定和验证
当进入展示表单的视图状态时流转调用一个FormAction command bean来进行表单的装配和提交逻辑在提交时FormAction把用户的请求参数绑定到相应的sale属性上并同时验证它们
更多信息
Spring Web Flow全部的代码文档和个示例应用(包括sellitem)都可以在Spring网站上找到
集群Sellitem应用
现在我们已经看过了如何使用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的tcconfigxml配置文件的一个例子
name=swfsellitem>
true
orgspringframeworkwebflowsamplessellitemSale
这里我们为swfsellitem WAR文件启用了HTTP session集群并配置了Sale类
就是这样我们已经做了很多了
启用Terracotta
我们唯一需要做的就是在应用中启用Terracotta for Spring运行时这可以通过修改Tomcat web服务器的启动脚本并在脚本最前面加入下面的环境变量完成
set JAVA_OPTS=Xbootclasspath/p:%DSO_BOOT_JAR%
set JAVA_OPTS=%JAVA_OPTS% Dtcinstallroot=%TC_INSTALL_DIR%
set JAVA_OPTS=%JAVA_OPTS% nfig=%LOCAL_DIR%\tcconfigxml
这里面
DSO_BOOT_JAR环境变量指向jar的根目录(能在Terracotta for Spring安装的根目录的common/lib/dsoboot下找到)
TC_INSTALL_DIR环境变量指向Terracotta for Spring安装的根目录
LOCAL_DIR指向包含tcconfigxml的目录
Sellitem应用预配置了Terracotta for Spring集群的代码可以在下面的Resources章节找到也包括了开箱即用的Tomcat集群配置
注解Spring应用上下文中的集群bean可以在服务(bean)级别配置并依赖于Terracotta for Spring的 autoinclude检测机制例如大多数情况下我们不需要关心引入哪个类而只需要在tcconfigxml文件中定义希望集群的bean的名称
总结
Spring Web Flow 给包括文章中看到的这种简单应用到有很多页面流转的大型企业应用都提供了构建基于会话的有状态应用的有力手段 Terracotta for Spring给你的Spring Web Flow提供了高可用性
简而言之 Terracotta for Spring提供了
给基于Spring Web Flow的应用包括普通Spring的应用提供容错性
不需要实现javalangSerializable在多个节点间分布的应用中透明共享状态
在多个分布式节点进行资源调用
在多个分布式节点间保持了Passbyreference语义
声明式配置基本上不用修改现有代码(除了以前是无状态Spring应用现在需要变成有状态)
Spring Web Flow和Terracotta for Spring结合在一起给你提供了构建有状态基于会话可扩展和高可用性web应用的新方式