標籤:
最近開始WCF相關知識的學習,雖然實際工作中使用公司自己的一套SOA系統,但微軟的一套服務架構還是具有很大的參考意義。除了WCF的一些基礎使用,相對比較複雜的內容有分布式的事務和通訊的安全等,不過基本都和WS-協議簇相關聯。為了引出WCF中的交易處理,今天優先介紹Windows下的交易處理模型,說實話用了很多年的TransactionScope,其實我一直都不知道它到底是如何運作的,回想Java中不管是EJB還是Spring也有和這部分類似的實現。今天通過學習蔣金楠老師的WCF服務架構解析來瞭解這部分的原理。
首先回憶事務的概念和相關應用機制。事務具有ACID(原子性,一致性,隔離性,持久性)特性,主要的應用情境比如銀行轉賬,大部分的資料庫都支援事務操作(SQL Server,Oracle,DB2,MySQL-InnoDB)。事務的實現最常見的有在SQL語句中使用事務BEGIN TRANSACTION, COMMIT TRANSACTION, ROLLBACK TRANSACTION,還可以通過ADO.NET的DbTransaction對象來控制事務,但這些只能支援單個連結的本地事務,而不能支援分散式交易。但實際的項目中,事務的參與者往往不會分布在同一個網路節點,有時是不同類型的事務資源(不僅僅是資料庫,也可以是檔案系統,註冊表等)。分散式交易的典型業務情境有以下三類:將多個資源的訪問納入同一事務;將多個服務納入同一事務(涉及事務在服務間的流轉);將多個資源和服務納入同一事務(如所示)。
接下來進入核心內容Windows下的交易處理模型,交易處理模型一般包括三種角色:
應用(也包括服務、組件等),資源管理員RM(管理具體事務型資源的軟體程式),交易管理員TM(管理整個事務的中介軟體程式)。事務是為了將一組相關操作作為一個不可分割的整體來執行,保證資料的一致性,實際上就是為應用服務的。應用主要負責開始事務&事務的封送(Marshaling)和傳播(Propagation)、提交事務。
事務型資源的存取需要通過資源管理員,按照目標資源是否可以被持久化,相應的資源管理員分為:持久化(Durable)資源管理員,例如資料庫管理員和小心隊列,當交易回復時具有可恢複性;易失(Volatile)資源管理員用於管理類似記憶體的資源,不具可恢複性。其主要職責是協助應用實現對目標資源的操作、註冊到相應的交易管理員便於復原時接受恢複請求、向交易管理員報告本地事務的結果。交易管理員
交易管理員是整個模型的樞紐,協調所有的事務參與者,提供事務的開始、提交和復原服務。Windows提供了三種不同的交易管理員,包括輕量級交易管理員LTM、核心交易管理員KTM和分散式交易協調器DTC。不過從這兒也可以看出事務服務並不具有跨平台特性,因而這部分內容主要作為參考學習,重在理解原理。輕量級交易管理員和核心交易管理員分別負責SQLSERVER和Windows檔案&註冊表,高效但不支援分布式。分散式交易協調器用於管理跨邊界的分散式交易,支援Ole-Tx和WS-AT協議,每一台Windows電腦都具有唯一的DTC用於管理本地所有的資源管理員,支援分布式但效率較低。需要注意的是,事務開始預設LTM作為交易管理員,在其進行的過程中,根據實際的情形會進行事務提升(Transaction Promotion)。例如,涉及到註冊表的讀寫就會升級到KTM,涉及到跨域的封送就會提升到DTC。
那麼我們不經要問,分散式交易到底是如何?的呢?其最關鍵的兩個概念分別是事務登記&事務提交樹(Transaction Commit Tree)和兩階段交易認可協議,接下來分別介紹這兩個概念。
事務登記(Transaction Enlist)的目的在建立事務參與者之間的關係,促進相互間的協作,整個流程如所示:
以上的事務涉及兩台機器,事務由ServiceA開啟,並將其作為當前執行內容的環境事務(Ambient Transaction)。當ServiceA調用本機資源管理員(如SQLSERVER)時,會將該RM納入到本事務中。資源管理員RM向原生DTC進行事務登記,從而DTC於RM建立起上下級關係。當ServiceA在調用ServiceB時,會將當前事務資訊(分散式交易ID和本機DTC資訊)封送,Service接到後取出資訊重建事務,並將其設定為環境事務具有與原事務相同的ID。同時,ServiceB根據得到的MachineA的DTC訊息,讓本機DTC對MachineA的DTC進行事務登記,使得兩台機器DTC建立上下級關係,之後將RM也納入該DTC管理。在登記流程結束後就形成了如下事務提交樹:
在分布式環境下,事務提交需要保證在操作成功時,所有需要持久化的資料被相應資源管理員RM寫入目標資源,而失敗時所有RM中資料要恢複到原始狀態。為了實現這一目標,分散式交易提交需要採用"兩階段交易認可"協議,接下來詳細介紹這兩個階段:
第一階段--準備階段,根節點的DTC向所有事務參與者發起請求,要求他們對本地事務的結果進行投票,如所示遞迴的進行訊息傳播,相應節點反饋就緒、唯讀、終止等投票類型。若所有投票結果為就緒和唯讀就代表提交,如果有任何一個為終止則代表終止提交。同時可以設定逾時時限,若超過此時限整個事務進行復原。
第二階段—提交或者復原,根節點DTC根據投票結果對整個事務發起提交或者終止操作,使用和第一階段相同的訊息傳播方式完成操作。當遇到事務參與者在完成第一階段投票後網路斷開等異常情況時,該子事務處於"未決態"。再重啟此事務後,該DTC會向上級詢問最終結果,如果上級不能確認則繼續向根節點傳播,直到得到回覆,若時間太長系統管理者可以強制提交或終止事務。
此外,為了提高提交效能,協議會根據節點所具有的下級節點數決定是否選用單階段提交協議,就是在節點只有一個唯一下級時直接發起提交。
在完成了原理的剖析後,進入實際的應用,這也是與工作息息相關的部分。WCF基於DTC的Windows事務架構為基礎,提供一個完善的分散式交易解決方案。提供了System.Transactions.Transaction的命令式編程模型和System.Transactions.TransactionScope的聲明式編程方式。
Transaction類時可以序列化封送的,通過EnlistDurable和EnlistVolatile等方法將資源管理員登記到當前事務,構建事務提交樹。Currrent屬性工作表示當前的環境事務(Ambient),其儲存在當前線程的TLS中。TransactionInformation屬性工作表示事務的基本資料,包括建立時間、狀態、本地標識和分布式標識。並通過IsolationLevel表示隔離等級,使用Clone和Rollback方法複製事務和復原事務,需要注意的是事務的開始和結束需要同一個事務來完成,我們把這種事務稱為可提交事務(Commitable Transaction),而其他的相關事務被稱為依賴事務(DependentTransaction)。可提交事務通過TransactionOptions結構體設定逾時時間和隔離等級,也可以通過如下配置設定。
<system.transactions> <defaultSettings timeout="00:01:00"/> <machineSettings maxTimeout="00:10:00"/> </system.transactions> |
可提交事務通過Commit方法和非同步Begin/EndCommit方法提交事務,例子如下所示:
private static void Transfer(string accountFrom, string accountTo, double amount) { Transaction originalTransaction = Transaction.Current; CommittableTransaction ct = new CommittableTransaction(); try { Transaction.Current = ct; Withdraw(accountFrom, amount); Deposit(accountTo, amount); ct.Commit(); } catch { ct.Rollback(); throw; } finally { Transaction.Current = originalTransaction; ct.Dispose(); } } |
在Transaction類中存在一個DepedentClone的方法,該方法用於基於當前事務建立其所對應的依賴事務,也就是其所轄的子事務,將當前線程的環境事務傳遞到新的事務中。接下來的代碼示範了通過依賴事務採用非同步方式進行銀行轉賬。需要注意的是,由於在調用DependentClone時指定的Options參數為BlockCommitUntilComplete,所以主線程在提交事務時,如果依賴事務未結束會一直等待到逾時。還有一個關於事務型方法的實現也很有意思,大家有興趣可以去看蔣老師的原著。
private static void Transfer(string accountFrom, string accountTo, double amount) { Transaction originalTransaction = Transaction.Current; CommittableTransaction ct = new CommittableTransaction(); try { Transaction.Current = ct; ThreadPool.QueueUserWorkItem(state => { Transaction.Current = state as DependentTransaction; try { Withdraw(accountFrom, amount); Deposit(accountTo, amount); (state as DependentTransaction).Complete(); } catch (Exception ex) { Transaction.Current.Rollback(ex); } finally { (state as DependentTransaction).Dispose(); Transaction.Current = null; } }, Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete)); ct.Commit(); } catch { ct.Rollback(); throw; } finally { Transaction.Current = originalTransaction; ct.Dispose(); } } |
最後介紹實際工作中最常見的TransactionScope類,如之前提到的事務型方法就有如下簡易實現。
static void InvokeInTransaction(Action action) { using (TransactionScope ts = new TransactionScope()) { action(); ts.Complete(); } } |
需要注意的是TransactionScopeOption的設定,有如下三種類型:Required,表示如果存在環境事務則使用,否則進入範圍前建立新的事務;RequiresNew,表示總是在該範圍建立新事物;Suppress,表示屏蔽環境事務,即所有操作均在無環境事務的情況下執行。這個J2EE Spring中的REQUIRED 、REQUIRES_NEW 、 NOT_SUPPORTED很相似,不過對於J2EE與DTC相類似的機制個人還未涉獵,之後有機會再和大家分享了。
註:本文主要供自己學習,不妥之處望見諒。
參考資料:
[1]蔣金楠. WCF全面解析[M]. 上海:電子工業出版社, 2012.
WCF學習--Windows事務系統