博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
重入与回调并发(Reentrant & CallbackConcurrency )
阅读量:6243 次
发布时间:2019-06-22

本文共 8622 字,大约阅读时间需要 28 分钟。

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(
1
2);
                                                     }
                                                 });
            }
        }
注意:如果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对回调服务的并发设置无效。

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

 

转载地址:http://hbpia.baihongyu.com/

你可能感兴趣的文章
JavaScript语法详解(四)
查看>>
Fail to queue the whole FAL gap in dataguard一例
查看>>
03在Windows Server 2008R2上面建立子域
查看>>
网络系统组成、OSI模型、TCP/IP协议簇
查看>>
服务器无法远程
查看>>
目前发现Exchange 2016的两个管理问题
查看>>
java发送邮件问题
查看>>
myeclipse2013 安装 egit
查看>>
介绍几种常见的网站负载均衡技术
查看>>
httpd详解
查看>>
jquery获取复选框的值
查看>>
深入理解C语言的define
查看>>
安装Discuz
查看>>
zabbix问题集锦
查看>>
MYSQL EXPLAIN 中的KEY_LEN的说明
查看>>
Linux笔记(VIM)
查看>>
pyrhon脚本小练习(9*9乘法表)
查看>>
Python按行读取文件
查看>>
Linux Shell从一个文件去掉包含在另一个文件的内容
查看>>
Linux CentOS6.5下编译安装MySQL 5.6.16
查看>>