本地事务和分布式事务工做实践

一:从事务的历史提及数据库

  知已知彼,百战不败。想了解事务,咱们从事务的历史提及。c#

  在Windows平台上,事务的概念最开始出如今关系型数据库中,可是随着.net平台的发展,事务包括的的范围也愈来愈宽,先一睹为快,跨域

  在关系型数据库中的事务是经过begin transaction,rollback transaction, commit 等关键字来实现事务的。服务器

BEGIN TRANSACTION  
UPDATE [dbo].[T_ACCOUNT] SET BALANCE = BALANCE + @amount WHERE ID = @toAccount  
IF @@ERROR <> 0   
BEGIN 
    ROLLBACK TRANSACTION  
END 
ELSE
    COMMIT TRANSACTION 

  随着面象对象的发展,.net的诞生,在.net 1.* 的版本中,能够经过ADO.NET来实现数据库的事务。把事务交给业务程序来控制,使业务程序的本职发挥的淋漓尽致,而从数据库的角度来看,数据库也能更专注的进行数据的存储。达到了各尽其责的效果。网络

using (DbTransaction transaction = connection.BeginTransaction())
 {
    command.Transaction = transaction;
     try
    {
        command.ExecuteNonQuery();
        transaction.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        throw new Exception(ex.Message);
    }
}

  万物是发展的,程序的发展也是永无止境的。某天有一个需求:建立一个客户信息,其中包括姓名和头像。姓名保存在数据库,头像保存在服务器的硬盘中。姓名和头像的保存有一个失败都是一个不完整的操做,都要回滚到初始状态。用ADO.NET的事务显示不能实现这个要求。由于回滚资源包括了数据库硬盘。基于这种状况,.net 2.0推出了System.Transactions事务。分布式

using (TransactionScope transactionScope = new TransactionScope())
{
    bankService.Pay("111", 50);
    bankService.Receipt("222", 50);
     transactionScope.Complete();
}

  System.Transactions名称空间下的事务,是.net的主推事务。ui

二:事务有关概念spa

  第一节,咱们都用代码把事务实践了一把,那还有必要来讲事务的概念和事务在运行过程当中涉及到的对象呢?若是你想深刻了解事务,这一步是必须的。.net

     事务的定义和属性:code

  1,什么是事务:它是一个操做序列,这些操做要么都执行,要么都不执行,它是一个不可分割的工做单位。

  2,事务特性:接触事务,都是从事务的ACID开始,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。每一个特性的定义僵硬,枯燥,不易理解,读了好几篇都有让人想跳楼的欲望。今儿换一个方式来讲事务特性。

  银行转款是事务的经典示例,可能由于钱与银行扯的上关系,一提到银行,你们都精神倍爽,为了避免另类,因此我也决定用银行转款来讲事务的四大特性。

  2.1:原子性:银行转款分为"转出"和"接收"两步,好比给异地结婚的朋友送礼金,本身的钱已经转出去了,可是朋友没有收到礼金,这时你们都心急如焚。因此转款必需要保证"转出"和"接收"要么都成功,要么都不成功。这就是转款的原子性。

  2.2:一致性:给朋友转款888元RMB做为婚礼的礼金,可是朋友只收到777元RMB。这时你不爽了,你朋友也不爽,因此转款要必须保证数据的一致性。

  2.3:隔离性:转款分为"转出"和"接收"两步,理论上是能够同时进行的,可是在实际生活中确有时间差,即便相差0.0001秒,在这段时间里,本身的帐户和朋友的帐户都不能进行查询操做,即便查询了,也是处于一种等待状态。在这期间外界没法对数据进行查询访问,因此称之为隔离性。

  2.4:持久性:转款这个操做被银行记录起来,过不少年后,你都还能查询到转款这个记录。这就是持久性。

   事务涉及的对象:

  1,资源:应用程序存储和获取数据的地方,能够是数据库,文件,也能够是内存。若是是应用程序的事务块代码中涉及到的数据库,文件,内存,那这些资源就称为事务型资源

  2,资源管理器:在事务模型中,应用不是直接访问资源,而是经过中间介访问资源,这个中间介就叫资源管理器。资源分为可持久化资源(对应了持久化资源管理),易失资源(对应了易失资源管理器)。

  3,事务管理器:实现事务的开始,提交,回滚。

  事务提高:

  要认识事务提高,必需要弄清楚事务管理器的类型。以下做了很是详细的介绍:

  1:轻量级事务管理器:做用于开启事务的应用程序域,只能包含一个持久化资源,若是再添加一个持久化资源,将被轻量级事务管理器忽略。可是能够登记多个易失资源。目前轻量级事务管理器只支持SQL 2005以及SQL2005以上的版本持久化资源。

  3:内核事务管理器:在Vista被引入,并后续于Windows Server 2008,WIN7。引入内核事务管理器主要是把文件管理(NTFS文件系统)和注册表管理归入事务范畴。咱们将那些支持事务的文件系统和注册表叫做事务型文件系统(TxF)和事务型注册表(TxR)。 之因此称为内核事务管理器,是因它运行在内核模式上,而不是在用户模式上。一样内核事务管理器只支持一个持久化资源。

  3:分布式事务协调器:每一台电脑上只有一个分布式事务协调器,它管理了当前计算机的全部事务资源。它能够跨程序域,跨进程,跨机器,跨网络来执行事务。当事务跨机器时,每台机器的分布式事务协调器按照相应的协议工做,实现对整个事务的管理,它对应的事务管理协议有Ole-Tx和WS-Atomic Transaction(WS-AT)这些。 分布式事务协调器可以管理一个分布式事务涉及的全部事务型资源,无论具体的事务型资源分布在何处。

  事务提高:事务是一个动态执行的操做序列,在整个过程当中,不可能预知资源的登记状况。因此轻量级事务管理器做为默认的事务管理器,随着事务的逐步执行,若是涉及到内核事务资源,那么将提高为内核事务管理器。若是出现对多个事务资源的访问,或者当前事务涉及跨域(调用另一个服务),就会提高为分布式事务协调器。Windows采用事务提高机制进行事务管理器的选择。

  

三:事务的分类

  1:本地事务

  轻量级事务管理器,内核事务管理器都只支持本地事务。本地事务相对简单,这儿不做重点简述。

  2:分布式事务

  理解分布式事务是怎样实现的,事务提交树是关键。

  事务提交树:事务提交树的根是事务初始化服务所在的机器的DTC,它在整个事务提交过程当中充当着总协调者,又被称为全局提交协调器。资源管理器充当着事务提交树的叶子节点,它们的父结点为本机的DTC,分布于不一样机器的DTC按照事务的传播路径造成了上下级关系。

  

  在一个分布式事务中,事务的初始化和提交是属于一个对象,只有最初开始的事务才能被提交,咱们将这种能被初始化和提交的事务称做可提交事务。随着参与者逐个登记到事务中,它们本地的事务实际上依赖着这个最初开始的事务,因此咱们称这种事务叫依赖事务。

四:示例

  文件事务

  在MSDN上对文件事务有详细的阐述  使用文件系统事务加强您的应用程序  咱们若是是仔细阅读这篇文章不难发现他提供了一个.exe类型文件的下载。先把这个TxF2007_07.exe文件下载到本地硬盘,执行它,能够获得一个关于 c#的 KtmIntegration.csproj 的项目,咱们用visual studio来打开这个项目,而且从新重成这个项目,能够获得一个KtmIntegration.dll文件。在你要实现的文件事务的项目中引入这个.dll文件,那你就能够很顺利的实现文件事务的操做了。具体代码:

using System;
using System.IO;
using System.Transactions;
using Microsoft.KtmIntegration;

namespace Exercise.WebLocalTransaction
{
    public partial class Test02 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            using (TransactionScope transactionScope = new TransactionScope())
            {
                try
                {
                    FileStream stream = TransactedFile.Open(
                        @"D:/Sam Xiao.txt"
                        , FileMode.OpenOrCreate
                        , FileAccess.ReadWrite
                        , FileShare.ReadWrite);
                    StreamWriter writer = new StreamWriter(stream);
                    writer.WriteLine(String.Concat("执行一个事务代码:",DateTime.Now.ToString(),Environment.NewLine));
                    writer.Close();
                    //int x = 0;
                    //int y = 10;
                    //int z = y / x;
                    transactionScope.Complete();
                }
                catch
                {

                }
            }
        }
    }
}

不要忘记了引入using Microsoft.KtmIntegration;名称空间。在段代码没有异常的状况下,咱们能够看到D盘里顺利建立了一个关于Sam Xiao.txt的文件。咱们故意在这段代码中抛出一个被0整除的异常,那么整个操做就会回滚。

  分布式事务

  在.net平台上,主要是经过WCF的手段来实现程序的分布式开发。在WCF事务体系:主要解决了事务在服务中的流转,以及解决服务内部直接或间接访问事务型资源的协做。

  用WCF来演示事务的时候,要选择好WCF的绑定类型,有一部份绑定是不支持WCF的事务传播的。咱们选择wsHttpBinding 来作WCF的事务演示。

  1,首先定义好WCF的服务契约

[ServiceContract(Name = "IBankingService")]
public interface IBankingService
{
      
    [TransactionFlow(TransactionFlowOption.Mandatory)]
    [OperationContract(Name = "Transfer")]
    void Transfer(string fromAccountId, string toAccountId, double amount);

    [TransactionFlow(TransactionFlowOption.Mandatory)]
    [OperationContract(Name = "Pay")]
    bool Pay(String accountID, double amount);

    [TransactionFlow(TransactionFlowOption.Mandatory)]
    [OperationContract(Name = "Receipt")]
    bool Receipt(String accountID, double amount);
}

  2,实现WCF的服务

[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]
public class BankingService : IBankingService
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void Transfer(string fromAccountId, string toAccountId, double amount)
    {
        throw new NotImplementedException();
    }

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public bool Pay(string accountID, double amount)
    {
        throw new NotImplementedException();
    }

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public bool Receipt(string accountID, double amount)
    {
        throw new NotImplementedException();
    }
}

  3,WCF宿主配置

<system.serviceModel>
      <behaviors>
          <serviceBehaviors>
              <behavior name="sBehaviorConfig">
                  <serviceMetadata httpGetEnabled="true" />
                  <serviceDebug includeExceptionDetailInFaults="true" />
              </behavior>
          </serviceBehaviors>
      </behaviors>

      <bindings>
        <wsHttpBinding>
          <binding name="wshttpConfig" transactionFlow="true" >
            <security mode="None" />
          </binding>
        </wsHttpBinding>
      </bindings>
      
      <services>
        <service name="Exercise.Service.BankingService" behaviorConfiguration="sBehaviorConfig">
          <endpoint address="mex"  binding="wsHttpBinding" bindingConfiguration="wshttpConfig" contract="Exercise.Contract.IBankingService"></endpoint>
        </service>
      </services>
      <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    </system.serviceModel>

  4,WCF客户端配置

<system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IBankingService" transactionFlow="true">
          <security mode="None" />
        </binding>
      </wsHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:9100/BankingService.svc/mex"
          binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IBankingService"
          contract="Exercise.Contract.IBankingService" name="WSHttpBinding_IBankingService" />
    </client>
  </system.serviceModel>

  5,调用服务

IBankingService bankService = WcfProxy.CreateProxy<IBankingService>("WSHttpBinding_IBankingService");
protected void Page_Load(object sender, EventArgs e)
{
    using (TransactionScope transactionScope = new TransactionScope())
    {
        bankService.Pay("111", 50);
        bankService.Receipt("222", 50);
        transactionScope.Complete();
    }
}

  此时咱们WCF演示事务的五步曲就完成了。咱们作敏捷开发都知道,可运行的代码高于详细的文档。若是还有不太明白的地方,能够在网上搜索,也能够留言问我。

五:总结

  感受"赞"的帮忙【推荐】,大家的推荐是个人动力。

  滴水之恩,当涌泉相报。回报帮助过个人人,以及须要我帮助的人源码下载

相关文章
相关标签/搜索