為什麼使用Spring Integration
Spring Integration是Spring框架創建的又一個API,面向企業應用集成(EAI)。說到集成,並不缺“解決辦法”:硬編碼的Java客戶端、其它ESB產品,還有消息隊列等更加傳統的應用集成技術。Spring Integration對以上各種解決方法都有所改進,改進的方式有時還頗具戲劇效果。Spring Integration非常輕量、易於測試;幾乎沒有入門門檻,概念上比任何“自己編寫”的解決方法都要簡單。長遠來看,它更為靈活、更具有適應性。一旦使用,你就會戀上它。Spring Integration可以和EJB、RMI、JMS這些標准技術協同使用,能讓你在一處對復雜的解決方法進行建模,從而對標准技術有所增強。這在很大程度上簡化了這些技術的使用。由於Spring Integration非常輕量(與應用一起部署Spring Integration服務器,不用將應用部署到Spring Integration中去),而且很注重開發生命周期(方便配置的XML schema、友好的POJO形式API、與Spring框架和JEE的強大集成),所以你會發現跟其它很多的ESB產品相比,Spring Integration要更加適用。
Spring Integration本身就很強大,毋庸置疑,它從Spring框架中得到了強大的支持。比如說,配置格式無非還是Spring schema,這些配置格式反過來又為你抽象出了bean示例。Spring Integration的使用沒什麼神奇之處,你可以自信地編寫main(String [] args)方法來完成XML配置所做的一切。Spring Integration中很多對RPC和消息的可用支持都以Spring框架的支持為基礎。Spring Integration配置文件中的所有內容仍是標准的Spring 應用上下文,和通常的Spring bean一樣,它也受益於依賴注入和運行時可用的方面(Aspect)。使用Spring Integration,應用上下文就是總線了。比如這能使依賴於應用上下文事件的解決方案成為可能。這是沒有獨立“運行時”的另一個原因,因為只要上下文可用,總線就存在。
背景
企業應用集成(EAI)是集成應用之間數據和服務的一種應用技術。它解決無限的問題,解決方案也幾乎沒有窮盡。工程師們已經為這些解決方案的創建努力了數十年。就在最近,我們才確定了原則的最佳實踐,並對這些方案進行了分類。
現代EAI的模式通常要歸功於Gregor Hohpe等人編著的《企業集成模式》[1],該書對集成解決方案共有的很多集成模式進行了分類和闡述。Hohpe等人列出了四種集成風格:
文件傳輸:兩個系統生成文件,文件的有效負載就是由另一個系統處理的消息。該類風格的例子之一是針對文件輪詢目錄或FTP目錄,並處理該文件。
共享數據庫:兩個系統查詢同一個數據庫以獲取要傳遞的數據。一個例子是你部署了兩個EAR應用,它們的實體類(JPA、Hibernate等)共用同一個表。
遠程過程調用:兩個系統都暴露另一個能調用的服務。該類例子有EJB服務,或SOAP和REST服務。
消息:兩個系統連接到一個公用的消息系統,互相交換數據,並利用消息調用行為。該風格的例子就是眾所周知的中心輻射式的(hub-and-spoke)JMS架構。
這些風格迥然不同,因為沒有一種解決辦法能在任何情況下都良好運轉。這導致整個中間件領域都在基於這些模式尋求可用的解決辦法,通常被稱為企業服務總線(ESB)。ESB是最終的中間人:它知道如何使用各種語言在各種協議上調解傳遞的消息。
這些架構風格是不同的,它們各有所長。通常,JEE標准存在著不足(坦率地說,現今的任何開發平台都是一樣),與其它系統集成時這些標准並不能提供解決方案。考慮到很多項目都是維護項目,新平台中又有多少技術會使用舊服務或功能呢?少之又少,這太令人驚訝了。
JEE以及後來的Spring在簡化企業編程模型方面都有了長足的進步。JEE進行了標准化並商業化了常見企業問題——數據庫訪問、遠程過程調用、事務、認證、目錄服務等等。除了基本的RPC和消息,JEE中並沒有對EAI解決方案的直接支持。
JMS、REST和SOAP都與平台無關,但這是假設有單一的消息協議。比如說,有一個舊的主機應用,其輸入、輸出都是存放在一些FTP端點上的批處理文件,解決方案要求集成該應用就是不可能的。簡單來說:現今的中間件能很好地處理常見問題,但在特殊情況的處理上就有所不足了。對大多數公告板或電子郵件列表,還是采取訂閱流程。通常,用戶給應用發送電子郵件,應用最終接收電子郵件、針對“訂閱”解析郵件、提取發送的郵件,然後在郵件列表中登記用戶後發送響應。第一反應可能就是基於CRON或通過Quartz構建定時器應用,以輪詢電子郵件,或是為稍後使用的批處理文件去檢查FTP。這種辦法很快就會變得單調而脆弱。
之後復雜性會急劇上升。隨著時間的推移,應用變得更為重要,與商業伙伴、其它應用、其它平台集成的負擔也變得更加昂貴。對必須進行維護的系統來說,每次集成都增加了系統間點對點的新通道。最終,集成各個端點的通道就會成為一個維持不了的爛攤子、復雜的架構。
SpringSource的Spring Integration[2]簡化了編程方式,以此改進了標准的ESB。
如何鞏固、梳理架構
企業應用集成有很多模式,同樣有很多需要處理的協議。Spring Integration提供ESB風格解決方案的建模能力,但使用方法及其便利性與Spring框架並無二致。ESB不僅能提供消息解決方案的建模能力,還有其它不同的技術/協議。
ESB中的服務
大多數ESB產品都有一些共性。其中最重要的有:
路由:能按條件邏輯或配置好的路由規則路由消息。
消息傳遞:對任何復雜的解決方案來說,將消息的有效負載從一種類型轉換為另一種類型都至關重要。在消息傳遞中,標准數據模型[3]模式要求系統提供通用的格式。處理消息自然也是ESB的另一個重要組成部分。
調解:適配器和服務映射。
復雜事件處理(CEP):根據相關性將總線上的事件處理為聚集的能力。
調用:這應該是最明顯的一個。每個ESB都要能消費、提供服務。
除了上面列出的服務,ESB通常還要包括記日志、審計、認證(安全)和管理等機制。
如果你的需求更加復雜,那ESB會提供很大的價值。對比你在JEE平台中已經獲得的東西和ESB能帶給你的東西很有價值。下表比較了適合於ESB、可使用JEE作為替代解決方案的常見用例。
ESB 傳統JEE中間件 消息隊列 可通過XML配置,支持所有常見的消息模式。 比如說,如果你使用Spring JMS支持就不會很復雜。 RPC 通常可以消費、提供和提取大部分RPC服務 同樣有無限的可能性,但沒有標准方法。 整合異構系統 ESB旨在分離消費、標准化、提供不同消息過程中的不確定性。 JEE只能很好地支持一些用例。但這些解決方案往往會很快讓事情變得復雜。SOAP消息到FTP?批處理文件記錄到EJB調用?JEE對每一個都只提供了一半的解決方案。 安全 支持良好 支持良好 傳統的主機系統 對JEE等支持的互操作習慣有非常好的支持,包括批處理文件 除了CORBA,JEE對舊系統沒有更多的支持,除非這些系統的前端已經使用了SOAP。 靈活的路由 路由決策在整個被支持的組件中盡可能延遲 JMS、EJB等技術指定可用性和路由的配置不盡相同。很難找到一個鳥瞰圖。 集成非標准需求的易難性 相對容易,特別是用Spring Integration作為所有內容的POJO,也不用和應用服務器集成;相反,你的解決方案在所有ESB產品中並不是標准的 需要深入了解JCA[4],或是類似於BEA Tuxedo[5]這樣一個系統。解決方案在所有JEE應用服務器中都是標准的(盡管結果可能會有所不同)。
流行的解決方案
Spring Integration是新生事物,當然會遭受質疑。Mule[6]是一個非常受歡迎的的ESB產品,值得密切關注。Mule似乎有很大的影響力,在解決方案的靈活性上也令人印象深刻。通過MuleForge[7]實現的開源擴展使其成為幾乎所有問題令人信服的選擇。它是一個標准的服務器:能部署並運行解決方案。Maven插件有助於開發的生命周期。
ServiceMix[8]也比較受歡迎。ServiceMix原本基於Java業務集成規范(JBI;JSR 208[9])。JBI是構建ESB產品的JCP規范。由於出身JBI,ServiceMix不如Mule那般靈活,但它正在進行改進。容器正轉向 OSGi基礎設施。
這裡沒有列出其它所有有價值的ESB產品,要是有機會你還是要對它們多了解一下。有些非常有價值,值得研究。
Spring Integration開箱即用的功能表現得很好,非常易於使用。開發用例非常引人注目:如果你已經被POJO和近年來測試友好的框架寵壞了,那這個框架也是你的拿手好戲。只要你願意,你可以使用接口或標准框架類,你還可以完全為POJO和你的領域模型進行編碼,或者,你可以將兩者結合使用。本文中,我選擇在解決方案中使用一些框架類,以讓發生的事情明確,並提供一致性。有時候,太過不可思議反而會讓人困惑,盡管對入門來說很有成效。
使用入門企業應用集成速成
我們實際構建一個非常普通的應用,以此來演示開發周期。該示例易於理解,但卻沒有斧鑿之嫌。此外,它還是一個很酷的工具。需求是:允許通過電子郵件將博客發送到一個已知地址,然後由該地址發布博客。這麼做有一些好的理由。
Blogger([10]Google的博客服務)已經有這個功能。但我在我博客上運行Apache Roller([11]),所以我可以使用這個解決方案。寫博客的惰性很大程度上是沒時間進入“發布”模式的副作用。Roller的軟件強迫用戶登錄並撰寫博客文章。這麼做很麻煩,尤其是你每次都不得不對付WYIWYG(所見即所得)編輯器。第二,構建該解決辦法最好的理由是,它提供了了一種簡單的集成,能與盡可能少的運轉部分集成在一起。我們可以在本文中仔細分析該辦法。盡管這是假設的,但很有實際用途。
構建集成非常簡單,最好的“IDE”只是一張紙和一支筆。可以在很多工具中畫圖。將圖轉換成你喜歡的ESB配置格式或工具非常簡單。ESB使用相同的術語。了解術語要比了解任何一種工具或ESB更為重要。
讓我們回顧一些ESB 101定義,權當補習了。消息是傳遞到端點的有效負載。端點是通道的終點。通道是兩個端點間被命名的連接。通常,消息來自於消息系統,被分發到不了解消息系統的應用中。同樣的事情或者會反過來以其它方式出現:應用可能會發送數據,但並不理解消息系統。針對這些問題,就需要通道適配器了。
就是這樣!這些就是你需要了解的條目,以便討論解決方案。其它條目都是這些條目之上的變異或詳細說明,或者是關於集成模式的,而不是關於集成本身。
Spring Integration概況
Spring Integration應用就是使用Spring schema配置的簡單Java程序。如果你傾向於用常規的Spring配置來配置一切,就可以不使用schema。Schema會使事情更為簡單,這和用schema配置使用Spring中方面的事務功能會更加簡單大致一樣。Spring Integration為一般概念(集成命名空間)提供了方便的schema,還有適配器的具體配置,比如針對電子郵件或文件類型的配置。
Spring Integration應用處理從通道傳遞過來的消息概念。消息的生命周期始於一個端點,通常是對適配器做出的反應。消息在經過處理管道的過程中,會在總線內被轉化、路由至其它通道、分發、響應,或者被中斷並發送到一個死消息通道中去。如果你使用Spring Integration接口編程——我們將在很大程度上盡可能保持內容一致並明確,那你要處理Message對象,Message對象如圖1所示。
Message類是個包裝器,包裝被處理消息的有效負載。對它進行操作,很容易獲得有效負載和消息頭,你可以對有效負載進行類型轉換,可以檢查、改變消息頭。Spring Integration不要求你使用Message接口。你的應用可以暴露一些方法,這些方法的參數類型跟你消息有效負載的類型相同。比如說,來自於文件適配器(可以從文件系統發送消息的適配器)的消息可能會被改為java.io.File實例。
讓我們來看一個集成例子,它把電子郵件發送到一個電子郵件地址,轉換電子郵件後再將其發布到博客中。
示例的配置在src/main/resources/integrationsContext.xml中。
integrationsContext.xml文件乍看起來很平常。XML頂端的bean負責將屬性文件中的變量宏替換到該XML配置腳本中。後面導入了另一個Spring文件,該文件包含本集成中要使用的簡單服務。再沒什麼特別的了。
接下來繼續看相繼定義的四個bean,它們還是有意思的:服務催化器newBlogPublishingServiceActivator、轉換器 emailToBlogTransformer、處理器outboundBlogPostHandler和過濾器 emailsInFilterAgainstWhitelist。這四個bean會在配置的後面部分裡用到。
實質配置的第一段是poller元素,它是文檔的默認輪詢器。確切來說,輪詢器是一種機制,在有變化時輪詢不同端點,並在感知到某些內容發生變化後讓適配器對其做出響應,就像一個事件。出於簡單,我們為整個Spring Integration配置一個默認的輪詢器。見圖2。
接下來的三個元素是三個通道聲明。他們毫無意義,只是命名的通道而已。沒有端點或適配器的通道毫無用處。見圖3。
下一行配置了電子郵件適配器。電子郵件適配器查找發入系統的電子郵件,並將其放入名叫emailsIn的通道,該通道已經在上面定義了。電子郵件發進來時,電子郵件系統並不會播報事件。在出現異常的時候(比如Spring Integration也支持的IMAP IDLE),你通常需要一些東西來輪詢電子郵件帳戶以查找新的電子郵件。只要有新消息,它們就一次閱讀一條消息,並將該消息傳遞到處理管道的下一個組件。見圖4。
現在消息在emailsIn通道中傳遞,被傳遞到通道的下一個組件——轉換器bean。轉換器獲取所有給它的內容,以某種方式改變消息(我們稍後會詳細討論這一點),然後順便發送出去。這種情況下,會給Transformer給定一個包含MimeMessage類型有效負載的Message,轉化器用於創建BlogPost類型的一個對象。BlogPost是我們系統特定的一個領域類。我們在這裡把它作為DTO來用,但很顯然,它來自哪裡並不重要 ——如果你願意,根據你的領域去處理它也可以。Transformer元素的最佳用法是:將消息格式轉換為系統常見的格式。這跟標准數據模式相關。
由此產生的BlogPost封裝在一條新消息中,其中包含原始消息的頭信息。頭信息正是你期望的那樣:特定於請求的值,可以對其訪問以獲取更多的元數據。例如,從內部來說,Spring Integration總線給每樣內容分配一個ID。該ID暴露為頭信息值供你使用。該ID保證是唯一的。
然後消息被發送給過濾器emailsInFilterAgainstWhitelist,該過濾器訪問有效負載獲取發送者的值。這樣做是為了確保在發布到博客的過程中你不會收到垃圾郵件。這個例子跟整個解決方案一樣,簡單的有些不合常理。你可以想象是去查詢Spring安全或LDAP,或是比這個做得還要好的東西。見圖5:
XML配置的最後一段內容是service-activator元素,它獲取所有給它的內容並進行處理。在這種情況下,我們告知Spring Integration調用ID為newBlogPublishingServiceActivator的bean的 pubishIncomignBlogEntry方法。該方法負責實際獲取有效負載,並將有效負載發送給發布博客條目的服務。
然後我們就大功告成了。內容看起來很多,但實際上就是相關XML的四段內容。聲明通道是為了講清楚例子。poller元素只定義了一次,這樣,上下文中需要輪詢器的所有內容都可以利用這個缺省的輪詢器了。
可能的擴展
我們定義了三個通道、四個組件。通過這樣設計構成解決方案的組件,我們能讓這些組件在其它集成解決方案中可以重用。集成流程不僅僅限於此示例。我們可能會在更復雜的集成中重用轉換器、過濾器,甚至是服務催化器。唯一需要重構的是Spring Integration XML文件。有很多可能性。一個好的ESB應該做到:盡可能晚地重構流程設計和配置。
這裡我們很容易就能想象到可以在什麼地方采用該解決方案。下面是一些可能的補充。
對於某些審計工作流,使用jBPM([12])對系統增加支持。假設我們想在解決方案中添加業務流程管理(BPM),比如在消息傳入時,以某種工作隊列的分類存儲該消息,以便編輯器進行核准。編輯過程可以處理得跟工作流引擎內部要求的一樣。最終的博客內容核准後,編輯器把定稿發送到同樣的電子郵件地址,帶著某種關鍵字或識別出能用作相關ID的關鍵字。相關ID可用來讓集成解決方案繼續進行,主要表現在更新的條目。見[13]。
使用Spring Integration“組播”博客。當然,博客已經發布了。但還有一個問題,用舊的哲學問題來描述再合適不過了:“如果在woods中RSS訂閱已經更新了,而沒有人輪詢到它,那博客算真正更新了嗎?”呃,也許我只是講了大概的意思!也可以通過Twitter傳播博客標題,可以使用接收者列表模式([16])通知博客聚合器([14]、[15]等)。
更新過濾器例子來驗證某種真正授權的服務。也許能使用LDAP或Spring安全。
除了簡單的電子郵件之外,多樣化對發布的支持。FTP、 WebDav、文件系統(在源碼中,電子郵件適配器的下面注釋了一個簡單的文件目錄適配器配置)等都是可行的輸入類型。更先進的例子則可以從移動客戶端發送SMS消息。(當然,我不確定有多少人用他們的手機寫博客。你永遠也不會知道。不過只有一種方式能找到答案。)目前還沒有對此的支持,但閱讀了 Spring Integration的源碼後會發現,很容易構建自己的適配器。你可以使用SMSLib[17]類似的庫。
Spring Integration的不足之處
Spring Integration是全新且強大的。你可以對其背後的SpringSource的價值及其自身的不斷發展抱有信心。但這並不意味著它是完美的——它離完美還遠著哩!應用集成不是一個新的領域,考慮解決方案和架構已經有數十年。應用集成的一些用法按慣例包含了重量級的適配器,比如那些與SAP集成的集成方法或JDEdwards OneWorld。Spring Integration不能直接支持這些具體情況。
盡管Spring Integration支持大量開箱即用的功能,但它對一些典型的適配器缺少支持,比如SFTP、HTTPS或AS2。目前,一些專有的解決方案能更好地支持這些需求。有些解決方案非常昂貴,所以你可以為Spring Integration試著改造第三方庫、編寫自己的適配器。如果你有興趣,你會因為為Spring Integration編寫適配器相當簡單而感到驚喜。你要想開始創建解決方案,只需要看看jSch[18]、Jakarta Commons VFS[19]或Jakarta Commons Net [20]。
最終,Spring Integration可能並不是你現在最佳的解決方案。如果不是,請不要擔心。你在本文中學到的東西也能應用在其他方案中。來吧,集成!
結論
Spring Integration是一個干淨、簡練的EAI手段,也是很好的ESB產品替代者。現在的ESB方案沉重且很難引入到一些環境中。Spring Integration則是一個功能強大、低摩擦的替代方案,它能溫和地解決一些最復雜的集成問題。