[转载]WCF系列_分布式事务(下)
浏览到chnking的WCF的分布式事务处理不错,转载过来分享一下。
1、 WCF分布式事务例子
这里也用转账的例子说事。
用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B的相应账户的资金就增加多少。
系统A机器上有数据库AccountA,系统B机器上有数据库AccountB,数据库的结构一样,都有一个数据表Account,结构如下:

为了演示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, , 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 = ;
int depositorid = ;
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>
<PropagationTokenxmlns="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系列_分布式事务(上)》《WCF系列_分布式事务(下)》
另一了解:《 WCF 分布式事务-应用》
[转载]WCF系列_分布式事务(下)的更多相关文章
- Zookeeper概念学习系列之分布式事务
不多说,直接上干货! 初学者来说,肯定会有这么一个疑问.为什么会在zookeeper里牵扯到分布式事务? zookeeper到底是什么? zookeeper实际上是yahoo开发的,用于分布式中一致性 ...
- SpringCloud系列——TX-LCN分布式事务管理
前言 SpringCloud分布式架构给我们带来开发上的便利,同时增加了我们对事务管理的难度,微服务的遍地开花,本地事务已经无法满足分布式的要求,由此分布式事务问题诞生. 分布式事务被称为世界性的难题 ...
- MSDN Webcast 跟我一起从零开始学WCF系列课程
系列课程 >跟我一起从零开始学WCF系列课程 跟我一起从零开始学WCF系列课程(1):WCF概述 (Level 200) 讲 师:徐长龙 课程简介:从 本堂课开始我们将开启一个新的 ...
- 分布式事务(二)Java事务API(JTA)规范
一.引子 既然出现了分布式场景(DTP模型), 大java也及时制定出一套规范来给各大应用服务器.数据库/mq等厂商使用,以方便管理互通--->JTA闪亮登场.JTA(Java Transact ...
- 分布式事务(三)mysql对XA协议的支持
系列目录 分布式事务(一)原理概览 分布式事务(二)JTA规范 分布式事务(三)mysql对XA协议的支持 分布式事务(四)简单样例 分布式事务(五)源码详解 分布式事务(六)总结提高 引子 从Mys ...
- Asp.Net Core&CAP实现分布式事务
需要注意的是标题中的CAP不是指的CAP理论,而是园区大神杨晓东实现的框架,CAP框架基于本地消息表用最终一致性实现分布式事务. 本地消息表 首先我们考虑一个场景,在将用户信息更改后,需要发送一条消息 ...
- 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存
原文:http://blog.csdn.net/heyewu4107/article/details/71009712 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存 问 ...
- WCF分布式事务(EF)
才说分布式事务,首先,了解一下什么是交易. 事务有四个特性:ACID A是Atomicity,原子性.一个事务往往涉及到很多的子操作,原子性则保证这些子操作要么都做,要么都不做,而不至于出现事务的部分 ...
- WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程
今天我们继续学习WCF分布式开发步步为赢系列的12节:WCF事务机制(Transaction)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...
随机推荐
- 汇编_指令_REP MOVESB 和 CLD
先说说MOVSB(MOVe String Byte):即字符串传送指令,这条指令按字节传送数据.通过SI和DI这两个寄存器控制字符串的源地址和目标地址,比如DS:SI这段地址的N个字节复制到ES:DI ...
- HDU 1686 Oulipo(优化的KMP)
Oulipo Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 面试总结之Database
什么是数据库事务? 数据库事务_百度百科 https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1/9744 ...
- 自动手动随便你 Win7驱动程序安装自己设
Win7系统是非常智能方便的操作系统,可以自动安装硬件驱动程序,为用户提供了很多方便.但是并不是所有的驱动程序和硬件都能完美兼容,如果不合适就需要卸载了重新安装:还有一些朋友就习惯自己安装驱动,那么, ...
- FCCMBBTN.RES
[ilink32 Error] Error: Unable to open file 'FCCMBBTN.RES' 用到了fc控件 添加路径到LibPath即可. 1st\1stClassStudio ...
- Memo synEditor 当前行号
Memo 当前行号,坐标,位置 可以使用Memo的属性CaretPos.X来取行鼠标所在行的行数与鼠标所在行的第几位 Memo.CaretPos.X 光标或鼠标所在行的列号(第几位),从0开始计数Me ...
- libcurl使用心得-包括下载文件不存在处理相关(转)
libcurl使用心得 Libcurl为一个免费开源的,客户端url传输库,支持FTP,FTPS,TFTP,HTTP,HTTPS,GOPHER,TELNET,DICT,FILE和LDAP,跨平台,支持 ...
- node.js中实现同步操作的3种实现方法
这篇文章主要介绍了node.js中实现同步操作的3种实现方法,本文用实例讲解一些需要同步操作的情况下,如何编程实现,需要的朋友可以参考下 众所周知,异步是得天独厚的特点和优势,但同时在程序中同步的需求 ...
- Linux下MariaDB 安装及root密码设置(修改)
根据官方说明在/etc/yum.repo.d/下添加repo: # MariaDB 10.2 Fedora repository list - created 2017-11-25 05:55 UTC ...
- 2018,学会python 在新公司扎根
五月份跳槽了,来新公司,是个跟我之前业务完全不同的行业,偏硬件,可是对硬件没有什么概念.而且自己都不知道从哪里下手的感觉.终于明白自己在上家公司已经将人待的生了锈,我们庆幸,也许还是不晚的. 执行学习 ...