轉載:http://www.griddss.cn/show.aspx?id=127&cid=7
因為這個問題讨論起來内容比較多一些,是以另開一個話題。
如果你隻是要解決兩個系統之間的事務同步問題,可以采用判斷服務是否成功的辦法來解決,即:
* A系統開始自己的事務,處理自己的資料,然後。。。
* 在送出之前調用B系統的服務。
* B系統開始自己的事務B,在事務中處理資料,再送出事務。
* B系統把自己事務的送出成功與否的資訊做為傳回值回饋A系統。
* A系統根據B的事務成功情況決定自己的事務是否送出或是復原。
但是,在繼續深入讨論這個問題之前,先反問一個引伸的問題:當分布式系統之間,要進行事務控制的子系統不是兩個,而是N個時,如果進行事務控制?
分布式事務一直都是很難解決的問題。在面向DCOM的分布式應用中,有一種分布帶事務支援政策,大體的思路是采用兩段式事務送出的辦法,第一次送出是預送出,預送出之後是可以復原的。第二次送出是永久性的送出,送出之後就不可以復原。并且,如果預送出成功,第二次送出也必然成功,系統必須可以保證這一點。
這樣,當每個系統都支援這種兩段式送出之後,就可以采用這樣的事務管理:一個控制角色向每個分布系統提出執行要求,并要求完成第一次事務送出。當每個系統的第一次送出都成功時,則要求所有系統完成最後的永久送出,可知這次的永久送出是肯定可以完成的,是以不須要再擔心這次送出是否成功。
如果第一次送出中,有某些應用出現失敗,則要求所有的應用都復原事務。
一些資料庫軟體本身就支援事務嵌套,如sqlserver等,不幸的是,我們的主力資料庫informix不支援。
為了簡化這種分布式事務管理,有一些中間件産品可以采用,用得比較廣泛的是MS DTS.
你可能已經看出來了,這樣的事務控制政策雖然可以在分布式環境下滿足事務的ACID要求,但是它對各個分布元件是有要求的,在基于COM, remoting,JRMI一類技術的分布式應用程式中,這個沒有問題。但是在采用web service的場景中,這是有問題的。
問題1. web service是一種以松耦合為指導思想的內建方式,一般情況下,主張采用無狀态方法。
webservice主張兩次調用之間沒有上下文關系,即一次調用與其他之前和之後的調用都沒有關系,一次送出即完成一次完整的處理。但是分布式事務卻要求各方要在兩次對話之間保持對話狀态,以便于知道本次永久性送出時,要對之前“哪一個”已經被預送出成功的事務執行最後的送出。
當遇到這個問題時,我們必須要再多問自己一個問題:我們已經選擇了正确的內建技術嗎?如果多個系統之間有如此緊密的事務耦合關系時,我很懷疑它們其實就是同一個應用系統。同一個應用系統中,應該有相同的平台,相同的程序空間,相同的資料模型以及資料源。這種情況下,采用web service是一種錯誤的選擇,web service應該用于不同平台、不同應用、不同的資料模型的系統內建。即便是的确需要在同一個應用系統中由于某些原因而實作子產品間的分布式構造,也應該采用同一技術平台内的遠過程通路技術,它們能通常比web service能提供更好的耦合性支援。
好吧,假設你經過思考之後,對上述問題的回答是“是”:我們确實必須要在異構的、多平台的、本來應該是低耦合系統之間實作分布式事務控制。那麼,webservice還有用處嗎?
謝天謝地,web service雖然主張互動之間采無狀态方式,但是它并不是禁止采用有狀态的互動。WEB SERVICE還是一種web技術,而web技術中的狀态儲存可能是最早被解決的問題之一了。在所有的web開發技術平台中,都有session機制,無論這些Session是通過IP,cookies, hidden input來實作,還是url sessionid來實作的,反正都有辦法實作,請參閱所用平台的session支援機制就可以了。退一萬步,你也可以通過在伺服器中維護一個應用程式級的事務池來實作,未最後送出的事務對象都放在裡面,每一個事務對象都給定一個唯一個的标志ID來識别,形成一個字典對象池。如果啟動事務成功,則把此事務的ID傳回給調用者執有,做第二段送出時,把事務的ID做為參數送出就是了。(随便提一下,用這種方法時,千萬不能把對象的指針、句柄、引用什麼的平台相關的值交給客戶方,倒不是害怕安全問題,而是這些值在分布系統中是沒有意義的,上次傳回的指針沒準早被垃圾收集機挪到其他地方去了)
無論如何,webservice在通信層上是一種無連接配接的協定,每兩次調用之間,tcp連接配接是斷開的,是以,一但采用session機制來管理上下文,你就必須為這些session的生命期負責。試想,如果一個事務上下文已經開啟,而此時客戶方系統卻突然當機了,這時會出什麼事情?在同一個應用程式域中,客戶方的當機會讓連接配接中斷,伺服器立即就會中斷并回退事務,但是在webservice裡,狀态管理機無法立即感覺到此事務的調用方已經失去控制,隻能在一定的時間之後,才發現:“噫?這個事務已經N長時間沒有人通路了!快快回退!”在ASP.net裡,預設的狀态逾時時長大概是20分鐘,JSP也差不多,阻塞了20分鐘的事務對資料源是什麼影響可想而止!是以,必須考慮合适的狀态時長與事務隔離級别,以減小對資料源的性能影響。
問題2. web service的“反模式”方法論使得無法在系統之間統一出共同的抽象接口。
web service是一種“反模式”的系統架構思想,即不是一般的由先模組化并抽象接口開始,再由各個分布系統實作接口的系統構造方式,而是反過來:系統可能早已經完成,現在的問題是兩個系統間的資訊互動作用,是以互動的接口規格是根據需要,把系統資料模型去範式化後挑挑撿撿而定的。
是以,webservice中不支援接口抽象,即:你無法定義一個各個系統都必須實作的抽象事務接口,然後由各個系統實作這個接口的多态,最後在承擔事務控制器的應用中調用統一事務接口以排程分布事務。雖然這樣的接口模型在很多面向對象的開發平台中的遠過程調用技術中所支援,但是如同之前說過的,web service是一種用于內建的松耦合的反模式方法論,而不是為緊耦合系統中的分布式對象而設計的。
是以,雖然有點讨人煩,但是我又一次忍不住想問我已經問過的那個問題:我們真得用對了技術嗎?如果多個系統之間需要如此級别的接口耦合性,我真得越來越懷疑它們其實就是同一個應用系統了。
假設你的回答還是“是,他們真得不是同一個系統,他們是異構平台的,異構資料的!”好吧,那麼繼續。讓我們采用web service來完成內建,但是你必須忘記你的OOP思想,老老實實地編碼,用枯燥的、重複的代碼把所有的系統的事務都控制在一起,别想用對象抽象的概念來省一點事。
真的嗎?
如果把事務控制器獨立出來如何?假設我們建立一個專用于分布式服務控制的應用,而用WEB SERVICE的方式公布接口, 允許其他應用程式通過向這個事務控制器注冊自己的兩段式事務開啟、送出和回退的web service接口。然後,當有客戶想啟動分布事務時,就可以向這個事務控制器發起分布式事務請求,選擇事務各方,啟動一個分布式,最後向事務控制器,而非是各個事務方直接發起送出請求,這樣事務控制的多态就可以在事務控制伺服器中實作,雖然實作可能還是通過查表等方式實作,而非平台級的抽象方法,但是對于事務客戶來講,這樣一個伺服器就是多态的實作部分。
如果真得比MS更快更好地實作這樣一個web service做接口,面向異構系統的分布式事務控制器,NASDAQ也許會有你的一席之地吧!
問題3. 異構平台不一定都支援兩段事務送出模式。
web service面向的是完全異構平台的內建,那麼顯然不能指望每個平台都能支援兩段時送出事務模式。但是,标準就是标準,協定就是協定,标準就是用來讓大家遵守的,如果一個平台本來不支援兩段式事務,那麼為了能支援分布式事務,它就必須改造以實作兩段式事務送出。
怎麼改造是各個應用系統内部的事情,為了本文讨論的全整性,也在這裡稍微涉及一下。
首選的方式是通過資料緩存的方式來實作。很多OO系統中,都采用了所謂的N層架構,即把業務對象與關系表模型分離開來,業務對象位于系統記憶體或是緩存中,由運作時的對象容器管理,容器根據一定的政策,把緩存中業務對象向資料庫這樣的久永媒體中儲存,或是從資料庫中加載所需要的業務對象,在儲存和加載過程中,将完成對象到表資料的轉換,或是相反。
一般的N層結構的中間件産品中,都會提供兩個級别的事務,即面向緩存中對象的事務控制和面向持久化過程的事務,可以考慮簡單地将此兩個事務級别對應的分布事務中的兩段事務送出。但是,這種方式必須冒一定的風險,如對象容器級的事務成功,而資料庫事務送出時出現失敗,此時将會導緻的資料不一緻的風險,盡管這個幾率并不很大。
在使用資料容器的情況下,也可以用儲存對象的曆史狀态來實作事務的手工回退。因為在業務對象層與持久化層相分離之後,持久化層在資料更新時并沒有複雜的邏輯,隻是一些被羅列的、業務意義無關的資料更新序列。如果可以保持對象的狀态曆史,那麼就可以在需要的時候将對象的狀态恢複到舊的舊版上。實際上,在一些出色的中間件平台中,這個機制已經實作得非常完善了。(可以參閱Graphtalk平台的對象持久化管理,簡直是天才!)
另一種笨辦法是通過資料邏輯來實作兩段事務送出,例如在要求第一次送出時,即真正送出,在第二次送出時固定什麼也不做,而傳回正常。如果要求回退,那麼就通過資料邏輯或是業務邏輯來更新資料為舊狀态。這種實作方式絕對是很令人頭痛的。
不過,幸虧我們不是在為一個通用的資料庫設計兩段事務機制。要知道,面向服務的事務處理并不是如同資料庫級别的事務那樣,在事務的期間資料的操作有無窮的可能性。通常我們一個服務就是一個功能,其資料操作過程中,資料的變化方式是可預知的,是以恢複資料的狀态也是一個個具體而固定的過程,隻要我們針對每一個服務操作設計資料恢複機能就是了。
最後,如果這些都不可能實作的話——大于50%的可能性,因為時間、成本、技術等原因,這些都實作不了,那麼隻能靠兩個字了:妥協。