代码
https://yunpan.cn/cPns5DkGnRGNs 密码:3913
不是特殊的Demo,我们不再贴实例Demo的图片了,直接去网盘找相应的项目看
继续说可靠性中的 事物 以及 消息队列 两块内容。
二、事务 :
事务是系统可靠性的一个重要特征。在两个或两个以上的活动必须要作为单一的操作加以协调的时候,事务是非常重要的,它可以确保系统永远处于一致的状态。仅有的两个可行方案,一是成功完成所有活动,一是把系统置为发起任何活动之前的状态。 经典的形容事务的缩写,它必须有ACID属性。
ACID代表如下意义
1.原子性:一项事务是一个要么全有要么没有的概念。假设事务做出指示,要插入两条数据库记录,那么两个插入操作必须作为单一的原子操作完成,或者成功或者失败。
2.一致性:事务绝不会将系统置为不一致的状态。假设其中的一个插入完成,然后另外的调用者就会看到这条插入的记录,如果事务失败,那么这个插入操作会回滚。
3.独立性:独立的事务,可各自完成操作,直至它们完成各自的工作。假设第二个事务试图插入记录到同一个表中,而第一个事务在进行中,那么第二个事务应被锁定,直到第一个事务完成,无论其成功与否。
4.持久性:这意味着不应该丢失任何事务。假设一个事务正在处理中时,机器宕机了,那么被访问的资源(通常是数据库),以及事务协调器在失败之后仍要存在,这就需要在计算机恢复时,存储足够的消息来完成事务。
我们看下 如何配置WCF事务
客户端和服务之间的事务流程,是绑定配置的一项功能,是服务契约的要求,也是被启用的服务操作。
1:配置的绑定必须能够支持事务流程,而且必须启用特性。
2:必须选中适当的事务协议。(也就是说有些绑定是不支持事物的)
3:定义在服务契约中的操作必须明确支持事务流程。
4:服务操作必须决定它们是否需要事务,是否愿意加入一个客户端事务。
启用事务流程(配置的绑定必须能够支持事务流程,而且必须启用特性。)
为了使客户端事务能够跨越服务边界流动,绑定必须支持事务流程,这会限制你只能使用这些标准绑定中的一个:NetNamedPipeBinding,NetTcpBinding,WSHttpBinding,WSDualHttpBinding。
每一个绑定都有TransactionFlow属性,默认值设置为false。为了启用事务流程,你可以为WSHttpBinding声明设置此属性为true。
如下所示:
1 <wsHttpBinding> 2 <binding name="wsHttpTx" transactionFlow="true"/> 3 </wsHttpBinding>
选择事务协议(必须选中适当的事务协议。)
在某些情况下,当你为服务终端启用事务流时,还可以控制支持事务流程的协议。该TransactionProtocol抽象类型有两个接口,定义了两种对协议的选择。TransactionProtocol公开了静态特性,以在运行时选择以下实现。
OleTransactions
这个设置表明你的服务终端支持标准的RPC方式来协调事务。
WSAtomicTransactionOctober2004
此设置表明如果有DTC的支持,那么就会支持WS-AT互操作消息传送。目前,这是WCF支持的唯一版本规范。
对于使用HTTP协议的绑定,TransactionProtocol设置总是WSAtomicTransactionOctober2004,所以当你设置绑定时,不必指定该设置。
默认情况下,NetTcpBinding和NetNamedPipeBinding支持OleTransactions。
你可以按如下方式在绑定配置中做到绑定别的协议:
1 <netTcpBinding> 2 <binding name="netTcpTx" transactionFlow="true" transactionProtocol="WSAtomicTransactionOctober2004"> 3 </netTcpBinding>
事务流选项(定义在服务契约中的操作必须明确支持事务流程)
启用绑定的事务流使得客户端事务可以跨服务边界流动,但服务契约中的每个操作,最终都会决定自己的事务流量要求。TransactionFlowAttribute可以应用到服务契约的每一个操作中,以明确选择下列枚举值之一。
1.NotAllowed:此设置表明该服务的操作不会接受客户端事务的请求。这是默认设置,并应被应用于永远不会参与客户端事务的服务操作中。例如单向操作。
2.Mandatory:表明客户端事务必须流向服务。服务模型使客户端事务对加入的操作可用。只有当服务契约总是包括一起调用的操作,并总是事务的一部分时,这个选项才有用。由于这要求服务契约的设计是紧耦合的,因此是要避免的。
3.Allowed:如果客户端的事务流向服务,服务模型将使事务加入到服务操作中。这个选项是有用的。他让客户端能够作为事务的一部分有选择地调用服务操作。
我们如何在服务契约中设定TransactionFlowOption?
1 [ServiceContract] 2 public interface ICountersService 3 { 4 [OperationContract] 5 [TransactionFlow(TransactionFlowOption.Allowed)] 6 void ResetCounters(); 7 }
虽然TransactionFlowOption确实影响客户端事务的表现,但它并不保证服务操作将使用事务。在实现服务契约过程中,每一个操作都必须被明确加入以支持事务。这是由OperationBehaviorAttribute的TransactionScopeRequired属性控制的。
如下所示:
1 [OperationBehavior(TransactionScopeRequired = true)] 2 public void ResetCounters() 3 { 4 5 }
当服务契约的实现中某个方法的TransactionScopeRequired设置为false时(这是默认值),该服务将永远不会加入一个客户端事务,也不会自动地创建一个新的事务。
这意味着,如果该服务契约的实现中的某一服务需要一个事务时,它必须使用TransactionScope代码块手工创建 事物
例如代码块:
1 using (TransactionScope scope = new TransactionScope()) 2 { 3 Operation1(); 4 scope.Complete(); 5 }
当设置为true时,如果客户端事务流向服务,那么服务操作将加入到事务中,如果客户端的事务不流动,那么为了使服务操作在其中执行,就会创建一个新的事务。
OperationBehaviorAttribute的另一项影响事务处理的属性是TransactionAutoComplete属性。
默认情况下,这个属性被设置为true。你可能偶尔想用代码来控制完成事务的过程,在这种情况下,你可以明确地设置TransactionAutoComplete为false,然后使用OperationContext的SetTransactionComplete()方法。
如下:
1 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] 2 public void ResetCounters() 3 { 4 //...... 5 6 OperationContext.Current.SetTransactionComplete(); 7 }
事务超时
事务超时设置,能够避免系统死锁。默认情况下,与服务操作相关联的事务,被设置为60秒即为超时。这个设置是通过ServiceTimeoutsBehavior控制的。你可以通过加入<serviceTimeouts>行为节点来声明设定这个配置。如下所示:
1 <serviceBehaviors> 2 <behavior name="serviceBehavior"> 3 <serviceTimeouts transactionTimeout="00:00:01"/> 4 </behavior> 5 </serviceBehaviors>
也可以通过ServiceBehaviorAttibute来控制超时设定,如下所示:
1 [ServiceBehavior(TransactionTimeout = "00:00:01")] 2 public class CountersService : ICountersService 3 { 4 5 }
当服务操作超时的时候,会抛出一个ProtocolException异常,这可能会使系统进入一个种不一致的状态。默认的60秒可以为大多数系统所接受,而对于一个较长的运行请求,使用120秒超时也是可以的,同时该系统仍然保持平均接近60秒的设定来处理每个请求。
看完了服务端的配置,我们再看下客户端的配置:
客户端配置:
客户端想要向服务传送事务时,需要满足以下要求:
1:事务流必须为与客户端终端配置相关的绑定启用。当你生成一个代理,如果服务需要事务(TransactionFlowOption.Mandatory),那么客户端绑定将被配置为启用事务流。
2:事务必须出现在客户端调用服务的线程中。这通常是在执行代理之前,通过在调用链接的某处创建TransactionScope块来实现的。
3:被调用的操作必须被配置,以允许客户端服务流向服务。实质上,在生成代理和配置文件之后,客户端唯一需要考虑的是决定它是否想作为事务的一部分来调用服务,创建TransactionScope,并在没有启用的情况下启用流。
事务和会话实例上下文模式:
当在服务中启用会话时(InstanceContextMode.PerSession),你希望用同一个服务实例处理客户端代理生命周期内所有的请求。通过这种方法,服务实例可以管理你的应用程序所请求的会话状态。当事务被引入这个公式时,尽管在代理期间内,维护的是同一个会话标识,但服务实例是在事务中涉及的每个操作完成之后发布的。
如果直到事务完成的时候才发布服务实例,效果可能会更好一些。这样,服务实例及任何正在被管理的状态都可以参加到事务之中。
设置TransactionAutoCompleteOnSessionClose为true。
1 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, TransactionAutoCompleteOnSessionClose = true)] 2 public class CountersService : ICountersService 3 { 4 5 }
当然,为了请求事务的那些操作,你还必须设置OperationBehaviorAttribute的TransactionAutoComplete属性为false。
1 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] 2 public void SetCounter1(int counterValue) 3 { 4 5 } 6 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] 7 public void SetCounter2(int counterValue) 8 { 9 10 }