WCF热门问题编程示例(2)多个实例调用一个WCF服务操作,需要等待服务响应吗

今天是《WCF热门问题编程示例》系列的第二个问题。WCF热门问题编程示例(2)多个实例调用一个WCF服务操作,需要等待服务响应吗。原文在MSDN中文论坛出现过,一直没有人解答,当时楼主的实验方式式两个客户端掉一个服务操作,结果出现等待问题,他当时的疑惑是不是WCF在相应客户端请求的时候需要等待排队。WCF服务响应客户端请求时是否是依次响应的。今天我做了实验,写了测试代码,和大家分享一下具体的心得体会。也好给大家一个参考。

下面我们来进入详细的问题讨论,首先是问题的来源。

【1】问题来源:

    这个问题来自于MSDN中文技术论坛,闲置很久,yangjian15 提出的“WCF一个疑惑?,原帖地址:

http://social.microsoft.com/Forums/zh-CN/wcfzhchs/thread/c990c287-c422-4550-9501-c04395d9ea15

 内容是:建立如下wcf服务 ,采用tcpbinding:

      契约:

   [ServiceContract ]
   public interface Iservice
    {
       [OperationContract ]
       string getstr(string str);

    }

    实现:

[ServiceBehavior (InstanceContextMode =InstanceContextMode.PerCall  )]
    public class servicetest:Iservice
    {
        
        public string getstr(string str)
        {
            Thread.Sleep(3000);
            return   str;
        }
      
    }

用两个客户端同时访问getstr, 结果是一个客户端3秒返回 ,一个客户端6秒 。测试过是产生的两个实例,但这两个实例好像运行在一个线程上。如果是这样,wcf的性能就有很大问题。

   各位大师帮我看看 这是什么原因 ?怎么让两个客户端同时返回,即这两个实例运行在不同线程上??

【2】资料收集:

     我在网上查看了一下资料,其实也没搜索到这个问题特定的讨论或者文章。其实分析这个问题,更好应该参考服务激活类型之类的文章。基本的资料就是:WCF分布式开发步步为赢(9):WCF服务实例激活类型编程与开发 和《WCF服务编程》里关于激活的章节。大家也可以搜索相关的技术文章。作为参考。这个问题直接很难找到准确的的答案,需要从服务实例的类型入手。

【3】示例代码:

    我们这里的测试代码涵盖了3中类型的服务。提问帖子讨论的是默认的VS2008创建的服务,这个WCF服务如果不指定类型的话,会使用会话服务。我们这里为了测试的准确和严密,分别对单调服务(Call Service),会话服务(Sessionful Service),单例服务(Singleton Service)坐了测试,每次操作延迟5秒,模拟服务处理费时。下面分别给出代码:

【3.1】服务端:

    这里我们分别配置了单调服务(Call Service),会话服务(Sessionful Service),单例服务(Singleton Service)三种不同激活类型的服务。具体代码如下:

  // 1.服务契约
    [ServiceContract(SessionMode = SessionMode.Allowed, Namespace  =   " http://www.cnblogs.com/frank_xl/ " )]
    
public   interface  IWCFService
    {
        
// 操作契约
        [OperationContract]
        
void  SayHello();
    }
    
// 2.服务类.单调服务
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    
public   class  WCFServicePerCall : IWCFService,IDisposable
    {
        
// 服务实例计数
         private  Guid ServiceGUID;
        
// 构造函数
         public  WCFServicePerCall()
        {
            ServiceGUID 
=  Guid.NewGuid();
            Console.WriteLine(
" WCFServicePerCall{0} is Created at: "   +  DateTime.Now.ToLongTimeString(),ServiceGUID);
        }
        
// 实现接口定义的方法
         public   void  SayHello()
        {
            Console.WriteLine(
"  WCFServicePerCall SayHello at:{0}  " , DateTime.Now.ToLongTimeString());
            Thread.Sleep(
5000 );
        }
        
// 实现接口定义的方法Dispose
         public   void  Dispose()
        {
            Console.WriteLine(
" WCFServicePerCall{0} is disposed at: "   +  DateTime.Now.ToLongTimeString(), ServiceGUID);
        }
    }
    
// 3.服务类.会话模式
    [ServiceBehavior(InstanceContextMode  =  InstanceContextMode.PerSession)]
    
public   class  WCFServicePerSession : IWCFService, IDisposable
    {
        
// 服务实例计数
         private  Guid ServiceGUID;
        
// 构造函数
         public  WCFServicePerSession()
        {
            ServiceGUID 
=  Guid.NewGuid();
            Console.WriteLine(
" WCFServicePerSession{0} is Created at: "   +  DateTime.Now.ToLongTimeString(), ServiceGUID);
        }
        
// 实现接口定义的方法
         public   void  SayHello()
        {
            Console.WriteLine(
"  WCFServicePerSession SayHello at:{0}  " , DateTime.Now.ToLongTimeString());
            Thread.Sleep(
5000 );
        }
        
// 实现接口定义的方法Dispose
         public   void  Dispose()
        {
            Console.WriteLine(
" WCFServicePerSession{0} is disposed at: "   +  DateTime.Now.ToLongTimeString(), ServiceGUID);
        }
    }
    
// 4.服务类.单件、单例模式
    [ServiceBehavior(InstanceContextMode  =  InstanceContextMode.Single)]
    
public   class  WCFServiceSingle : IWCFService, IDisposable
    {
        
// 服务实例计数
         private  Guid ServiceGUID;
        
// 构造函数
         public  WCFServiceSingle()
        {
            ServiceGUID 
=  Guid.NewGuid();
            Console.WriteLine(
" WCFServiceSingle{0} is Created at: "   +  DateTime.Now.ToLongTimeString(), ServiceGUID);
        }
        
// 实现接口定义的方法
         public   void  SayHello()
        {
            Console.WriteLine(
"  WCFServiceSingle SayHello at:{0}  " , DateTime.Now.ToLongTimeString());
            Thread.Sleep(
5000 );
        }
        
// 实现接口定义的方法Dispose
         public   void  Dispose()
        {
            Console.WriteLine(
" WCFServiceSingle{0} is disposed at: "   +  DateTime.Now.ToLongTimeString(), ServiceGUID);
        }
    }

 【3.2】宿主:分别配置三种类型的服务,这里不在详述,基本服务的配置大家都应该了解了,如果有疑问可以参考WCF分布式开发步步为赢(2)自定义托管宿主WCF解决方案开发配置过程详解。下面是配置文件的代码:

< services >
      
< service behaviorConfiguration = " WCFService.WCFServiceBehavior "  name = " WCFService.WCFServicePerCall " >
        
< endpoint
          address
= " net.tcp://localhost:9001/WCFServicePerCall "
          binding
= " netTcpBinding "
          contract
= " WCFService.IWCFService " >
        
</ endpoint >
        
< endpoint address = " mex "  binding = " mexTcpBinding "  contract = " IMetadataExchange "   />
        
< host >
          
< baseAddresses >
            
< add baseAddress = " net.tcp://localhost:9001/ " />
          
</ baseAddresses >
        
</ host >
      
</ service >
      
< service behaviorConfiguration = " WCFService.WCFServiceBehavior "  name = " WCFService.WCFServicePerSession " >
        
< endpoint
          address
= " net.tcp://localhost:9002/WCFServicePerSession "
          binding
= " netTcpBinding "
          contract
= " WCFService.IWCFService " >
        
</ endpoint >
        
< endpoint address = " mex "  binding = " mexTcpBinding "  contract = " IMetadataExchange "   />
        
< host >
          
< baseAddresses >
            
< add baseAddress = " net.tcp://localhost:9002/ " />
          
</ baseAddresses >
        
</ host >
      
</ service >
      
< service behaviorConfiguration = " WCFService.WCFServiceBehavior "  name = " WCFService.WCFServiceSingle " >
        
< endpoint
          address
= " net.tcp://localhost:9003/WCFServiceSingle "
          binding
= " netTcpBinding "
          contract
= " WCFService.IWCFService " >
        
</ endpoint >
        
< endpoint address = " mex "  binding = " mexTcpBinding "  contract = " IMetadataExchange "   />
        
< host >
          
< baseAddresses >
            
< add baseAddress = " net.tcp://localhost:9003/ " />
          
</ baseAddresses >
        
</ host >
      
</ service >
    
</ services >

【3.3】客户端:客户端添加服务引用的方式,生成本地类和配置文件。这里的测试使用分别实例化客户端代理,一次两个客户端,同事调用单调服务、会话服务、单例服务,看看宿主的输出信息。服务Thread.sleep(5000),进程休眠5秒。来打印消息,对比结果,下面是客户端代码:

   public   void  WCFClientPerCall()
        {
                   
#region  1.单调服务代理
                    
using  (ServiceReferencePerCall.WCFServiceClient WCFServicePerCallProxy  =   new  ServiceReferencePerCall.WCFServiceClient())
                    {
                        
// 调用服务,每次服务需要5秒执行时间
                        Console.WriteLine( "  WCFServicePerCallProxy calls at:{0}  " , DateTime.Now.ToLongTimeString());
                        WCFServicePerCallProxy.SayHello();
                    }
                    
#endregion
        }
        
public   void  WCFClinetPerSession()
        {
                
#region  2.会话服务代理
                
using  (ServiceReferencePerSession.WCFServiceClient WCFServicePerSessionProxy  =   new  ServiceReferencePerSession.WCFServiceClient())
                {
                    
// 调用服务,每次服务需要5秒执行时间
                    Console.WriteLine( "  WCFServicePerSessionProxy calls at:{0}  " , DateTime.Now.ToLongTimeString());
                    WCFServicePerSessionProxy.SayHello();
                }
                
#endregion
        }
        
public   void  WCFClientSingle()
        {
            
#region  3.单例服务代理
            
using  (ServiceReferenceSingle.WCFServiceClient WCFServiceSingleProxy  =   new  ServiceReferenceSingle.WCFServiceClient())
            {
                
// 调用服务,每次服务需要5秒执行时间
                Console.WriteLine( "  WCFServiceSingleProxy calls at:{0}  " , DateTime.Now.ToLongTimeString());
                WCFServiceSingleProxy.SayHello();
            }
            
#endregion
        }

【3.4】测试结果:

    测试的结果依次是分别截图如下:

(1)单调服务(Call Service):

(2)会话服务(Sessionful Service):

(3)单例服务(Singleton Service):

【4】总结:

      以上就是全部分分析和测试代码,由于手动操作测试的客户端,开始时间有差别,不过服务时间响应后移,对比结果的时候不影响。这里调用时间都做了打印输出,大家可以再图中看出.一下几点是值得注意的地方:

     (1)WCF服务实例激活类型包括三种方式:单调服务(Call Service),会话服务(Sessionful Service),单例服务(Singleton Service). 
     (2)服务实例的默认激活方式为会话服务模式。

     (3)针对三种方式,应该说单调服务(Call Service),会话服务(Sessionful Service)不需要客户端等待,可以分别响应客户端请求。单例服务(Singleton Service).服务需要依次响应客户端的请求。

     (4)服务并发模式对客户端请求处理也有影响,当服务的并发模式设置为ConcurrencyMode.Multiple,客户端请求不会放入WCF消息队列,会直接转发给WCF服务实例处理,此时不会等待。默认的最大并发调用数字16,而且限流对此也有影响,大家处理的时候应该注意。

     (5)服务的并发模式默认的情况是ConcurrencyMode.Single.客户端的消息会被放入WCF消息队列。等待服务的处理,此时如果长时间处理请求,可能导致连接出错。      所以这个是针对原有问题的正确和全面的答案~

     最后给出代码供大家参考:/Files/frank_xl/2.WCFServiceTypeAndResponseFrankXuLei.rar欢迎大家访问MSDN中文技术论坛或者直接留言交流~

 


 

老徐的博客

【作者】:Frank Xu Lei

【地址】:http://www.cnblogs.com/frank_xl/archive/2009/06/27/1509845.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值