zoukankan      html  css  js  c++  java
  • 事务编程与可靠会话

    事务编程与可靠会话

    WCF把书读薄(3)——数据契约、消息契约与错误契约

      真不愧是老A的书,例子多,而且也讲了不少原理方面的内容,不过越读越觉得压力山大……这次来稍微整理整理事务和可靠会话的内容。

      

      十八、事务编程

      WCF的分布式事务编程,指的是在客户端定义一个事务范围,在这个范围内对WCF服务进行连续调用,可以实现其中一个出现问题整体回滚的效果。由于WCF依赖于MSDTC,所以首先需要开启MSDTC服务才能够通过WCF进行分布式事务编程。

      这里我也自己写了一个典型的银行转账的练习,首先需要建立一个数据库,数据表只有一张Account表,其中有AccountId和Money两个int型字段,AccountId为主键。里面有两个账户:账户1有1000,账户2有1000。

      首先,既然是分布式事务,事务需要从客户端流转到服务端,那么它们之间就应该达成“共识”,也就是说需要对服务契约做手脚。下面定义一个服务契约:

    复制代码
    [ServiceContract(SessionMode = SessionMode.Required)]
    public interface IBankService
    {
        [OperationContract]
        [TransactionFlow(TransactionFlowOption.Mandatory)]
        void OutMoney(int fromAccountId, int money);
    
        [OperationContract]
        [TransactionFlow(TransactionFlowOption.Mandatory)]
        void InMoney(int toAccountId, int money);
    }
    复制代码

      在转账的整个过程当中,用户首先发送一个OutMoney请求,减少账户1当中的钱,之后发送InMoney请求,增加账户2的钱。显然这个契约需要一个会话,所以给服务契约增加SessionMode属性。注意,在实际应用当中转账应该作为一个完整的服务而不是两个服务方法,这里只是举个例子而已。

      事务的流转是一个操作行为,所以需要在操作契约上增加TransactionFlow标记,并设置TransactionFlowOption的值,这个标记是事务的总开关。其值有三个:NotAllowed(默认值,客户端事务禁止通过该方法流入服务端),Allowed(允许流入事务),Mandatory(必须在事务内调用),这里将转账操作设置为必须在事务内。

      由于事务操作必然伴随着消息交换,所以OneWay操作必然是不支持事务的,即OneWay操作的TransactionFlowOption只能为NotAllowed(下P143)!

      接下来实现这个服务操作,代码如下,其中Repository细节就不贴了:

    复制代码
    [ServiceBehavior(TransactionIsolationLevel = IsolationLevel.Serializable,
        TransactionTimeout = "00:05:00",
        TransactionAutoCompleteOnSessionClose = true)]
    public class BankService : IBankService
    {
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
        public void OutMoney(int fromAccountId, int money)
        {
            try
            {
                AccountRepository repository = new AccountRepository();
                Account accOut = repository.GetAccountById(fromAccountId);
                accOut.Money -= money;
                repository.Save(accOut);
            }
            catch (Exception ex)
            {
                System.Transactions.Transaction.Current.Rollback();
                throw new FaultException(new FaultReason(ex.Message));
            }
        }
    
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public void InMoney(int toAccountId, int money)
        {
            try
            {
                AccountRepository repository = new AccountRepository();
                Account accIn = repository.GetAccountById(toAccountId);
                accIn.Money += money;
                repository.Save(accIn);
            }
            catch (Exception ex)
            {
                System.Transactions.Transaction.Current.Rollback();
                throw new FaultException(new FaultReason(ex.Message));
            }
        }
    }
    复制代码

      服务操作的执行是否需要自动登记到事务当中,以及服务操作何时提交,是服务端自己说了算的,所以要在具体的操作上设定操作行为。这里我们用到两个行为:TransactionScopeRequired和TransactionAutoComplete,它们都是布尔值,前者用于决定操作是否纳入事务内,默认为false,这里需要设置为true,后者用于决定该操作执行完毕后是否提交事务,于是在第一个操作上设置为false,第二个操作设置为true。

      在ServiceBehavior上可以设定事务的行为,

      最后需要在事务抛异常的情况下回滚。TransactionIsolationLevel用于指定事务隔离级别,默认是Serializable,TransactionTimeout不解释,TransactionAutoCompleteOnSessionClose表示在会话正常结束时是否自动提交事务,默认为false,另外还有一个ReleaseServiceInstanceOnTransactionComplete,表示当事务完毕时是否需要释放服务实例,默认为false。

      写完了服务端代码就该改XML了,XML如下:

    复制代码
    <configuration>
      <system.serviceModel>
        <bindings>
          <ws2007HttpBinding>
            <binding name="transactionalTcpBinding"
                     transactionFlow="true" />
          </ws2007HttpBinding>
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9527/bankservice/metadata" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <services>
          <service name="Bank.Service.BankService" behaviorConfiguration="metadataBehavior" >
            <host>
              <baseAddresses>
                <add baseAddress="http://127.0.0.1:9527/"/>
              </baseAddresses>
            </host>
            <endpoint address="bankservice"
                      binding="ws2007HttpBinding"
                      bindingConfiguration="transactionalTcpBinding"
                      contract="Bank.Interface.IBankService" />
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    复制代码

      WCF是否有能力流转事务以及事务按照怎样的协议流转,是绑定控制的。在WCF当中,除了BasicHttpBinding、NetMsmqBinding和MsmqIntegrationBinding外,都是支持事务传播的(下P145),即便支持事务,事务流转也是默认关闭的,所以需要配置绑定的transactionFlow属性。这里使用了ws2007HttpBinding,并将其transactionFlow属性设置为true。

      发布服务后,在客户端使用如下代码调用服务:

    复制代码
    static void Main(string[] args)
    {
        BankServiceClient proxy = new BankServiceClient();
                
        using (TransactionScope transactionScope = new TransactionScope())
        {
            try
            {
                proxy.OutMoney(1, 100);
                proxy.InMoney(2, 100);
                transactionScope.Complete();
            }
            catch (Exception ex)
            {
                (proxy as ICommunicationObject).Abort();
            }
        }
    }
    复制代码

      这里定义了一个事务范围,并且在最后提交了事务,如果出现异常,则关闭服务代理。这样,在服务过程当中如果出现了异常,事务就会回滚了。

      十九、可靠会话

      这次真的是名副其实地把书读薄了!下册书上全是原理,嗯,这里不抄原理,只用来拷代码,不过发现书上木有太多现成可用的代码,于是就稍微总结总结好了。所谓可靠会话就是用于保证消息传递有效、有序、不重复的一套机制,WCF对这套机制的实现体现在一个叫ReliableSessionBindingElement的绑定元素上,所以要实现可靠会话,可以修改绑定,或者手写绑定。

      典型的应用就是文件分段传输,如果不实现可靠会话,分段传输就可能发生丢包、接收发送顺序不一致和重复发送的问题。

      WCF已经为我们提供了很多支持可靠会话的内置绑定,其中wsHttpBinding、wsFederationBinding、netTcpBinding的可靠会话功能是默认关闭的,wsDualHttpBinding和netNamedPipesBinding是默认开启的。

      启动可靠会话很简单,只要在绑定里加上配置:

    复制代码
    <bindings>
      <netTcpBinding>
        <binding name="reliableNetTcpBinding">
          <reliableSession enabled="true"/>
        </binding>
      </netTcpBinding>
    </bindings>
    复制代码

    并且在终结点的bindingConfiguration属性指定这个绑定配置就行了。需要注意客户端和服务端的绑定配置要一致。  

      这个绑定配置节有几个属性,参考http://msdn.microsoft.com/zh-cn/library/ms731302.aspx,可以用于做负载控制,不过无论如何,ordered要设置为true才能开启可靠会话(P255)。

      另外,如果我们需要某个服务操作必须要保证有序性才能被执行,则需要在ServiceContract接口定义上多打上一个反射标签:

    [DeliveryRequirements(RequireOrderedDelivery = true)]

    这样一来,如果没有在服务行为上配置有序,则host时会报异常。

     
     
  • 相关阅读:
    ACL2019对话、问答相关论文整理
    docker for windows添加卷映射
    聊聊多轮任务型对话那些事
    创建用户故事地图(User Story Mapping)的8个步骤
    关于如何做出好的产品
    知识体系整理
    关于如何做好需求的方法
    使用rasa搭建聊天机器人
    【转载】指代消解笔记
    计算机相关会议排名(一)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3185185.html
Copyright © 2011-2022 走看看