介尘部落

文学|音乐|休闲娱乐|计算机技术|地球科学|社会学——知识成就命运


WCF消息交换模式(MEP)-回调操作

回调操作:是服务契约的一部分,取决于服务契约对回调契约的定义。一个服务契约最多只能包含一个回调契约,一旦定义了回调契约,就需要客户端支持回调,并在每次调用中提供指向服务的回调终结点,可以通过ServiceContract特性提供的Type类型的属性CallbackContract来定义回调契约。为了托管一个回调对象,客户端需要实例化回调对象,然后通过它创建一个上下文对象。

在客户端与服务端通讯过程中,只要服务终结点的契约定义了一个回调契约,客户端都必须使用代理创建双向通讯,并将回调终结点的引用传递给服务。只要客户端正在等待回调,就不能关闭代理,如果关闭回调终结点,当服务试图将调用返回时,就会导致服务端产生错误。因此,可以由客户端自身实现回调契约,把代理定义为成员变量,当释放客户端时关闭代理。

回调契约只能在支持双向通讯的端点上暴露,并非所有的绑定都支持回调操作,只有具有双向能力的绑定才能够用于回调:

A:NetTcpBinding支持双向绑定(Duplex Binding);

B:NamedPipeBinding支持双向绑定(Duplex Binding);

C:WSDualHttpBinding提供了两个信道以支持双向通讯(它实际上设置了两个HTTP通道:1个是从客户端到服务的调用,另一个则用于服务到客户端的调用);

1、混合双向通讯;

2、两个OneWay WSHttpBinding信道;

因HTTP本质上是与连接无关的,所以它不能用于回调。因此,我们不能基于BasicHttpBinding或WSHttpBinding绑定使用回调。

在回调过程中,并发管理、同步访问与死锁是重点考虑的问题。在默认情况下,服务类被配置为单线程访问:服务实例与锁关联,在任何时间都只能有一个线程拥有锁,也只能有一个线程能够访问服务实例。在操作调用期间,向客户端发出的调用需要阻塞服务线程,并调用回调。然而一旦回调要返回它所需要的同一个锁的所有权,则处理从客户端返回的应答消息就会导致死锁。指当回调的应答消息也需要获得与服务实例关联的相同的锁时,就会导致死锁。因为此时服务线程已经被阻塞,服务操作正在等待回调操作执行完毕,而回调操作却又大等待服务释放锁,自然会产生锁的争用。这属于正在调用的会导致死锁的客户端的回调。

如果单线程的服务实例试图将调用返回给它的客户端,为了避免免死锁,WCF会抛出一个InvalidOperationException异常。有三种可能解决方案:

第一种方案:配置服务允许多线程访问,由于它与锁无关,因此允许回调。但是,也可能会增加服务开发者的负担,因为它需要为服务提供同步。

第二种方案:将服务配置为重入(Reentrancy),指将服务的并发行为配置为重入([ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)])。一旦配置为重入,则服务实例仍然与锁关联,同时只允许单线程访问,然而,如果服务正在回调它的客户端,WCF就会首先释放锁。

重入(Reentrancy):指对同步域拥有独占访问权的线程A调用了同步域之外对象的方法,此时另外的线程B若要访问该同步域,则线程A将释放对同步域的锁,允许线程B进入,直到线程B执行完毕并释放对同步域的锁后,线程A将重新进入该同步域。配置回调为重入时,因为服务对象是与线程关联的,属于同步域的对象,而回调对象则属性同步域之外的对象。由于服务被配置为重入,则服务调用回调引用时会释放锁,然后回调返回给客户端,控制权则返回给服务,服务会重入并重新获取锁。

第三种方案:将回调契约操作配置为单向操作,这样服务就能够安全地将调用返回给客户端。因为没有任何应答消息会竞用锁,即使并发被设置为单线程,服务也能够支持回调。

与ChannelFactory<T>类相似,WCF同样提供了DuplexChannelFactory<T>类,它被用于通过编程方式设置双向代理,与ChannelFactory<T>不同的是,它的构造函数既能接收回调实例,又能接收回调上下文。

下面通过一个DEMO来介绍重入和单向操作的回调契约:

主要代码如下:

契约接口代码:

[ServiceContract(Namespace = "http://schemas.xinhaijulan.com/demos/Duplex", CallbackContract = typeof(IMyContractCallback))]
    public interface IMyContract
    {
        [OperationContract(IsOneWay = true)]
        //[OperationContract]
        void HelloWCF(string msg);
    }

    public interface IMyContractCallback
    {
        [OperationContract(IsOneWay = true)]
        //[OperationContract]
        void HelloWCFCallback(string msg);
    }

契约实现类代码:

    //[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
    public class MyContract : IMyContract
    {
        public void HelloWCF(string msg)
        {
            Console.WriteLine("Message from client:" + msg);
            IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>();
            callback.HelloWCFCallback("This is the server message.");
            System.Threading.Thread.Sleep(3000);
        }
    }

服务端代码:

        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(ServiceLibrary.MyContract)))
            {
                host.AddServiceEndpoint(typeof(ServiceLibrary.IMyContract), new NetTcpBinding(), "net.tcp://localhost:9001/MyContract");
                host.Open();

                Console.WriteLine("Please input exit to close host.");
                string key = Console.ReadLine();
                while (key.ToLower() != "exit")
                {
                    Console.WriteLine("Please input exit to close host.");
                    key = Console.ReadLine();
                }
            }
        }

回调接口实现类代码处于客户端

阅读全文
公众号-介尘阅读时光
赞赏支持
,发布于 2015-03-29 22:25

0 Responses to “WCF消息交换模式(MEP)-回调操作”

Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

×