• 重入与回调并发(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对回调服务的并发设置无效。

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

  • 相关阅读:
    BSGS模板(互质与不互质) 洛谷P4195、P3846
    整除分块(数论分块)
    洛谷P3327 [SDOI2015]约数个数和——莫比乌斯反演
    模块二
    模块
    二分法 匿名函数
    3.26作业
    函数的递归调用
    yield表达式 三元表达式
    3.25作业
  • 原文地址:https://www.cnblogs.com/tyb1222/p/2517318.html
Copyright © 2020-2023  润新知