在.net remoting的事務傳播以及wcf分布式事務 中,我們用TIP協在.net remoting協議上實現了遠程事務,其本質是通過將TIP事務的TIP URL傳遞到遠程機器上,然后由遠程機器通過TIP協議連接到事務協調器上,但是總體看下來還是有一些缺點:
(1)實現起來還是有點麻煩。
(2)特別是如果涉及多臺服務器,多個服務都參與到事務中的時候,那樣需要將TIP URL傳來傳去,換言之,需要自己實現事務傳播,那更是麻煩。
(3)不支持異構系統,比如除了調用.net remoting外,還需要調用j2ee的web service的話,那根本就不可能做到AtomicTransaction了。
相對而言,WCF在遠程分布式事務傳播上就簡單得多了,而且遠程分布式事務傳播是WCF內部實現的,使用起來只需要在配置文件上和Contract上面加上一些特性即可。與TIP事務不同,WCF是通過實現WS-Transaction,WS-Coordination, WS-AtomicTransaction,而這幾個都不是微軟自己的協議,是一套工業標準。只要服務方的基礎架構也實現了這套工業標準,WCF實現的WS就可以和WebSphere實現的WS在實現分布式事務。
廢話少說,我們就來看看怎么用WCF實現基于WS-AT的遠程分布式事務吧(假設有兩臺服務器MachineA和MachineB參與了分布式事務)。
第一件要做的事情就是配置MSDTC,讓它只是WS-AT協議
1、安裝Windows Communication Foundation 更新程序 (KB912817)
2、使用Windows SDKs的工具MakeCert.exe,為MachineA和MachineB生成X.509證書(分別稱為CertA和CertB)
MakeCert.exe -r -pe -sky exchange -n "CN=MachineA" -ss Root -sr LocalMachine
MakeCert.exe -r -pe -sky exchange -n "CN=MachineB" -ss Root -sr LocalMachine
這里需要注意的是"CN=MachineA"中的服務器名,需要用FQDN,比如MachineA在域cos.com,那么就需要用"CN=MachineA.cos.com",而不是MachineA。
3、在MachineA中,從證書中的計算機帳號Trusted Root Certificate Authority Node區到處證書CertA,并導入到Personal 區域(導出時需要同時導出私鑰)。在MachineB也做同樣的操作。
4、在MachineA將CertA的公鑰導出到文件CertA.cer,并導入到MachineB的計算機帳號中的Trusted Root Certificate Authority Node區域和Personal區域。同樣將CertB的公鑰導出到MachineA的計算機帳號中的Trusted Root Certificate Authority Node區域和Personal區域。
5、配置MSDTC,啟用WS-AT協議。
注冊程序集wsatui.dll(可以在Windows SDKs的bin目錄找到該文件,默認安裝目錄是C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin),執行命令:
regasm /codebase C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\wsatui.dll
6、執行了上面的操作後,打開MSDTC的配置,就會看到多了一個WS-AT的Tab。在里面配置一下相關的參數:
(1)選擇啟用WS-AT
(2)設置Https端口,默認是443,但為了避免沖突,我設成了1443
(3)設置Endpoint Certificate,對于MachineA,選擇CertA,對于MachineB,選擇CertB
(4)設置Authorized certificates,對于MachineA,選擇CertB,對于MachineA,選擇CertA(如果有多臺服務器參與分布式事務,可以選擇多個cert)
到此為止已經配置好啟用WS-AT協議了,接下來就可以寫WCF程序來跑跑WS-AT協議了。
第二步:寫WCF程序
和一般的WCF程序差不多,唯一的區別就是配置上有所不同,和需要在Contract的方法上面加一些特性。對于接口方法,加上特性:[TransactionFlow(TransactionFlowOption.Mandatory)]。對于實現類,加上特性:[OperationBehavior(TransactionScopeRequired=true)]

[ServiceContract()]
public interface ICustomerService
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
int Update(int customerID);
[OperationContract]
string GetCustomerName(int customerID);
}
public class CustomerService : ICustomerService
{
[OperationBehavior(TransactionScopeRequired=true)]
public int Update(int customerID)
{
try
{
Console.WriteLine("Update Customer
");
using (SqlConnection cn = new SqlConnection("Server=.;Integrated security=true;initial catalog=learningsite"))
{
cn.Open();
SqlCommand cmd = new SqlCommand("Update Customers Set CustomerName=CustomerName + Cast(@CustomerID as varchar(10)) where customerID = @customerID", cn);
cmd.Parameters.AddWithValue("@CustomerID", customerID);
return cmd.ExecuteNonQuery() > 0 ? 0 : -1;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return -1;
}
}
public string GetCustomerName(int customerID)
{
string now = DateTime.Now.ToString();
Console.WriteLine("CustomerName");
return "CustomerName";
}
}配置文件方面,我們使用customBinding(當然,也可以使用wsHttpBinding,它只支持WS-AT協議)。客戶端配置如下:

<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
</diagnostics>
<behaviors />
<bindings>
<customBinding>
<binding name="httpWSAT">
<transactionFlow transactionProtocol="WSAtomicTransactionOctober2004" />
<httpTransport />
</binding>
</customBinding>
<netTcpBinding>
<binding name="netTCPWSAT" transactionFlow="true" transactionProtocol="OleTransactions" />
</netTcpBinding>
<wsHttpBinding>
<binding name="wsTransaction" transactionFlow="true">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://li/CustomerService" binding="customBinding"
bindingConfiguration="httpWSAT" contract="ICustomerService" name="ICustomerService">
<identity>
<userPrincipalName value="domain\username" />
</identity>
</endpoint>
<endpoint address="http://li/OrderService" binding="customBinding"
bindingConfiguration="httpWSAT" contract="WCFTransactionClient.OrderService.IOrderService"
name="IOrderService">
<identity>
<userPrincipalName value="domain\username" />
</identity>
</endpoint>
</client>
</system.serviceModel>
服務器端配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<diagnostics performanceCounters="Off" />
<behaviors>
<serviceBehaviors>
<behavior name="metadataSupport">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="httpWSATBinding">
<transactionFlow transactionProtocol="WSAtomicTransactionOctober2004" />
<httpTransport/>
</binding>
</customBinding>
<netTcpBinding>
<binding name="netTCPWSATBinding" transactionFlow="true" transactionProtocol="WSAtomicTransactionOctober2004" />
</netTcpBinding>
<wsHttpBinding>
<binding name="wsTransaction" transactionFlow="true">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="metadataSupport" name="WCFTransactionLib.CustomerService">
<endpoint address="" binding="customBinding" bindingConfiguration="httpWSATBinding"
contract="WCFTransactionLib.ICustomerService" />
<host>
<baseAddresses>
<add baseAddress="http://li/CustomerService" />
</baseAddresses>
</host>
</service>
<service behaviorConfiguration="metadataSupport" name="WCFTransactionLib.OrderService">
<endpoint address="" binding="customBinding" bindingConfiguration="httpWSATBinding"
contract="WCFTransactionLib.IOrderService" />
<host>
<baseAddresses>
<add baseAddress="http://li/OrderService" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>配置文件里面有一點需要注意的,就是在endpoint address和baseAddress中使用的地址中的服務器地址必須使用和生成X.509證書一樣的地址,比如必須用http://MachineA.cos.com,而不是http://192.168.1.101或者http://MachineA。這個是ssl證書的要求:)
廢話少說了,先附上測試用的源代碼吧
/Files/walkinhill/wcftransactiondemosolutionnew.zip
已經寫了不少了,只好把通過分析WCF程序之間交互的消息來看看WS-AT的原理的。只好留作下一篇在寫WS-AT消息的內容和WS-AT和OleTx協議的一些差別了:)
參考資料:
http://msdn2.microsoft.com/en-us/library/ms733943.aspx
浙公網安備 33010602011771號