【引自简爱的博客】简单的来说,双向通讯是这样的一种机制,就是服务端与客户端的身份是可换的,服务示例可以回调客户端的操作,当一个服务契约被定义在服务端的时候,有时候我们的业务逻辑要求我门进行相应的回调操作。标准的服务契约定义了能够被客户端调用的服务操作。
回调契约定义了能够被服务端调用的客户端操作。因此,客户端必须具有实现回调契约的义务和宿主回调对象的能力。每当客户端调用具有回调操作的服务端示例操作的时候,客户端必须提供足够的信息以使服务端能够寻址到客户端并执行相应的回调操作。默认情况下,WCF服务被配置在单线程下,这意味着在任何服务实例的给定时间,有且仅有一个线程去处理消息,因此,当服务实例正在处理从客户端传来的消息的时候,服务端应当拒绝接受任何其他来自客户端的调用,这中机制就是枷锁。这种机制的提出,保证了正在处理的消息能够有效,安全的处理完毕。
请首先理解枷锁机制是必须而且有时候是不可避免的。但是由于我们的业务逻辑的灵活性,有时候我们却试图在我们能够控制局面的情况下,摘掉枷锁。所以本文以下的描述,就是关于这个问题的一些探讨。
如果我们试图在一个单线程的服务操作的执行中执行一个客户端的回调操作,死锁将会发生,这是WCF默认的并发模式下的枷锁机制的原因。当回调操作在客户端被调用,服务端必须等待客户端的返回值,才能执行接下来的操作,然而,这时的服务实例却因为处理当前的操作已经被加锁,所以,死锁不可避免。哈,有趣的是,如果你这样做了,WCF并不会抛出关于死锁的异常,而会抛出一个InvalidOperationEXception异常。
上边我门说过,由于我们独特的业务逻辑,我门又不得不在一个服务操作的执行中间进行客户端的回调,那得如何处理呢?我想,可以从这几方面进行尝试:
一、首先,也许大家普遍知道的是,设置ConCurrendMode的属性为Multiple,这将会开启一个多线程模式,但是我请你注意,并非一定设置如此,在默认的Single模式下也可以进行客户端的回调,不过需要做些额外的工作,后边我会简单介绍一个实例,这个示例就是工作在单线程下。当然对于初学者来说,简单设置Multiple是最好的办法,因为本身WCF程序就不好调试,更何况是双向通讯呢,我觉得没有什么比WCF的异常错误带来的挫败感更让人灰心丧气,而且通常对它无计可施。
二、设置ConCurrendMode的属性为Reentrant,这其实就是单线程下的一种工作方式,但是当你触发一个客户端的回调操作的时候,WCF将会释放那个枷锁,所以服务实例可以处理来自回调操作的消息。但是我请你务必理解上述几句话,也就是说在执行客户端回调操作的时候,服务端示例并没有进行枷锁机制。我门说过,WCF默认的情况下,是进行加锁,这是有必要的。在上边这种情况下,并没有进行枷锁会出现什么情况呢?如果一个服务示例在执行一个服务操作的中间进行客户端回调操作,服务端暂休息,等待回调操作返回消息以便进行接下来的操作。同样在这个过程中,就是在服务端间歇的时候,它可以接受任何客户端的消息,不仅仅是回调操作的消息,如果客户端又进行了一个服务调用,服务示例就会执行这个调用,这种情况下,是不能保证在回调操作前和回调操作后的数据状态以及完整性的一致性。因此,如果你使用这种方法,请确保没有其他客户端的调用或者说请确保服务端数据的安全性。
三、设置回调方法的操作属性为Isoneway。这允许WCF触发客户端回调操作而没有请求一个枷锁,这是一种强制手法,强制客户端不回传消息(当然我门必须有适合如此场景的业务逻辑才行),因此,服务端就没有必要进行枷锁。后边介绍的事例也会采用本方法。
我想通过一个示例来进行接下来的阐述,这个示例的作者是jeff.barnes,他写了大量的关于WCF的文章,更多文章请参阅http://jeffbarnes.net/portal/blogs/jeff_barnes/default.aspx,我觉得老外写的示例总是很个性而又很深刻。这个示例采用了publish-subscribe 设计模式,这个模式非常非常重要,个人觉得,很多SOA框架的核心主要是利用了两把利箭,一个就是publish-subscribe模式,另一个是事件驱动。
回到我们的示例。简单讲一下这个示例的场景。这个场景,就是帮助一个party的主人去记录客人和他们消费的beer的设计。每一个顾客(subscribes)通过参加party来订阅服务。当一些有趣的事情发生了,譬如顾客参加/离开了聚会或者消费啤酒的情况,那个服务,把这些相关信息publish那些订阅者。
本示例执行如图:
我们开始我们的设计。我定义一个服务契约,就是一个用ServiceContract修饰的接口,然后,在服务契约中定义CallbackContract = typeof(IBeerInventoryCallback),这创造了服务端和客户端的两个接口的联合。为了消费服务操作,客户端必须实现回调契约,并且宿主实现回调契约的对象,这种机制其实跟服务端没有任何区别,只是WCF为我门做了些工作,为了简化代码,在客户端我门不需要声名一个ServiceHost,然后宿主服务示例,我们仅仅将实现回调操作的事例放在一个上下文中就可以了,或者说用一个上下文去获取,譬如我门可以这样写:InstanceContext(new CallbackObject());(callbackobject是一个实现回调操作的对象)。
很多的场景,我们都会用到上下文这个概念,我想这也是很多人困惑的地方,上下文这个概念比较复杂,可以这样理解,上下文是一种逻辑线程,我们说回调实现了服务端和客户端的联合,这也就不可避免的让服务端的线程和客户端的线程联合在一起,我们的业务逻辑的执行往往需要在这两个线程中执行,出于折中考虑,WCF提出了上下文的机制,可以简单的理解为,这种机制的提出,屏蔽了服务端线程和客户端线程的差异,使得业务逻辑的执行好象是在一个线程中执行。因此,当我们在某一端,譬如服务端执行某些操作的时候可以获取客户端的一些相关对象。譬如,操作上下文,我们在编码的时候,也许用到过,OperationContext.Current.GetCallbackChannel
相关专题
- TMPGEnc中码率控制的两大类模式浅析 (8次浏览)
- 甩掉DTC,实现客户端控制事务-WCF的状态 (0次浏览)
- 简单理解WCF中的双向通讯/枷锁机制/线程并发 (0次浏览)




