重入与回调并发(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秒
服务端输出:
 
客户端输出:
 
 

测试2:

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

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

 

客户端输出:

 

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

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

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

 

客户端输出:

 

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

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

 客户端:ConcurrencyMode = ConcurrencyMode.Single

服务端输出:

 

客户端输出:
 
测试结果和测试3一样。
 
测试5:

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

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

 

客户端输出:

 

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

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

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

 

客户端输出: 

 

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

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

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

 

客户端输出: 

 

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

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

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

 

客户端输出:

 

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

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

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

 

客户端输出:

 

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

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

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

 

客户端输出:

 

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

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

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

客户端输出:

 

 
 
测试12会话模式:

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

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

 

客户端输出:

 

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

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

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

 

客户端输出:
测试14会话模式:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

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

 

客户端输出:
测试15:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

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

 

客户端输出: 

 

测试16:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall

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

 

客户端输出:

测试17:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single

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

服务端输出:

 

客户端输出:

 

测试18:

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

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

服务端输出:

 

客户端输出:

 

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

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

 
分类: WCF基础
原文地址:https://www.cnblogs.com/Leo_wl/p/2517575.html