程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 用Spring Web Flow和Terracotta搭建Web應用

用Spring Web Flow和Terracotta搭建Web應用

編輯:關於JAVA

Spring Web Flow 引入了幾種有狀態數據域:request、flash、flow和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 MVC、Struts或者JSF等。

頁面流轉使用一種領域定義語言(DSL)來配置,這個語言專門開發用來定義和組合頁面流轉。目前的實現方式是XML和Java.

Spring Web Flow引入了能滿足多種用戶案例和需求的幾種有狀態數據域:request、flash、flow和conversation,這給你開發有狀態web應用提供了很大的靈活性和能力。

這裡是1.0 release中最有趣特性的快速概要。(來自release notes on InfoQ):

在一個地方而不是把邏輯分散在很多地方來定義應用任務的所有控制邏輯,比如一個搜索流程。

把簡單的流轉組合在一起來創建富控制模塊。

使用自然和面向對象的線性編程模型,而不是冗長的的if/else塊來定義嚴格的用戶導航規則。

但流轉結束或過期時自動清除你在流轉執行中分配的內存。

在使用你選擇的基礎web框架的Servlet環境中Deploy一個可執行的流轉。

改變web框架(比如Struts、Spring MVC、JSF及其它)而不用修改流轉定義。

和環境一起改變而不需要修改你的流轉定義, 比如從JUnit測試到Portlet。

開發時在不重啟容器的情況下不斷完善你的應用導航規則。

自動正確響應浏覽器按鈕(後退、前進、刷新)而不需要定制編程。

在4個受管理域中存儲任務數據:request、flash、flow、和、conversation等,每個都有自己的獨特語義。

脫離容器單獨測試流轉。能在部署前確保應用控制邏輯能正常運作。

使用Spring IDE 2.0進行可視化編輯你的流轉導航邏輯圖

聽上去很有趣?到目前為止還僅僅是概念和理論,但我們很快會看到這些都能在實踐中應用。所以請多等一會。

企業對擴展性和高可用性的需求

集群在企業應用開發中變得越來越重要,開發人員經常會碰到這樣的問題:

我們如何在一個節點上擴展來提高應用的可擴展性?

如果保證高可用性和消除單點故障,如何確保我們能滿足客戶的SLAs(Service Level Agreement)?

為了支撐業務,可預測的擴展性和高可用性是一個生產應用必須展示的運行特性。一些企業需要超過99.9999%的正常運行時間,另一些不要求這麼高,但所有的應用都需要保證SLA規定的可運行性。從預測的角度看,開發一個系統在99.9%和99.9999%級別是一樣困難的。

集群一直以來都是難以解決的問題。在Spring Web Flow和普通web應用上下文中,這尤其意味著保證用戶狀態的高可用性和故障恢復能力是滿足性能且值得信賴的。在一個節點出現故障(應用服務器或JVM崩潰),使用session粘滯(這是配置負載均衡最通用的首選方式)是一個開始,但我們需要一個有效手段來無縫的將用戶狀態從一個節點遷移到另一個節點。

當我們說集群時意味著什麼,它和緩存有什麼區別?我們使用的集群定義是: 在多個JMV間共享應用狀態,而緩存可以被定義為: 讓應用狀態靠近執行上下文。從這個角度看,緩存是集群的一個子集。

我們所認為的企業級Web/企業集群最小解決方案集至少需要包括:

可擴展性

高可用性

fail-over

性能

對已有代碼的最小影響

簡單的部署和配置

可見運行時(監控)

讓我們關注一個能解決所有這些看上去無關的需求的解決方案。通常解決一個問題有很多方案,而且市場上也有很多宣稱能給web應用提供高可用性的產品。Terracotta就提供了這樣一個解決方案。

什麼是Terracotta for Spring?

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 運行時的普通應用。調度器不是單點失敗的,但你可以配置一組備用調度器,並在主調度器崩潰時選擇一個來接替。你也可以獨立於客戶端,對調度器進行集群擴展。

構建一個高可用的有狀態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,它在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" />

配置flow executor和flow registry beans

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的邏輯。

Form綁定和驗證

當進入展示表單的視圖狀態時,流轉調用一個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。

集群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的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

我們唯一需要做的就是在應用中啟用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應用的新方式。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved