今天我們繼續學習WCF分布式開發步步為贏系列的12節:WCF事務機制(Transaction)和分布式事務編程。眾所周知,應用系統開發過程中,事務是一個重要的概念。它是保證數據與服務可靠性的重要機制。作為面向服務應用的開發平台,WCF也提供了對事物編程模型的支持。.NET 2.0提供的System.Transactions類來開發事務應用程序。同樣WCF也支持事務特性,WCF事務機制是什麼,它與微軟已有的技術如Microsoft 分布式事務協調器 (MSDTC)有何關系?與Enterpise Services(微軟應用程序服務器技術)和COM+的事物機制有何區別,這篇文章會詳細介紹,今天我們就來學習一下WCF事務處理機制和如何在項目中使用這一特性。全文的結構【1】事務概念 【2】事務屬性、【3】事務協議、【4】事務管理器、【5】事務編程、【6】示例代碼講解、【7】總結。
WCF提供的事務機制。其實除了利用已有的.NET 框架提供的事務機制外,還根據自身需求進行了擴展。這個問題不難理解。WCF編程模型告訴我們,WCF的應用通常包含客戶系統、WCF服務系統。除了可以借助SQL Server等RDBMS內部的事務機制來實現事務以外,還可以使用NET 2.0提供的System.Transactions類來實現事務處理。這種事務僅僅存在於服務端或者客戶端。而WCF要求通常要實現客戶端與多個服務端之間操作的事務約束,也就是通常所說的分布式事務。WCF沒有完全重新開發一套框架來實現分布式事務。這裡它借助了微軟早期的技術MSDTC分布式事務協調器來實現的分布式事務。下面我們也會介紹。
首先我們來回顧一下事務的感念:
【1】事務概念 :
什麼是事務呢?其實這個事一個數據庫系統中的一個概念。事務(Transaction)是並發控制的基本單位。所謂事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。
例如,銀行轉帳:通常包括兩個操作:
(1)從一個帳號A扣款;
(2)使另一個帳號B增款。
這兩個操作要麼都執行,要麼都不執行。在銀行系統裡,數據庫系統執行相關的命令來完成兩個操作。事務是數據庫維護數據一致性的單位,在每個事務結束時,都能保證數據一致性。連個賬號的金額不會出現錯誤。保證轉賬操作的正確完成。
【2】事務屬性:
事務也有自己的特性。這個大家都非常的熟悉。相信每個數據庫相關的書籍都會介紹事務的特性ACID。事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢復並使應用程序更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性:
<1>原子性:
事務必須是原子工作單元;對於其數據修改,要麼全都執行,要麼全都不執行。通常,與某個事務關聯的操作具有共同的目標,並且是相互依賴的。一個事務要被完全的無二義性的做完或撤消。在任何操作出現一個錯誤的情況下,構成事務的所有操作的效果必須被撤消,數據應被回滾到以前的狀態。比如轉賬事務中的兩個操作,要麼全執行,要麼全部執行。
<2>一致性:
事務在完成時,必須使所有的數據都保持一致狀態。在相關數據庫中,所有規則都必須應用於事務的修改,以保持所有數據的完整性。事務結束時,所有的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發人員承擔,他們必須確保應用程序已強制所有已知的完整性約束。例如,A賬戶裡10000元,B賬戶0元,轉賬成功以後。A和B賬戶的總額保持不變。還是10000元。不會因為轉賬成功就增加了總額。
<3>隔離性:
由並發事務所作的修改必須與任何其它並發事務所作的修改隔離。事務查看數據時數據所處的狀態,要麼是另一並發事務修改它之前的狀態,要麼是另一事務修改它之後的狀態,事務不會查看中間狀態的數據。這稱為可串行性,因為它能夠重新裝載起始數據,並且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將獲得最高的隔離級別。在此級別上,從一組可並行執行的事務獲得的結果與通過連續運行每個事務所獲得的結果相同。串行執行事務:在一個事務執行過程中,數據的中間的(可能不一致)狀態不應該被暴露給所有的其他事務。
兩個並發的事務應該不能操作同一項數據。數據庫管理系統通常使用鎖來實現隔離。
<4>持久性:
WCF 支持分布式事務,也就是說事務可以跨越服務邊界、進程、機器、網絡,在多個客戶端和服務之間存在。而與此對應的事務數據信息傳播和管理的協議不同。
【3】事務協議:
WCF 使用不同的事務協議來控制事務執行范圍(execution scope)。 事務協議的出現時為了實現分布式環境事務傳播。
1).Lightweight: 僅能在同一程序域的上下文中傳遞事務,無法跨越程序域和服務邊界。只能在服務內部或外部適用,同時它也是性能最好的一種協議。不過這種協議似乎沒什麼用處,因為 WCF Framework 中沒有任何一種 Binding 支持此協議。
2).OleTx: 允許事務跨越程序域、進程或機器邊界。使用 RPC 調用,采取 Windows 專用二進制格式。無法跨越防火牆,也不能和其他異種平台進行整合。多用於 Windows 體系的 Intranet 環境。
3).WS-Atomic(WSAT): 和 OleTx 相似,同樣允許事務跨越程序域、進程或機器邊界。和 OleTx 不同,WSAT 是一種工業標准,采取 HTTP 協議,TEXT 編碼,可以跨越防火牆。雖然 WSAT 也能用於 Intranet,但多數時候它用於 Internet 環境。
事務協議的配置只有在事務傳播的情況下才有意義。WCF在預定義綁定中實現了標准的WSAtomicTransaction(WS-AT)協議和Microsoft專有的OleTx協議,這些協議可以用來在消息中加入事務狀態的信息。WS綁定可以使用多個WSAT事務協調器,跨越Internet。但是如果只有一個事務協調器,OleTx協議將是默認的協議。我們可以編程或者配置文件設置事務協議。
<bindings>
<netTcpBinding>
<binding name = "TransactionalNetTCP"
transactionFlow = "true"
transactionProtocol = "WSAtomicTransactionOctober2004"
/>
</netTcpBinding>
</bindings>
【4】事務管理器:
分布式事務的實現要依靠第三方事務管理器來實現。他負責管理個個事務的執行情況。最後根據全部的事務執行結果,決定提交或者回滾整個事務。這個也就是通常所說的兩階段提交協議。通常來收事務管理器有3種:LTM、KTM、DTC。他們應用的場合不同。由於事務有本地和分布式事務的區別。所以三種協議使用的場合也不相同。下面依次詳細介紹:
【4.1】LTM:輕量級事務管理器,它只能管理本地事務,單個應用程序域中的事務,它根據輕量級事務協議來管理和實現兩階段提交協議。 LTM是一種高效的資源管理器。它只能管理本地事務。在.NET2.0中經常使用。WCF事務編程中我們可以使用其來管理本地事務。
【4.2】KTM:在Vista核心中的新組件,其目的是方便進行大量的錯誤恢復工作,而且過程幾乎是透明的,而KTM之所以可以做到這一點,是因為它可以作為事務客戶端接入的一個事務管理器進行工作。與LTM一樣,KTM只能管理一個本地服務的事務。而且不支持事務傳播給別的服務。
【4.3】DTC:.NET Framework 依靠 MTS/COM+ 服務來支持自動事務。COM+ 使用 Microsoft Distributed Transaction Coordinator (DTC) 作為事務管理器和事務協調器在分布式環境中運行事務。分布式事務協調器 (DTC) 服務可協調更新兩個或多個受事務保護的資源的事務, 如數據庫、消息隊列、文件系統等等。這些受事務保護的資源可能位於單個計算機上,或分布在許多網絡計算機上。DTC可以使用OleTx或者WSAT協議。WCF可以借助DTC實現分布式事務機制。DTC可以創建事務、傳播事務信息、收集全部事務的結果、通知事務管理器提交或者回滾事務。
在分布式事務中,事務管理器A會向參與事務的其他機器發出調用請求。其它機器攔截請求。獲取事務ID,啟動本地事務。其他機器同時啟動本地資源管理器登記。執行兩階段提交協議。最後根據全部的結果。執行第二階段是否提交和回滾。DTC管理分布式事務如圖所示:
事務資源管理器會根據事物執行的實際情況和需求進行提升。最初的事務由LTM管理,這樣能獲得最好的性能;
如果事務訪問的是KRM資源,開始會由KTM管理事務。當事務訪問其它持久化資源或者其它事務並傳播事務時,事務就會提升為DTC事務。事務的如果為舊資源,管理器會自動提升為DTC,資源與事務管理器之間的關系如下表:
Resource
LTM
KTM
DTC
Volatile
Yes
Yes
Yes
SQL Server 2005
Yes
No
Yes
Kernel
No
Yes
Yes
Any other RM
No
No
Yes
本地事務ID和分布式事務ID都可以通過事務類的屬性TransactionInformation獲得。string LocalIdentifier和Guid DistributedIdentifier。
【5】事務編程:
下面我們來介紹一下WCF事務編程。WCF事務范圍可以涉及到客戶端、服務端。當然這個取決於你項目具體的配置。在WCF的事務模式主要由綁定協議、事務流屬性、事務范圍屬性決定。在WCF所有的綁定協議中不是所有的協議都支持事務。事務流屬性TransactionFlowAttribute 只能用於服務方法(Operation/Method)上,它允許我們進行不同的事務參與設置。注意不能為 IsOneWay=true 的服務設置事務流支持:
TransactionFlowOption.NotAllowed: 不參與任何事務。(默認值)
TransactionFlowOption.Allowed: 允許參與事務。如果調用方(客戶端)和服務Binding啟用了事務,則參與。
TransactionFlowOption.Mandatory: 強制啟用事務。調用方(客戶端)和服務 Binding 必須啟用事務才能調用本服務。
這樣綜合作用,匹配的結果就是3種啟動事務的模式。分別是:Client/Service transaction、Client transaction、Service transaction模式。他們分別的設置情況是:
【5.1】. Client/Service transaction,最常見的一種事務模型,通常由客戶端或服務本身啟用一個事務。設置步驟:
(1) 選擇一個支持事務的Binding,設置 TransactionFlow = true。
(2) 設置 TransactionFlow(TransactionFlowOption.Allowed)。
(3) 設置 OperationBehavior(TransactionScopeRequired=true)。
【5.2】. Client transaction,強制服務必須參與事務,而且必須是客戶端啟用事務。設置步驟:
(1) 選擇一個支持事務的Binding,設置 TransactionFlow = true。
(2) 設置 TransactionFlow(TransactionFlowOption.Mandatory)。
(3) 設置 OperationBehavior(TransactionScopeRequired=true)。
【5.3】. Service transaction,服務必須啟用一個根事務,且不參與任何外部事務。設置步驟:
(1) 選擇任何一種Binding,設置 TransactionFlow = false(默認)。
(2) 設置 TransactionFlow(TransactionFlowOption.NotAllowed)。
(3) 設置 OperationBehavior(TransactionScopeRequired=true)。
【6】示例代碼講解:
示例代碼這裡測試的服務包括兩個服務。服務分別有一個操作訪問事務資源。定義一個宿主托管服務。一個客戶端。客戶端調用操作啟動事務,服務事務模式。
【6.1】服務端:
服務模式的實現代碼如下:首先是服務契約的定義:
//1.服務契約
[ServiceContract(Namespace = "http://www.cnblogs.com/frank_xl/",SessionMode=SessionMode.Required)]
public interface IWCFServiceTransaction1
{
//操作契約
[OperationContract]
//禁止事務流,使用服務事務
//TransactionFlowAttribute t = new TransactionFlowAttribute(
[TransactionFlow(TransactionFlowOption.NotAllowed)]
bool AddDataUsingAdapter(string name);
//操作契約
}
服務類的代碼實現如下:
//2.服務類,實現契約
//事務有30分鐘的超時限制
//[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, TransactionTimeout = "00:30:00",InstanceContextMode=InstanceContextMode.PerCall)]
public class WCFServiceTransaction1 : IWCFServiceTransaction1
{
//實現接口定義的方法
//需要事務環境,啟動事務
[OperationBehavior(TransactionScopeRequired = true)]//, TransactionAutoComplete = true)]
public bool AddDataUsingAdapter(string name)
{
Transaction transaction = Transaction.Current;
//斷言是一個本地事務
Debug.Assert(System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier == Guid.Empty);
//輸出事務信息
Console.WriteLine("Create a new Transaction at {0}", System.Transactions.Transaction.Current.TransactionInformation.CreationTime);
Console.WriteLine("WCFService 1 Transaction Status is {0}", System.Transactions.Transaction.Current.TransactionInformation.Status);
Console.WriteLine("WCFService 1 Transaction LocalIdentifier is {0}", System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier);
Console.WriteLine("WCFService 1 Transaction DistributedIdentifier is {0}", System.Transactions.Transaction.Current.TransactionInformation.DistributedIdentifier);
//////啟動事務
using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope())
{
try
{
userTableAdapter adapter1 = new userTableAdapter();
//第1次增加數據
adapter1.CreateUser(name);
Console.WriteLine("Calling WCF Transaction{0} length is:{1}", name, name.Length);
//////第2次增加數據
//userTableAdapter adapter2 = new userTableAdapter();
//adapter2.CreateUser(name);
//Console.WriteLine("Calling WCF Transaction{0} length is:{1}", name, name.Length);
if (name.Length > 8)
throw new Exception("Name is too long,it should be less than 10");
}
catch (Exception e)
{
System.Transactions.Transaction.Current.Rollback();
Console.WriteLine("Calling WCF Transaction error:{0}", e.Message);
//throw e;
return false;
}
ts.Complete();
//OperationContext.Current.SetTransactionComplete();
return true;
}
}
}
這裡把操作行為要設置為 [OperationBehavior(TransactionScopeRequired = true)],需要事務支持。其次是認為增加一個判斷name長度的語句,超過8就拋出異常,if (name.Length > 8)
throw new Exception("Name is too long,it should be less than 10...");把剛才的插入操作回滾,System.Transactions.Transaction.Current.Rollback();不提交。
【6.2】宿主:
我們采用托管宿主,這裡主要需要配置的就是服務超時時間限制,還有事務流選項,默認即可,使用代碼實現也可以。
具體代碼如下:
<system.serviceModel>
<services>
<service behaviorConfiguration="WCFService.WCFServiceBehavior"
name="WCFService.WCFServiceTransaction1" >
<endpoint
bindingConfiguration="netTcpBindingTcp"
address="net.tcp://localhost:9001/WCFServiceTransaction1"
binding="netTcpBinding"
contract="WCFService.IWCFServiceTransaction1">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8001/WCFServiceTransaction1"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WCFService.WCFServiceBehavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true">
</serviceDebug>
<serviceTimeouts transactionTimeout="00:30:00"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding >
<binding name="netTcpBindingTcp" transactionFlow="false">
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
【6.3】客戶端:
客戶端代碼主要是添加服務引用的代碼就不做詳細描述,大家都能明白這個過程。主要是測試代碼。進行了兩次測試。允許用戶輸入姓名,代碼如下:
try
{
//正常保存。10位以內。.超過長度就會出錯
Console.WriteLine("input name please ");
string name = Console.ReadLine();
wcfServiceProxy1.AddDataUsingAdapter(name);
Console.WriteLine("{0} length is :{1}", name, name.Length);
}
catch (Exception ex)
{
//出現異常就就通知其他事務資源管理器回滾
//System.Transactions.Transaction.Current.Rollback();
Console.WriteLine("Exception Message1 is : {0}", ex.Message);
//throw ex;
}
//////////////////////////////////////////測試2////////////////////////////////
try
{
///正常保存。10位以內.超過長度就會出錯
Console.WriteLine("input name please ");
string name = Console.ReadLine();
wcfServiceProxy1.AddDataUsingAdapter(name);
Console.WriteLine("{0} length is :{1}", name, name.Length);
}
catch (Exception ex)
{
Console.WriteLine("Exception Message1 is : {0}", ex.Message);
//throw ex;
}
分別進行了測試,首先輸入“FrankXu”長度7,提交新增事務,保存數據成功。其次是輸入“FrankXuLei”,長度是10,拋出異常,事務回滾,取消剛才的插入操作。執行的結果如下圖:
【7】總結:
事務編程是WCF中一個重要的概念。WCF借助已有的技術System.Transactions可以實現本地事務的編程。而分布式事務則是借助MSDTC分布式事務協調機制來實現。事務也可跨多個數據資源。 使用分布式事務可以將在不同系統上執行的多種不同的操作合並到一個通過或失敗的操作中。 在這種情況下,事務由位於每個系統中的 Microsoft 分布式事務協調器 (MSDTC) 進行協調。 在WCF使用 System.Transactions 所提供的類開發事務應用程序時,不必考慮需要使用哪種事務,也不必考慮所涉及的事務管理器。 System.Transactions 基礎結構會為WCF自動管理這些事宜。我們可以隱式編程實現事務或者顯式編程控制事務。
(1)事務的級別提升和狀態管理由事務管理器來實現,WCF很好地與LTM、KTM、MSDTC事務管理器結合,實現了自己需要的事務模型。
(2)WCF提供了支持事務傳播的綁定協議包括:WSHttpBinding、WSDualHttpBinding、WSFederationBinding、NetTcpBinding和NetNamedPipesBinding。最後兩個綁定允許選擇WS-AT協議或OleTx協議,而其他綁定都只使用標准的WS-AT協議。
(3)在此次准備事務編程示例代碼的過程中,查閱了不少資料。發現很少有完整的可以執行的代碼。WCF學習,實踐動手能力必不可少,我在調試代碼的過程中遇到很多問題,自己也查閱了資料,並整理響應的解決辦法。大家可以再另外一個系列WCF分布式開發常見錯誤裡看到,我也放到微軟WCF中文技術論壇了,大家也可以查閱。一起交流~
(4)本來計劃些個最全面的示例代碼,包括2個服務,連個宿主。來測試客戶端/服務端事務模式。但是調試過程有很多問題。目前代碼可以執行,但是有些潛在的異常,還在調試。我會在後續文章裡給放出來吧。
本文配套源碼