WCF系列_分布式事务(下)
1、 WCF分布式事務例子
這里也用轉賬的例子說事。
用戶在系統A和系統B都有賬戶,賬戶間的資金可以互轉,系統A的資金減少多少,系統B的相應賬戶的資金就增加多少。
系統A機器上有數據庫AccountA,系統B機器上有數據庫AccountB,數據庫的結構一樣,都有一個數據表Account,結構如下:
| 字段 | 數據類型 | 含義 |
| depositorID | int | 賬戶id |
| amount | decimal(18, 2) | 金額 |
?
為了演示TxF事務性文件,在系統B中增加了一個寫文件的操作,記錄本次轉賬操作的信息。轉賬的所有操作步驟:系統A上賬戶上減少金額,系統B上記錄轉賬信息文件,系統B上相應賬戶資金增加這三個操作都在一個事務流中,要么全部完成,要么全部回滾。
系統A和系統B分別在服務器A和服務器B上。系統A在賬戶上減少金額后調用系統B的WCF服務,在系統B中繼續增加賬戶資金,生成轉賬信息文件。
下面開始這個例子的完整過程。
1.1.?? 建立系統B轉賬WCF服務
建立服務契約:
[ServiceContract]
public interface IAccountB
{
??? [OperationContract]
??? [TransactionFlow(TransactionFlowOption.Allowed)]
??? void deposit(int depositorid, double amount);
}
服務契約就一個方法deposit,其中depositorid表示賬戶id,amount表示要從系統A轉賬到系統B的金額。
TransactionFlow這個屬性指示operation是否跟隨調用端的事務流,參數含義:
TransactionFlowOption.NotAllowed – 表示此operation不跟隨傳入的事務流,不參與分布式事務。
TransactionFlowOption.Allowed – 表示此opreation可以跟隨傳入的事務流,如果有傳入的事務流則參與,如果沒有傳入的事務流則不參與,但是可以啟動本地的事務。
TransactionFlowOption.Mandatory – 表示此operation必須跟隨傳入的事務,參與分布式事務,如果調用此operation的客戶端沒有事務流則拋出異常。
?
下面是服務實現:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class AccountBService : IAccountB
{
??? [OperationBehavior(TransactionScopeRequired = true)]
??? public void deposit(int depositorid, double amount)
??? {
??????? #region 新建事務性文件
??????? string path = @"c:\test.txt";
??????? FileStream fs = TransactedFile.Open(path, System.IO.FileMode.Create,
??????????? System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);
??????? string fileContent = string.Format("從系統A轉賬到系統B\r\n用戶ID:{0}\r\n轉賬金額為:{1}",??????????? depositorid.ToString(), amount.ToString());
??????? byte[] byteArrar = Encoding.UTF8.GetBytes(fileContent);
??????? fs.Write(byteArrar, 0, byteArrar.Count());
??????? fs.Flush();
??????? fs.Close();
??????? #endregion
?
??????? #region 數據訪問,在指定賬戶上增加存款
??????? string connstr = ConfigurationManager.ConnectionStrings["ConnStr"].ToString();
??????? SqlCommand mySqlCommand = new SqlCommand("update account set amount = amount +??????? @amount where depositorid = @depositorid ");
??????? mySqlCommand.Connection = new SqlConnection(connstr);
??????? SqlParameter par1 = new SqlParameter("@amount", SqlDbType.Decimal);
??????? par1.Value = amount;
??????? mySqlCommand.Parameters.Add(par1);
??????? par1 = new SqlParameter("@depositorid", SqlDbType.Int);
??????? par1.Value = depositorid;
??????? mySqlCommand.Parameters.Add(par1);
??????? mySqlCommand.Connection.Open();
??????? mySqlCommand.ExecuteNonQuery();
??????? mySqlCommand.Connection.Close();
??????? #endregion
???? }
}
服務實現了deposit操作。
[OperationBehavior(TransactionScopeRequired = true)],這里的TransactionScopeRequired = true表示這個操作在TransactionScope內執行,加上前面OperationContract上的TransactionFlowOption.Allowed 允許跟隨事務的設置,這個deposit的操作將會參與客戶端發起的分布式事務。
實現的deposit操作中完成兩個任務,先轉賬信息寫入c:\test.txt文件,這里寫文件操作使用了TxF事務性文件操作TransactedFile.Open,關于TxF的操作部分的代碼微軟有提供,在本文中提供的代碼中包含了這部分源碼。使用事務性文件操作,在事務中的其他事務資源操作失敗后,文件操作也會回滾。
?
1.2.?? 建立系統A轉賬客戶端
系統A是個Console應用:
static void Main(string[] args)
{
? ChannelFactory<IAccountB> myFactory = new ChannelFactory<IAccountB>("endpointConfig");
? IAccountB myClient = myFactory.CreateChannel();
? Double amount = 500;
? int depositorid = 1;
? using (TransactionScope scop = new TransactionScope())
? {
??? #region 數據訪問,在指定賬戶上減少存款
??? string connstr = ConfigurationManager.ConnectionStrings["ConnStr"].ToString();
??? SqlCommand mySqlCommand = new SqlCommand("update account set amount = amount - @amount where depositorid = @depositorid ");
??? mySqlCommand.Connection = new SqlConnection(connstr);
??? SqlParameter par1 = new SqlParameter("@amount", SqlDbType.Decimal);
??? par1.Value = amount;
??? mySqlCommand.Parameters.Add(par1);
??? par1 = new SqlParameter("@depositorid", SqlDbType.Int);
??? par1.Value = depositorid;
??? mySqlCommand.Parameters.Add(par1);
??? mySqlCommand.Connection.Open();
??? mySqlCommand.ExecuteNonQuery();
??? mySqlCommand.Connection.Close();
??? #endregion
??? try
??? {
????? myClient.deposit(depositorid, amount);
????? scop.Complete();
??? }
??? catch (Exception e)
??? {
????? Transaction.Current.Rollback();
??? }
? }
}
?
1.3.?? 配置使用OleTransactions協議
先測試使用OleTransactions分布式事務協議,下面是使用OleTransactions時在客戶端和服務端需要的服務和配置。
1.3.1.?? 配置文件
系統B上WCF服務的配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
? <system.web>
??? <compilation debug="true" />
? </system.web>
? <connectionStrings>
??? <add name="ConnStr" connectionString="Server=.;Integrated security=true;initial catalog=AccountB" />
? </connectionStrings>
? <system.serviceModel>
??? <bindings>
????? <customBinding>
??????? <binding name="customBindingConfig">
????????? <transactionFlow transactionProtocol="OleTransactions" />
???????? ?<httpTransport />
??????? </binding>
????? </customBinding>
??? </bindings>
??? <services>
????? <service behaviorConfiguration="WCF_ATTransTest.Service.Service1Behavior"
????????? name="WCF_ATTransTest.Service.AccountBService">
??????? <endpoint address="" binding="customBinding" bindingConfiguration="customBindingConfig"
??????????? name="serviesEndpoint" contract="WCF_ATTransTest.Service.IAccountB" />
??????? <host>
????????? <baseAddresses>
??????????? <add baseAddress="http://localhost/WCF_ATTransTest.Service/AccountBService/" />
????????? </baseAddresses>
??????? </host>
????? </service>
??? </services>
??? <behaviors>
????? <serviceBehaviors>
??????? <behavior name="WCF_ATTransTest.Service.Service1Behavior">
????????? <serviceMetadata httpGetEnabled="true"/>
????????? <serviceDebug includeExceptionDetailInFaults="true" />
??????? </behavior>
????? </serviceBehaviors>
??? </behaviors>
? </system.serviceModel>
</configuration>
系統A WCF客戶端的配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
? <connectionStrings>
??? <add name="ConnStr" connectionString="Server=.;Integrated security=true;initial catalog=AccountA" />
? </connectionStrings>
? <system.serviceModel>
??? <client>
????? <endpoint address="http://chnking-pc/WCF-ATTransTest/WCF_ATTransTest.Service.AccountBService.svc"
????????? binding="customBinding" bindingConfiguration="customBindingConfig"
????????? contract="WCF_ATTransTest.Service.IAccountB" name ="endpointConfig"/>
??? </client>
??? <bindings>
????? <customBinding>
??????? <binding name="customBindingConfig">
????????? <transactionFlow transactionProtocol="OleTransactions" />
????????? <httpTransport />
??????? </binding>
????? </customBinding>
??? </bindings>
? </system.serviceModel>
</configuration>
?
1.3.2.?? 配置RPC
使用OleTx協議時,要通過RPC協議在DTC事務管理器之間通訊,RPC本身使用135端口,還要使用數量不定的1024端口以上的動態端口。所以防火墻必須要開放135端口和1024端口以后的所有端口。
但是要開放所有1024以上的端口又是很危險的事情,最好能把RPC使用的端口限制在一定的范圍,然后防火墻只開放這個范圍的端口。
可以通過修改注冊表來達到這個目的:
運行Regedt32.exe打開注冊表,在以下注冊表項下添加 Internet 項:HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc,在 Internet 項下,添加值“Ports”(MULTI_SZ)、“PortsInternetAvailable”(REG_SZ) 和“UseInternetPorts”(REG_SZ)。
在本示例中,使用了端口 5000 到 5100(含 5000 和 5100),因此該新注冊表項將顯示為以下形式:
Ports:REG_MULTI_SZ: 5000-5100
PortsInternetAvailable:REG_SZ:Y
UseInternetPorts:REG_SZ:Y
??????
在所有參與事務的機器上都必須啟動RPC服務,并配置RPC使用的動態端口,防火墻開放這些動態端口。
1.3.3.?? DTC服務
發起事務的服務器的DTC服務必須啟動,參與事務的服務器的DTC服務可以不啟動。
在事務內要調用跨越進程或機器的服務,事務需要提升級別到DTC管理器,如果發起事務的服務器的DTC服務未啟動,則會拋出異常:“試圖提升事務時失敗。服務器xxx上的MSDTC不可用。”
1.3.4.?? 測試
將系統B的WCF服務發布至IIS,系統A調用WCF的地址指向系統B發布的服務地址,測試結果,發送到WCF的消息和返回的消息:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
? <s:Header>
??? <a:Action s:mustUnderstand="1">http://tempuri.org/IAccountB/deposit</a:Action>
??? <a:MessageID>urn:uuid:6d8931ab-f79e-4de8-a377-4d8acdcf3545</a:MessageID>
??? <a:ReplyTo>
????? <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
??? </a:ReplyTo>
??? <OleTxTransaction s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2006/02/tx/oletx">
????? <PropagationToken>????? AQAAAAMAAACxK7vUESf1RJ8BoxrUWJUvAAAQAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU0NDljY2MzLWY3OWQtNDVjMi05YjQzLTI1OTRmY2JhZTQ2NwAALAAHAAAAZM1kzSEAAABXSU43LVBDABAAAABXAEkATgA3AC0AUABDAAAAAQAAAAAAAAAOAAAAdGlwOi8vV2luNy1QQy8AAA==
????? </PropagationToken>
? ? </OleTxTransaction>
??? <a:To s:mustUnderstand="1">http://chnking-pc/WCF-ATTransTest/WCF_ATTransTest.Service.AccountBService.svc</a:To>
? </s:Header>
? <s:Body>
??? <deposit xmlns="http://tempuri.org/">
????? <depositorid>1</depositorid>
????? <amount>500</amount>
??? </deposit>
? </s:Body>
</s:Envelope>
注意在請求消息的header中有<OleTxTransaction>標簽,這個標簽用來標示采用OleTxTransaction分布式事務協議。
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
? <s:Header>
??? <a:Action s:mustUnderstand="1">http://tempuri.org/IAccountB/depositResponse</a:Action>
??? <a:RelatesTo>urn:uuid:6d8931ab-f79e-4de8-a377-4d8acdcf3545</a:RelatesTo>
? </s:Header>
? <s:Body>
??? <depositResponse xmlns="http://tempuri.org/"/>
? </s:Body>
</s:Envelope>
?
1.4.?? 配置使用WS-AtomicTransaction協議
1.4.1.?? 配置文件
將服務端和客戶端的配置文件中的:
????????? <transactionFlow transactionProtocol="OleTransactions" />
改成:
????????? <transactionFlow transactionProtocol="WSAtomicTransactionOctober2004" />
1.4.2.?? 配置WS-AT
要讓DTC支持WS-AT協議需要配置參與分布式事務的所有機器的證書,安裝WS-AT的DTC的UI等等,配置過程還比較復雜,詳細步驟參見微軟文檔:配置 WS-Atomic 事務支持(http://msdn.microsoft.com/zh-cn/library/ms733943.aspx)
配置好的界面如下:
?
WS-AT事務管理器之間的通訊使用SSL安全通道傳輸,所以每臺參與WS-AT事務的機器都必須配置證書和相應的https的端口。
WS-AT的UI界面雖然安裝在DTC中,但是WS-AT事務并不依賴于DTC服務,所以所有參與WS-AT事務的機器的DTC服務不必啟動。
1.4.3.?? 測試
抓通訊數據包,這時的消息如下:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
? <s:Header>
??? <a:Action s:mustUnderstand="1">http://tempuri.org/IAccountB/deposit</a:Action>
??? <a:MessageID>urn:uuid:1c111341-b7ef-4e21-91b9-a00de9aa8eea</a:MessageID>
??? <a:ReplyTo>
????? <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
??? </a:ReplyTo>
??? <CoordinationContext s:mustUnderstand="1" xmlns="http://schemas.xmlsoap.org/ws/2004/10/wscoor" xmlns:mstx="http://schemas.microsoft.com/ws/2006/02/transactions">
????? <wscoor:Identifier xmlns:wscoor="http://schemas.xmlsoap.org/ws/2004/10/wscoor">urn:uuid:c7932c29-6b96-4961-b6d5-ce3507a54f1e</wscoor:Identifier>
????? <Expires>3600000</Expires>
????? <CoordinationType>http://schemas.xmlsoap.org/ws/2004/10/wsat</CoordinationType>
????? <RegistrationService>
??????? <Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">https://win7-pc:1443/WsatService/Registration/Coordinator/</Address>
??????? <ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">
????????? <mstx:RegisterInfo>
??????????? <mstx:LocalTransactionId>c7932c29-6b96-4961-b6d5-ce3507a54f1e</mstx:LocalTransactionId>
????????? </mstx:RegisterInfo>
??????? </ReferenceParameters>
????? </RegistrationService>
????? <mstx:IsolationLevel>0</mstx:IsolationLevel>
????? <mstx:LocalTransactionId>c7932c29-6b96-4961-b6d5-ce3507a54f1e</mstx:LocalTransactionId>
????? <PropagationToken xmlns="http://schemas.microsoft.com/ws/2006/02/tx/oletx">AQAAAAMAAAApLJPHlmthSbbVzjUHpU8eAAAQAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU0NDljY2MzLWY3OWQtNDVjMi05YjQzLTI1OTRmY2JhZTQ2NwAALAAHAAAAZM1kzSEAAABXSU43LVBDABAAAABXAEkATgA3AC0AUABDAAAAAQAAAAAAAAAOAAAAdGlwOi8vV2luNy1QQy8AAA==</PropagationToken>
??? </CoordinationContext>
??? <a:To s:mustUnderstand="1">http://chnking-pc/WCF-ATTransTest/WCF_ATTransTest.Service.AccountBService.svc</a:To>
? </s:Header>
? <s:Body>
??? <deposit xmlns="http://tempuri.org/">
????? <depositorid>1</depositorid>
????? <amount>500</amount>
??? </deposit>
? </s:Body>
</s:Envelope>
可以看出使用WS-AT協議時,消息的header部分比使用OleTransactions協議時復雜的多,注意header部分里的:
<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">https://win7-pc:1443/WsatService/Registration/Coordinator/</Address>
這是發起WS-AT事務機器的WS-AT事務管理器的地址,參與WS-AT事務的別的機器都要向這個地址來注冊本地事務和報告本地事務的完成情況,以便WS-AT事務管理器協調整個分布式事務,來決定提交整個事務還是回滾整個事務。
返回消息:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
? <s:Header>
??? <a:Action s:mustUnderstand="1">http://tempuri.org/IAccountB/depositResponse</a:Action>
??? <a:RelatesTo>urn:uuid:1c111341-b7ef-4e21-91b9-a00de9aa8eea</a:RelatesTo>
? </s:Header>
? <s:Body>
??? <depositResponse xmlns="http://tempuri.org/"/>
? </s:Body>
</s:Envelope>
?
但是在抓數據包是發現一個問題,就是從消息的header看是顯示使用WS-AT協議,但是從抓的數據包看依然是使用的OleTransactions協議處理的事務,通過RPC協議協調事務,下面是部分截圖:
???????
DCERPC顯示是RPC協議,ctx_id: 1 906b0ce0-c70b-1067-b317-00dd010662da顯示了事務id。
原來由于OleTransactions協議的開銷遠比WS-AT協議小,所以微軟的默認設計是即使指定使用WS-AT協議,仍然義無反顧的使用OleTransactions協議。所以這時你的機器上要是DTC和RPC的端口沒有配置好,就會報錯。
要想真正使用WS-AT協議需要手工在注冊表中增加一個key:
在注冊表HKLM\SOFTWARE\Microsoft\WSAT\3.0下增加一個名為“OleTxUpgradeEnabled”的DWord值,這個key表示在指定WS-AT協議時是否升級使用OleTransactions協議,0表示不升級(即表示使用WS-AT協議),1表示升級(即表示使用OleTransactions協議),默認是1,所以默認時即使你指定使用WS-AT協議,實際上還是被OleTransactions協議替換了。設置為0即可使用WS-AT協議了。
使用WS-AT協議時,事務協調不依賴于DTC,也不走RPC協議,只通過WS-AT配置中的https端口通過SSL進行事務管理器之間的通訊。WS-AT協議是業界標準,只要實現了這個協議的系統都能進行互操作,當然不能依賴于微軟的專有RPC協議。
?
代碼下載:
WCF-ATTransTest客戶端.rar
WCF-ATTransTest服務端.rar
總結
以上是生活随笔為你收集整理的WCF系列_分布式事务(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 酷6暴力裁员,是清洗也是重塑
- 下一篇: HP ProLiant 服务器 - 基础