[转载]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)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...
随机推荐
- php soap实例讲解
一,什么是soap,什么是wsdl,为什么要用他们 SOAP是基于XML和HTTP通信协议,xml各种平台,各种语言都支持的一个种语言.http呢它得到了所有的因特网浏览器及服务器的支持. WSDL ...
- 源文件封装为IP的步骤
因为模块的交接,最好将写好的源文件和生成的IP封装一个IP,然后再转交给其他的同事使用,这是一种好的习惯.但是对于,封装的过程还是需要注意一下.实际的看看步骤吧.1)将源文件和使用到的IP生成工程. ...
- Spring JDBC Framework详解——批量JDBC操作、ORM映射
转自:https://blog.csdn.net/yuyulover/article/details/5826948 一.spring JDBC 概述 Spring 提供了一个强有力的模板类JdbcT ...
- python模块的打包
python模块的打包方法: http://blog.csdn.net/five3/article/details/7847551
- MonoBehaviour.OnValidate
[MonoBehaviour.OnValidate] This function is called when the script is loaded or a value is changed i ...
- Unknown type name 'NSString'
今天看到个问题,编辑工程提示Unknown type name 'NSString',如下图 导致出现异常的原因是是因为工程中添加了ZipArchive(第三方开源解压缩库) 一般情况下出现“Unkn ...
- [z]微信平台开发教程
http://blog.csdn.net/lyq8479?viewmode=contents
- webform调用windows服务
准备工作: .电脑->管理->本地用户和组->组->Administrator双击->隶属->添加Network service->确定 .启动windows ...
- LIst和table的转换
public static class DataTableExtensions { /// <summary> /// 转化一个DataTable /// </summary> ...
- 关于Android控件EditText的属性InputType的一些经验,java组合多个参数
关于Android控件EditText的属性InputType的一些经验 2013-11-14 15:08:02| 分类: 默认分类|举报|字号 订阅 1.InputType属性在代码中 ...