美文网首页.NETWEB前端程序开发程序员
WebForm / WebApi 分布式事务(DTC)实例

WebForm / WebApi 分布式事务(DTC)实例

作者: gruan | 来源:发表于2017-02-14 11:45 被阅读0次

    目前 .NET Core 不支持System.Transactions 这个命名空间,所以火遍全球的 .NET Core 不在本示例的讨论范围。WCF 天生支持分布式事务,所以也不讨论。本文主要讨论 WebApi / WebForm 如何支持 DTC.

    为什么要用 DTC

    我司现在的系统架构很老了,大部分还是通过 ashx 在内部各个系统间传输数据。要命的订单处理流程涉及到多个服务(网站),如果哪个环节出了个小漏子,那只能手动重来一次了。为了解决这个问题,我研究了下 DTC(虽然很老了,但是一直没机会接触过)。

    DTC

    分布式事务 Distributed Transaction Coordinator , Windows 下叫 MSDTC
    要使用 DTC 当然要开启这个服务,为了简单期间,我们这样设置:

    • 控制面板\所有控制面板项\管理工具
    • DTC 设置

    示例地址

    https://github.com/gruan01/DTCTest

    原理

    • 发起请求的时候,在请求头中添加当前事务的传播标识。
    • 服务端收到请求的时候,读取请求头中的传播标识。
    • 如果存在,跟据接收到的传播标识还原事务。
    • 在客户端决定是提交还是回滚 。

    从客户端发起一个需要 DTC 的调用

    if (Transaction.Current != null) {
        var token = TransactionInterop.GetTransmitterPropagationToken(Transaction.Current);
        xxx.Headers.Add("TransactionToken", Convert.ToBase64String(token));
    }
    

    根据你的使用的组件自行修改。

    WebForm 如何支持 DTC

    由于生命周期的限制,事务的还原与使用不能在 Global 的 Application_BeginRequest / Application_EndRequest 中,实测无效。

    简便期间,我做了一个包装:

    public class DTCWrapper : IDisposable {
    
        private static readonly string TransactionID = "TransactionToken";
    
    
        private TransactionScope Scope = null;
        private Transaction Trans = null;
    
        public DTCWrapper() {
            var context = HttpContext.Current;
            if (context.Request.Headers.AllKeys.Contains(TransactionID)) {
                var values = context.Request.Headers.GetValues(TransactionID);
                if (values != null && values.Any()) {
                    var token = Convert.FromBase64String(values.First());
                    this.Trans = TransactionInterop.GetTransactionFromTransmitterPropagationToken(token);
                    //var scope = new TransactionScope(trans, TransactionScopeAsyncFlowOption.Enabled);
                    this.Scope = new TransactionScope(this.Trans);
                }
            }
        }
    
        public void Commit() {
            if (this.Scope != null)
                this.Scope.Complete();
        }
    
        public void Rollback(Exception ex = null) {
            if (ex != null)
                this.Trans.Rollback(ex);
            else
                this.Trans.Rollback();
        }
    
        #region dispose
        public void Dispose() {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
    
    
        ~DTCWrapper() {
            this.Dispose(false);
        }
    
    
        private bool isDisposed = false;
        private void Dispose(bool flag) {
            if (flag && !isDisposed) {
                isDisposed = true;
                if (this.Trans != null) {
                    this.Trans.Dispose();
                    this.Trans = null;
                }
    
                if (this.Scope != null) {
                    this.Scope.Dispose();
                    this.Scope = null;
                }
            }
        }
        #endregion
    }
    

    使用:

    private void Process(string ctx) {
        using (var dtc = new DTCWrapper())
        using (var db = new TestDbContext()) {
            db.Logs.Add(new Data2.Models.Log() {
                CreateOn = DateTime.Now,
                Ctx = ctx
            });
    
            db.SaveChanges();
            dtc.Commit();
        }
    }
    

    WebApi / MVC 如何支持 DTC

    WebApi 就没有 WebForm 那样复杂的生命周期了,而且 WebApi / MVC 的 ActionFilter 特性可以很方便的对 Action 的执行前/执行后做手脚,所以 WebApi / MVC 对 DTC 的支持简直太那个了。。。

    public class DTCAttribute : ActionFilterAttribute {
    
    
        private static readonly string TransactionID = "TransactionToken";
    
        public override void OnActionExecuting(HttpActionContext context) {
            base.OnActionExecuting(context);
    
            if (context.Request.Headers.Contains(TransactionID)) {
                var values = context.Request.Headers.GetValues(TransactionID);
                if (values != null && values.Any()) {
                    var token = Convert.FromBase64String(values.First());
                    var trans = TransactionInterop.GetTransactionFromTransmitterPropagationToken(token);
    
                    var transactionScope = new TransactionScope(trans);
    
                    context.Request.Properties.Add(TransactionID, transactionScope);
                }
            }
        }
    
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
            base.OnActionExecuted(actionExecutedContext);
    
            if (actionExecutedContext.Request.Properties.ContainsKey(TransactionID)) {
                var tScope = (TransactionScope)actionExecutedContext.Request.Properties[TransactionID];
    
                if (tScope != null) {
                    if (actionExecutedContext.Exception != null) {
                        Transaction.Current.Rollback(actionExecutedContext.Exception);
                    } else {
                        tScope.Complete();
                    }
    
                    tScope.Dispose();
                    actionExecutedContext.Request.Properties.Remove(TransactionID);
                }
            }
        }
    }
    

    使用:

    [DTC]
    public class TestController : ApiController {
    。。。
    

    如何看测试效果

    请移步 https://github.com/gruan01/DTCTest/

    参考

    https://code.msdn.microsoft.com/Distributed-Transactions-c7e0a8c2

    相关文章

      网友评论

        本文标题:WebForm / WebApi 分布式事务(DTC)实例

        本文链接:https://www.haomeiwen.com/subject/evdxwttx.html