重入与回调并发(Reentrant & CallbackConcurrency )

WCF中,并发是一个很影响服务性能的重要方面。通过ServiceBehaviorAttribute中的ConcurrencyMode可以设置服务的并发性。

对于双工通讯来说,服务对客户端的回调也是通过代理完成的。那么这又涉及到另外一个问题:回调客户端时,能否让回调服务也并发执行呢?WCF中定义了CallbackBehaviorAttribute ,可以通过它来设置回调服务的行为。它同样定义了ConcurrencyMode,可指定回调的并发模式,但它没有定义回调的实例模式,即InstanceContextMode。本文主要探讨服务的并发与回调服务的并发。

目录:

 

  1. 测试重入与回调并发 
  2. 会话对 重入与回调并发 
  3. 设置服务并发的ServiceBehaviorAttribute能用于回调服务吗?
通过实例进行说明:

 

契约定义:
[ServiceContract(CallbackContract =  typeof (IAddCallback))]   

publicinterface IAdd
{
    [OperationContract]
    void Add(int x, int y);
}

回调契约定义:
     public  interface  IAddCallback  

    {
        [OperationContract]
        void ShowResult(int result);
    }

服务实现:
     public  class  AddService : IAdd 

    {
        private readonly int counter;

        public AddService()
        {
            counter++;
            Console.ResetColor();
            Console.WriteLine(string.Format("AddService Construction function excute... counter is {0}", counter));
        }

        #region IAdd 成员

        public void Add(int x, int y)
        {
            var clientId = OperationContext.Current.IncomingMessageHeaders.GetHeader<int>(MessageWrapper.headerClientId,
                                                                                          MessageWrapper.headerNamespace);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(string.Format("Time:{0};ThreadId is :{1}.Request Id is {2} Add Method Invoked,", DateTime.Now,
                                            Thread.CurrentThread.ManagedThreadId, clientId));
            Thread.Sleep(5000);
            int result = x + y;
            MessageHeader<int> header = new MessageHeader<int>(clientId);
            System.ServiceModel.Channels.MessageHeader messageHeader =
                header.GetUntypedHeader(MessageWrapper.headerClientId,
                                        MessageWrapper.headerNamespace);
            OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
            Console.WriteLine(string.Format("Time: {1} Requst Id {0} begin Callback",clientId,DateTime.Now));
            IAddCallback callbackProxy= OperationContext.Current.GetCallbackChannel<IAddCallback>();
            callbackProxy.ShowResult(result);
            Console.WriteLine(string.Format("Time: {1} Requst Id {0} Callback finished", clientId, DateTime.Now));
            Console.WriteLine(string.Format("result is : {0}",result));
            Thread.Sleep(5000);
            Console.WriteLine("=========Excute finished=========");
            Console.WriteLine();
        }

        #endregion
    }

服务配置:
    
    <system.serviceModel>

<services>
            <service name="Service.AddService">
                <endpoint address="http://127.0.0.1:3636/AddService" binding="wsDualHttpBinding" contract="Contract.IAdd" ></endpoint>
            </service>
        </services>
    </system.serviceModel>

客户端调用:
     public  class AddCallback : IAddCallback
    {
         private  readonly  int counter;
         public AddCallback()
        {
            counter++;
            Console.WriteLine( string.Format( " AddCallback Construction function excute... counter is {0} ", counter));
        }

         #region IAddCallback 成员

         public  void ShowResult( int result)
        {            
             int callbackRequestId= OperationContext.Current.IncomingMessageHeaders.GetHeader< int>(MessageWrapper.headerClientId,
                                                                           MessageWrapper.headerNamespace);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine( string.Format( " Time: {1} ThreadId is :{2} Callback RequestId : {0} 开始执行回调 ", callbackRequestId, DateTime.Now,Thread.CurrentThread.ManagedThreadId));
            Thread.Sleep( 10000);
            Console.WriteLine( string.Format( " Add result is {0} ", result));
            Console.WriteLine( string.Format( " Time: {1} ThreadId is :{2} Callback RequestId : {0} 回调结束 ", callbackRequestId, DateTime.Now, Thread.CurrentThread.ManagedThreadId));
            Console.WriteLine( " ================== ");
            Console.WriteLine();
        }
#endregion 
       }   

回调服务实现:
static  void InvokeWithDiffrentProxy()
        {
            InstanceContext instanceContext= new InstanceContext( new AddCallback());
            DuplexChannelFactory<IAdd> factory =  new DuplexChannelFactory<IAdd>(instanceContext, " AddService ");
             for ( int i =  0; i <  2; i++)
            {
                IAdd proxy = factory.CreateChannel();
                ThreadPool.QueueUserWorkItem( delegate
                                                 {
                                                      int clientId = Interlocked.Increment( ref index);
                                                      using (
                                                         OperationContextScope contextScope =
                                                              new OperationContextScope(proxy  as IContextChannel))
                                                     {
                                                         MessageHeader< int> header =  new MessageHeader< int>(clientId);
                                                         System.ServiceModel.Channels.MessageHeader messageHeader =
                                                             header.GetUntypedHeader(MessageWrapper.headerClientId,
                                                                                     MessageWrapper.headerNamespace);
                                                         OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
                                                         proxy.Add( 12);
                                                     }
                                                 });
            }
        }
注意:如果XP下,使用wsDualHttpBinding作为绑定协议,需要对绑定做一下设置:
<bindings>
    <wsDualHttpBinding>
        <bind name= " wsDuplexBinding ">
            <clientBaseAddress  address= " http://127.0.0.1:6300/addCallbackService ">
        <bind>
        <wsDualHttpBinding>
</bindings>
下面做几个测试,观察一下服务端以及回调客户端的执行。

 

测试1:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

客户端:ConcurrencyMode = ConcurrencyMode.Single
回调服务休眠10秒
服务端输出:
重入与回调并发(Reentrant & CallbackConcurrency ) 
客户端输出:
重入与回调并发(Reentrant & CallbackConcurrency ) 
 

测试2:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

客户端:ConcurrencyMode = ConcurrencyMode.Single
回调服务休眠1秒
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过测试1、2的一组图的输出可知: Reentrant + Single 模式下:服务一次只能执行一个服务,当服务执行回调时,请求队列中的下一个请求将被服务执行。在回调服务的单实例模式下:回调也需要排队等待被处理。测试2的一组图显示:调用1的回调在时间为11:25:10的时候回调已经完成,但是回到服务端时,另外一个服务正占用锁,它只能等待。 这说明:当回调服务完成,回调客户端时,需要等待服务正在被处理的请求释放锁后才能被继续执行。
测试3:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Single
服务端输出:

 重入与回调并发(Reentrant & CallbackConcurrency )

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过上图分析知:服务对调用的请求通过并发的方式执行,而服务对客户端的回调还是串行的方式。
测试4:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

 客户端:ConcurrencyMode = ConcurrencyMode.Single

服务端输出:

 重入与回调并发(Reentrant & CallbackConcurrency )

客户端输出:
重入与回调并发(Reentrant & CallbackConcurrency ) 
测试结果和测试3一样。
测试5:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

客户端:ConcurrencyMode = ConcurrencyMode.Multiple
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过上图分析知:服务对调用的请求通过串行的方式执行,而服务对客户端的回调是并行的方式。
测试6:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Multiple
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出: 

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过上图分析知:服务对调用的请求以及服务对客户端的回调是并行的方式。
测试7:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Multiple
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出: 

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过上图分析知:服务对调用的请求以及服务对客户端的回调是并行的方式。
测试8会话模式

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Single
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过上图分析知:服务对调用的请求并发进行处理,服务对客户端的回调以串行的方式处理。
测试9会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Multiple
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

服务对调用的请求以及服务对客户端的回调是并行的方式。
如果将ServiceBehaviorAttribute用于回调类型,结果会怎样呢?
测试10会话模式

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

通过上图分析知:服务对调用的请求并发进行处理,服务对客户端的回调以并行的方式处理。
测试11会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerCall)
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency )

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

测试12会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerSession)
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

测试10、测试11、测试12分析知:服务对调用的请求并发进行处理;服务对客户端的回调以串行的方式处理,这点可以从回调的构造,以及打印出来的时间两方面得到验证。
对回调使用ServiceBehaviorAttribute无效。
测试13会话模式:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

客户端:ConcurrencyMode = ConcurrencyMode.Single
同一客户端并发调用服务,将调用代码做如下更改:
(proxy as ICommunicationObject).Open();
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:
重入与回调并发(Reentrant & CallbackConcurrency )
测试14会话模式:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

ConcurrencyMode = ConcurrencyMode.Multiple
同一客户端并发调用服务,将调用代码做如下更改:
(proxy as ICommunicationObject).Open();
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:
重入与回调并发(Reentrant & CallbackConcurrency )
测试15:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

ConcurrencyMode = ConcurrencyMode.Multiple
同一客户端并发调用服务,将调用代码做如下更改:
(proxy as ICommunicationObject).Open();
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出: 

 重入与回调并发(Reentrant & CallbackConcurrency )

测试16:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

ConcurrencyMode = ConcurrencyMode.Multiple
同一客户端并发调用服务,将调用代码做如下更改:
(proxy as ICommunicationObject).Open();
服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

重入与回调并发(Reentrant & CallbackConcurrency )
测试17:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

ConcurrencyMode = ConcurrencyMode.Multiple
同一客户端并发调用服务,将调用代码做如下更改:
(proxy as ICommunicationObject).Open();

服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

 重入与回调并发(Reentrant & CallbackConcurrency )

 

测试18:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerCall) 

服务端输出:

重入与回调并发(Reentrant & CallbackConcurrency ) 

客户端输出:

 重入与回调并发(Reentrant & CallbackConcurrency )

 

  由测试10、11、12、18这些测试得出如下结论:ServiceBehaviorAttribute对回调服务的并发设置无效。

 会话模式下,不同客户端代理对可重入的服务总是以并发的方式执行;相同客户端对可重入的服务总是以串行的方式执行,而无论怎样,回调服务总是以串行的方式执行。

 

你可能感兴趣的:(concurrency)