勇哥注:
此系列文章是梳理一下勇哥认为项目有用的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勇哥的分享

