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系列_分布式事务(下)相关推荐
- mysql分布式事务wcf_[转载]WCF系列_分布式事务(下)
浏览到chnking的WCF的分布式事务处理不错,转载过来分享一下.1. WCF分布式事务例子这里也用转账的例子说事. 用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B ...
- mysql分布式事务wcf_WCF系列_分布式事务(下)
1.WCF分布式事务例子 这里也用转账的例子说事. 用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B的相应账户的资金就增加多少. 系统A机器上有数据库AccountA, ...
- 分布式系统漫谈【拾】_分布式事务一致性:阿里方案
上篇文章:分布式系统漫谈[玖]_分布式事务一致性:协议支持 其实对于生产环境的分布式事务一致,各大互联网公司都是自己实现的解决方案,总结起来无非是异步.补偿.实时查询.定期校对几种模式,大部分场景都是 ...
- cap理论具体含义_分布式事务的CAP理论
相关历史文章(阅读本文之前,您可能需要先看下之前的系列 ) 分布式事务「2020年」必学,升职加薪你准备好了吗? 事务的基本概念 事务的四大特性ACID 分布式事务产生的场景 前言 通过前面的学习,我 ...
- Spring Cloud Alibaba系列之分布式事务Seata
Spring Cloud Alibaba系列之分布式事务Seata 1.分布式事务 分布式事务不是在现在微服务分布式架构上才产生的问题,在单体应用同样存在分布式事务问题,典型的场景就是单体应用使用了多 ...
- mysql ef 分布式事务_分布式事务系列--分布式跨库查询解决方案 mysql federated引擎的使用...
背景 在服务高度拆分,数据库不断细化切分的情况下,我们经常有连接多台数据库查询的需求,如果不断的把数据库连接的逻辑添加在代码中,那么这种耦合会越来越严重,这会给程序的拓展和维护带来很大的麻烦. mys ...
- 事务控制 新增后修改_分布式事务科普(初识篇)
点击上方"IT牧场",选择"设为星标" 技术干货每日送达! <分布式事务科普>是我在YQ期间整理的一篇科普型文章,内容共计两万五千字左右,应该算是涵 ...
- Zookeeper概念学习系列之分布式事务
不多说,直接上干货! 初学者来说,肯定会有这么一个疑问.为什么会在zookeeper里牵扯到分布式事务? zookeeper到底是什么? zookeeper实际上是yahoo开发的,用于分布式中一致性 ...
- vb6 串口同时读取写入数据怎么避免冲突_分布式场景下的数据复制究竟怎么做...
主从复制 集群中有一个主节点,写操作都必须经过主节点完成,读操作主从节点都可以处理. 同步复制 数据在副本上落盘才返回. 优点:保证在副本上的数据是最新数据. 缺点:延迟高,响应慢. 异步复制 数据不 ...
最新文章
- 千呼万唤,ACS始出来
- java-执行流程控制语句
- 推荐一个原型设计工具Balsamiq Mockups
- win10系统日志显示不可用sid_win10系统电脑修改sid安全标识符的操作方法
- nginx 日志和监控
- 七,OpenERP 移库操作模块
- canvas arc() 方法绘制弧线、曲线、圆形,rect() 绘制矩形
- 关于c语言从入门到精通那些事
- matlab建模方法,MATLAB 数学建模方法与实践(第 3 版)
- Matlab | matpower5.0:最优潮流执行步骤及修改教程
- cuda历史版本下载
- php读取本地txt,php读取本地文件常用函数(fopen与file_get_contents)_PHP教程
- MYSQL函数group_concat的使用
- 201809-1 卖菜(100分)
- 纹理的应用(凹凸贴图与法线贴图,三维噪声和三维纹理)
- with在python中啥意思_python中with的用法
- 【软件下载】换新电脑记录下下载的软件时所需地址
- 2017年度最值得读的AI论文 | CV篇 · 评选结果公布
- vue所有页面刷新一次mounted(以及所有生命周期函数)执行两次的解决方法
- 鲁大师PC最新硬件排行,汇总Q1季度最强PC硬件产品!