如何让普通变量也支持事务回滚?
有一次和人谈起关于事务的话题,谈到怎样的资源才能事务型资源。除了我们经常使用的数据库、消息队列、事务型文件系统(TxF)以及事务性注册表(TxR)等,还有那些资源直接可以纳入事务进行状态的管理呢?我说如果我们按照.NET事务模型的规范对相应的资源进行合理的封装,原则上我们可以让任何可编程的资源成为事务型资源。本篇文章中,我将通过简单的编程将一个普通的变量变成支持事务,让变量的值也可以回滚,以确保事务前后的数据一致性。
一、什么是事务型的变量
本文中所说的事务型变量指的是这样的变量:
- 在事务开始前,变量的初始值会被保存;
- 在事务中对变量的赋值只有在事务被成功提交后才会真正赋值给变量;
- 如果事务中止导致回滚,变量的值将会恢复到事务开始之前的状态。
上面的对事务型变量的描述可以通过下面的程序来体现:变量v在初始化时被赋值为1。然后通过TransactionScope开始一个事务,并将变量纳入该事务之中。在事务范围内将值赋值为2,然后调用DoSomething方法,并提交事务。如果DoSomething执行过程中抛出异常,整个事务将会回滚。当整个事务中止回滚后,变量v的值回复到事务开始之前的状态,即值为1。
1: static void Main(string[] args)
2: {
3: TransactionalVariable<int> v = new TransactionalVariable<int>(1);
4: try
5: {
6: using (TransactionScope transactionScope = new TransactionScope())
7: {
8: Transaction.Current.EnlistPromotableSinglePhase(v);
9: v.Value = 2;
10: DoSomething();
11: transactionScope.Complete();
12: }
13: }
14: catch
15: { }
16: Debug.Assert(v.Value == 1);
17: }
二、简单谈谈System.Transactions事务模型
事务型变量的性质已经说得很清楚了,现在根本的任务就是如何来定义这样的一个事务性变量类型,即上面实例程序中的TransactionalVariable<T>类型。不过在这之前,我们有必要简单看谈谈System.Transactions的事务模型。对于所有的事务参与者,按照各自在整个事务生命周期各个阶段所承担的职能,大致扮演着如下三种角色:
- 应用(Application)、服务(Service)或者组件(Component):代表用户程序,或者是承载着某功能的服务(Service)或者组件(Component);
- 资源管理器(RM:Resource Manager):代表用于管理具体事务型资源的软件程序,比如数据库或者队列(MSMQ)等;
- 事务管理器(TM: Transaction Manager):代表管理整个事务的中间件程序,为应用和资源管理器提供基本的事务控制服务。
关于System.Transactions具体的事务管理模型,可以参考我的文章《谈谈分布式事务之二:基于DTC的分布式事务管理模型[上篇]》,在这里就不在赘言介绍了。总而言之,只要我们能够为变量编写相应的“资源管理器”,我们就能够将其纳入到System.Transactions.Transaction之中。在System.Transactions体系中,编写事务管理器是一件很简单的事情,一种非常直接的方式就是实现IPromotableSinglePhaseNotification这么一个接口。实例代码中使用的TransactionalVariable<T>类型就是这么定义的。
三、通过实现IPromotableSinglePhaseNotification接口定义TransactionalVariable<T>
在具体介绍TransactionalVariable<T>的定义之前,我们不妨来看看IPromotableSinglePhaseNotification接口是如何定义的。下面的代码片断反映了IPromotableSinglePhaseNotification的定义:加上从父接口继承下来的成员,整个IPromotableSinglePhaseNotification接口一共具有4个方法成员。Initialize方法会在资源纳入事务的时候被调用,用于执行一些初始化操作。SinglePhaseCommit、Rollback和Promote用于通知事务正在被提交、回滚和提升。
1: public interface IPromotableSinglePhaseNotification : ITransactionPromoter
2: {
3: void Initialize();
4: void Rollback(SinglePhaseEnlistment singlePhaseEnlistment);
5: void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment);
6: }
7: public interface ITransactionPromoter
8: {
9: byte[] Promote();
10: }
TransactionalVariable<T>直接实现了IPromotableSinglePhaseNotification接口,下面是全部的定义。TransactionalVariable<T>中定义了两个数据成员,字段_originalValue和属性Value代表变量的初始值和当前值。
- Initialize:将当前值赋给初始值,此时两者具有相同的值;
- Rollback:将初始值赋给当前值,并调用SinglePhaseEnlistment的Aborted方法通知终止事务,这意味着事务过程中对变量的修改都将丢失;
- SinglePhaseCommit:将当前值赋给初始值,并调用SinglePhaseEnlistment的Committed方法通知提交事务,相当于将事务中对变量的修改正式生效;
- Promote:由于我们只打算让我们的事务型变量支持本地事务的场景,并不对分布式事务提供支持,在这里直接抛出一个异常
1: using System.Transactions;
2: namespace Artech.TransactionalObjects
3: {
4: public class TransactionalVariable<T> : IPromotableSinglePhaseNotification
5: {
6: private T _originalValue;
7: public T Value { get; set; }
8:
9:
10: public TransactionalVariable(T variable)
11: {
12: this.Value = variable;
13: }
14:
15: public void Initialize()
16: {
17: _originalValue = this.Value;
18: }
19:
20: public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
21: {
22: this.Value = _originalValue;
23: singlePhaseEnlistment.Aborted();
24: }
25:
26: public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
27: {
28: _originalValue = this.Value;
29: singlePhaseEnlistment.Committed();
30: }
31:
32: public byte[] Promote()
33: {
34: throw new TransactionException("TransactionalVariable just only support local transaction.");
35: }
36: }
37: }
以上就是所有的实现,并没有什么特别之处,仅仅就是通过实现对初始值的缓存,进而实现在事务中止时能够将值恢复到之前的状态。你可以通过这里下载该例子。不过,这个例子仅仅是一个简单的模拟演示而已,还有很多不足之处。比如事务四大属性的隔离性在TransactionalVariable<T>就不能体现出来。
- 一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
- 解决mstsc无法连接问题:由于没有远程桌面授权服务器可以提供许可证…
- Apache/Nginx伪静态规则匹配http://出现的问题与解决
- 微信文件微起底
- Go语言TCP Socket编程--1
- Go语言TCP Socket编程--2
- 服务器 数据库设计技巧--1
- CVE-2015-0235:Linux glibc高危漏洞的检测及修复方法
- zabbix监控在lnmp环境下编译安装小记
- 【重磅】百度开源分布式深度学习平台,挑战TensorFlow (教程)
- WordPress评论ajax动态加载,解决静态缓存下评论不更新问题
- WordPress显示访客UA信息:Show UserAgent纯代码轻度汉化版
- WordPress开启颜色评论但不造成XSS漏洞的小方法
- WordPress强迫症技巧:让文章(ID)地址完美连续(障眼法)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Leetcode 289. 生命游戏(元胞自动机模拟)
- Nginx 简介
- 详解 Vue 目录及配置文件之 package.json
- Codeforces Round #382 (Div. 2) D. Taxes (数论 哥猜 大胆尝试)
- Vue Router
- Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 1) B. Bear and Blocks (技巧dp 难想)
- Codeforces 727D-T-shirts Distribution (字符串 贪心)
- 详解 Vue 目录及配置文件之 build 目录
- Linux 常见文件管理命令及目录结构(1)
- Java parseInt( ) 方法
- 详解 Vue 目录及配置文件之 node_modules,src,static,test 目录
- 洛谷 P1077 摆花(记忆化搜索 or DP)
- Vue 使用 element-ui
- Codeforces Round #629 (Div. 3) F. Make k Equal (技巧暴力,类前缀和,思维,数学)
- vue 渐变色文字