重入与回调并发(Reentrant & CallbackConcurrency )
WCF中,并发是一个很影响服务性能的重要方面。通过ServiceBehaviorAttribute中的ConcurrencyMode可以设置服务的并发性。
对于双工通讯来说,服务对客户端的回调也是通过代理完成的。那么这又涉及到另外一个问题:回调客户端时,能否让回调服务也并发执行呢?WCF中定义了CallbackBehaviorAttribute ,可以通过它来设置回调服务的行为。它同样定义了ConcurrencyMode,可指定回调的并发模式,但它没有定义回调的实例模式,即InstanceContextMode。本文主要探讨服务的并发与回调服务的并发。
目录:
测试重入与回调并发 会话对 重入与回调并发 设置服务并发的ServiceBehaviorAttribute能用于回调服务吗?通过实例进行说明:
契约定义:
[ServiceContract(CallbackContract = typeof (IAddCallback))]
public interface 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对回调服务的并发设置无效。
会话模式下,不同客户端代理对可重入的服务总是以并发的方式执行;相同客户端对可重入的服务总是以串行的方式执行,而无论怎样,回调服务总是以串行的方式执行。
分类: WCF基础
代码下载: https://files.cnblogs.com/tyb1222/WCFCallbackConcurrency.rar
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于重入与回调并发(Reentrant & CallbackConcurrency )的详细内容...