勇哥注:
此系列文章是梳理一下勇哥认为项目有用的WCF的知识点,读者须有WCF的开发经验。
(一)为什么需要分布式事务?
见下图,Client需要向192.168.10.2的服务器请求处理ProductNum--, 然后再向192.168.10.1的服务器请求处理AddOrder。
由于请求跨了不同的服务器,因此需要分布式的事务来处理此种情况。
(图1)
分布式事务
ACID [单机版]
将多个节点的操作纳入一个执行单元里
CAP理论:
Consistency(一致性) 数据最终都能够落地[立即落地(强一致性),某时间后落地(最终一致性)]
Availability(可用性) 理想的响应性能
Partition tolerance(分区容错性) 某些网络阻塞或者机器挂掉不影响集群
CAP理论中,最重要的是P,先有P [分布式], 再才有C,或者A
注意:C和A是不可能同时满足的。
二。分布式事务实现方式(CAP理论)
1。2PC提交 【多个节点的CURD操作】
将一个分布式消息拆解成多个单节点上的CURD,通过事务协调器来进行协调。
[准备阶段,执行阶段],每个阶段都要向事务协调者汇报[性能低劣]
wcf的事务,本质上就一个2PC的操作
2PC的缺点就是性能低下,完全依赖事务协调器,但是它能保证强一致性。
下图是2PC方式的示意:
后面的wcf代码用来实现此方式。
(图2)
延伸话题:
三。使用队列进行”分布式事务处理“ (解决2PC性能低的问题)
1。 ”交钱“和”拿饭“是两个动作
为了增加处理量,只需要给交钱的客户一个小票,然后在出货处等待叫号,只要你有这个小票,就一定能拿到”饭 “的
这样其实就是使用异步的方式来增加性能。
这种方式的特点是:能达到最终的一致性,性能优越
四。wcf的分布式有DTC参与协调,它就是一个事务协调器,相当于2PC中的事务协调器
分布式事务,CAP理论中,追求 最终一致性。
五。使用WCF实现分布式事务
步骤:
1。服务契约上要指定会话binding,不是则抛异常
[ServiceContract(SessionMode=SessionMode.Required)]
2。操作契约上一定要指定该操作可以作为事务流的一部分。[TransactionFlow(TransactionFlowOption.Allowed)]
3. 实现方法上需要指定该方法纳入事务TransactionScope事务范围 [OperationBehavior(TransactionScopeRequired=true)]
4. config中指定支持会话binding, 并开启事务流支持。
六。总结
分布式事务实现起来还是有难度的。
许多电商项目,都会出现超卖现象。
分布式事务是一个很大的研究课题。
(二)演示代码
这个演示程序的需求见图1。
一个Client,两个wcf端。
事务包括两个节点的操作:一是AddOrder,添加一条订单;二是ProductNum--,用于把库存减1
这两个节点的操作被纳入一个事务流中。它们要么一起成功,要么一起失败。
两个wcf端会对两个数据库表进行操作。
数据库勇哥用的是SQL Server 2014。
两个表:OrderDB, Product
Product表要弄个初始值,其中ProductNums表示库存数量。
BusinessLayer项目使用了LINQ to SQL工具,生成了数据表和对应的实体类。
wcf的几个实现方法用这个实体类操作数据库。
Order的服务契约和操作契约:
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace OrderWcfService { [ServiceContract(SessionMode = SessionMode.Required)] public interface IOrderService { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void AddOrder(DataLib.Model.Orders order); } }
Order的实现方法:
using BusinessLayer; using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace OrderWcfService { public class OrderService : IOrderService { [OperationBehavior(TransactionScopeRequired = true)] public void AddOrder(DataLib.Model.Orders order) { using (OrderDBDataContext context = new OrderDBDataContext()) { context.Orders.InsertOnSubmit(new BusinessLayer.Orders() { OrderID = order.OrderID, OrderName = order.OrderName, CreateTime = order.CreateTime, ProductID = order.ProductID }); context.SubmitChanges(); } } } }
Product的服务契约和操作契约:
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace ProductWcfService { [ServiceContract(SessionMode = SessionMode.Required)] public interface IProductService { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void DecreaseNum(int productID); } }
Product的实现方法:
using BusinessLayer; using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace ProductWcfService { public class ProductService : IProductService { [OperationBehavior(TransactionScopeRequired = true)] //添加到数据库 public void DecreaseNum(int productID) { //自减操作 using (ProductDBDataContext context = new ProductDBDataContext()) { var product = context.Product.FirstOrDefault(i => i.ProductID == productID); Console.WriteLine(product?.ProductID); if (product != null) { product.ProductNums--; context.SubmitChanges(); } } } } }
WcfClient代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Transactions; using WcfClient.clientOrder; using WcfClient.clientProduct; namespace WcfClient { class Program { static void Main(string[] args) { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew)) { OrderServiceClient clientOrder = new OrderServiceClient(); ProductServiceClient clientProduct = new ProductServiceClient(); //事务1:增加订单 clientOrder.AddOrder(new Orders() { OrderID=1, CreateTime=DateTime.Now, OrderName="订单A", ProductID=1 }); //事务2:减productID的库存 clientProduct.DecreaseNum(1); scope.Complete(); Console.WriteLine("事务处理完成!"); } Console.ReadKey(); } } }
运行后,会看到商品库存减1
如果你在scope.Complete(); 这句上下断电,再查询数据表,会发现一直显示正在执行查询。
这是因为事务一直不进行commit,数据库此时在等待数据源的响应。
最后一个问题,那么wcf的事务协调器在哪里呢?
这是windows的一个服务,叫 Distributed Transaction Coordinator。
源码下载:
链接:https://pan.baidu.com/s/1GgYGt_jtQFUxw1Q_YY_h0g
提取码:u6ii
--来自百度网盘超级会员V6勇哥的分享

