WCF的知识点盘点(五)分布式事务

勇哥注:

此系列文章是梳理一下勇哥认为项目有用的WCF的知识点,读者须有WCF的开发经验。



(一)为什么需要分布式事务?

见下图,Client需要向192.168.10.2的服务器请求处理ProductNum--, 然后再向192.168.10.1的服务器请求处理AddOrder。

由于请求跨了不同的服务器,因此需要分布式的事务来处理此种情况。

image.png


(图1)


分布式事务


ACID [单机版]

将多个节点的操作纳入一个执行单元里


CAP理论:

Consistency(一致性)  数据最终都能够落地[立即落地(强一致性),某时间后落地(最终一致性)]

Availability(可用性)   理想的响应性能

Partition tolerance(分区容错性)  某些网络阻塞或者机器挂掉不影响集群


CAP理论中,最重要的是P,先有P [分布式], 再才有C,或者A

注意:C和A是不可能同时满足的。


二。分布式事务实现方式(CAP理论)


1。2PC提交  【多个节点的CURD操作】

    将一个分布式消息拆解成多个单节点上的CURD,通过事务协调器来进行协调。

  [准备阶段,执行阶段],每个阶段都要向事务协调者汇报[性能低劣]


  wcf的事务,本质上就一个2PC的操作

  2PC的缺点就是性能低下,完全依赖事务协调器,但是它能保证强一致性。


下图是2PC方式的示意:

后面的wcf代码用来实现此方式。

image.png

(图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端会对两个数据库表进行操作。


image.png


数据库勇哥用的是SQL Server 2014。

两个表:OrderDB, Product

image.png

image.png

Product表要弄个初始值,其中ProductNums表示库存数量。

image.png


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

image.png


如果你在scope.Complete(); 这句上下断电,再查询数据表,会发现一直显示正在执行查询。

这是因为事务一直不进行commit,数据库此时在等待数据源的响应。

image.png


最后一个问题,那么wcf的事务协调器在哪里呢?

这是windows的一个服务,叫 Distributed Transaction Coordinator。

image.png


源码下载:


链接:https://pan.baidu.com/s/1GgYGt_jtQFUxw1Q_YY_h0g 

提取码:u6ii 

--来自百度网盘超级会员V6勇哥的分享


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2024-06-25 10:50:54 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864