对于下面几种方式,哪一种是最合适的,我期望的是这样的:

1) 在客户端,对于TCP确保使用连接池,在每一次使用连接之后归还连接等待复用。由于连接池是宝贵的资源,不可能每一次调用都去创建,而是只创建一次。

2) 在服务端,调用完毕之后可以尽快释放服务实例(设置的是  [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)])

3) 对于多线程环境,是线程安全的并且也是高性能的,可以利用多线程环境提高速度的。

我想了一下有这些写法,每一种测试分别用TCP和HTTP信道运行一遍:

我分别测试了:

1) 用ClientBase的方式(也就是添加服务引用后生成的ServiceClient的方式)

2) 用ChannelFactory方式(直接复用服务契约的方式)

3) 是否主动开关的区别(主动开关是否会影响性能?是否必要?是否会影响连接的复用?)

4) 是否共享的区别(共享后是否有性能提高?是否线程安全?)

Parallel.For(0, it, (i, loopState) =>
{
     using (ServiceClient client = new ServiceClient(s))
     {
         try
         {

if (client.Add(1, 2) != 3) throw new Exception("error");
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }

}

});

Console.WriteLine(s + " 每次创建 Client:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

2)

Parallel.For(0, it, (i, loopState) =>
{
     using (ServiceClient client = new ServiceClient(s))
     {
         client.Open();
         try
         {

if (client.Add(1, 2) != 3) throw new Exception("error");
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }
         client.Close();
     }

});

Console.WriteLine(s + " 每次创建 Client (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

3)

using (ServiceClient client = new ServiceClient(s))
{
     client.Open();
     Parallel.For(0, it, (i, loopState) =>
     {

try
         {
             if (client.Add(1, 2) != 3) throw new Exception("error");
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }
     });
     client.Close();
}
Console.WriteLine(s + " 共享 Client:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

4)

using (ServiceClient client = new ServiceClient(s))
{
     client.Open();
     Parallel.For(0, it, (i, loopState) =>
     {

try
         {
             if (client.Add(1, 2) != 3) throw new Exception("error");
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }
     });

client.Close();
}

Console.WriteLine(s + " 共享 Client (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

5)

Parallel.For(0, it, (i, loopState) =>
  {
      using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
      {
          try
          {

var client = cf.CreateChannel();

if (client.Add(1, 2) != 3) throw new Exception("error");
              (client as ICommunicationObject).Close();
          }
          catch (Exception ex)
          {
              Console.WriteLine(ex.ToString());
              loopState.Break();
          }
      }

});

Console.WriteLine(s + " 每次创建 ChannelFactory:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

6)

Parallel.For(0, it, (i, loopState) =>
{
     using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
     {
         cf.Open();
         try
         {

var client = cf.CreateChannel();

if (client.Add(1, 2) != 3) throw new Exception("error");
             (client as ICommunicationObject).Close();
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }

cf.Close();
     }
    
});

Console.WriteLine(s + " 每次创建 ChannelFactory (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

7)

using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
{
     Parallel.For(0, it, (i, loopState) =>
     {
         try
         {

var client = cf.CreateChannel();

if (client.Add(1, 2) != 3) throw new Exception("error");
             (client as ICommunicationObject).Close();
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }
     });

}

Console.WriteLine(s + " 共享 ChannelFactory:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

8)

using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
{
     cf.Open();
     Parallel.For(0, it, (i, loopState) =>
     {
         try
         {

var client = cf.CreateChannel();

if (client.Add(1, 2) != 3) throw new Exception("error");
             (client as ICommunicationObject).Close();
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
             loopState.Break();
         }
     });
     cf.Close();
}

Console.WriteLine(s + " 共享 ChannelFactory (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);

测试结果如下(Y坐标是每秒操作执行成功数):

1) HTTP总体性能比TCP差不少,但是不管哪种方式都差不多,我猜测差不多的原因是因为HTTP是短连接而TCP是长连接的关系。

2) 按我的想法,每次创建ChannelFactory性能是最差的,可能是每一次都在使用新的连接池,事实也是这样,但是也没有差太多

3) 我本来以为共享ChannelFactory和每次创建Client的方式性能差不多的(因为ClientBase会缓存ChannelFactory),但是差距还是挺大的

4) 共享Client方式能有这么高的性能让人意外,我本来以为会和每次创建Client一样,不知这样用会不会有什么问题

官方的文档中虽然是每次创建Client或ChannelFactory后及时关闭的,但他那个都是控制台程序其实怎么做都不重要,对于ASP.NET多线程环境中的使用究竟哪种最靠谱?

我个人觉得两种方式比较靠谱的(没看到官方有相关的最佳实践的说法,自己猜的):

1) 如果使用ClientBase(ServiceClient)方式的,每一次使用都创建,使用后及时关闭

2) 如果使用ChannelFactory方式的,共享方式使用,但是使用后及时关闭Create出来的Channel

(反正共享方式即使性能高我是不推荐的,可能会有很多潜在的问题)

反正不管怎么样,测试结论还是和自己想的不太一样,请大家讨论。

后记:

http://blogs.msdn.com/b/wenlong/archive/2007/10/26/best-practice-always-open-wcf-client-proxy-explicitly-when-it-is-shared.aspx

在此文中,作者的观点是如果希望共享,对于Client方式和ChannelFactory都要显式Open(),否则会在第一次调用后Open,那么一开始的时候可因为排队导致性能变差。

http://blogs.msdn.com/b/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx

在此文中,作者的观点是:

1) 创建ChannelFactory(ClientBase内部也是创建ChannelFactory)是昂贵的,因为需要构建ContractDescription树、反射所有需要的CLR类型、构建信道栈,在.NET 3.5中进行了以下改进:A 在ClientBase内部缓存了ChannelFactory B 改善了信道管理的逻辑,因此不管是使用ClientBase还是直接使用ChannelFactory,性能都有改善

2) ClientBase对ChannelFactory的缓存采用的是MRU,大小是32。只要InstanceContext、endpointConfiguratioName以及remoteAddress参数匹配上就可以用到缓存。如果不想用到缓存,可以在Open ClientBase之前访问ChannelFactory、Endpoint或是ClientCredentials属性。

3) 最佳实践是 A 重用ServiceClient能获得最好的性能,但别忘记先Open B 每次都使用新的ServiceClient依靠缓存的话性能也不错 C 共享的话要注意多线程问题

http://blogs.msdn.com/b/wenlong/archive/2007/11/14/a-sample-for-wcf-client-proxy-pooling.aspx

作者在此文中给出了Client Proxy Pool的实现,他提到:

1) 通过客户的反馈我意识到直接重用代理不是理想的,因为可能多线程会出现竞争问题,以及关联的上下文可能会混乱。

2) 可能使用池的做法更合理,但是需要实现相关的同步、异常处理、无可用对象时新建、池大小可配置等逻辑。

作者给出了自己的实现,另外我也找到了基于池的一个更完善的动态代理的实现:http://wcfdynamicclient.codeplex.com/

究竟应该怎么调用WCF服务?相关推荐

  1. SharePoint 2013 调用WCF服务简单示例

    内容比较简单,主要记录自己使用SharePoint 2013WCF服务遇到的小问题和小经验,分享给大家,希望能够给需要的人有所帮助.好吧,进入正题! 第一部分 SharePoint 2013调用自带W ...

  2. 实现在GET请求下调用WCF服务时传递对象(复合类型)参数

    WCF实现RESETFUL架构很容易,说白了,就是使WCF能够响应HTTP请求并返回所需的资源,如果有人不知道如何实现WCF支持HTTP请求的,可参见我之前的文章<实现jquery.ajax及原 ...

  3. 实现jquery.ajax及原生的XMLHttpRequest调用WCF服务的方法

    废话不多说,直接讲解实现步骤 一.首先我们需定义支持WEB HTTP方法调用的WCF服务契约及实现服务契约类(重点关注各attribute),代码如下: //IAddService.cs namesp ...

  4. WCF 入门之旅(4): 怎样用客户端调用WCF服务

    WCF 入门之旅(4): 怎样用客户端调用WCF服务 前面的demo已经能够跑起来,现在开始考虑用客户端来测试所写的应用了,首先用个普通的应用程序来调用所写的wcf服务吧.其实最后运行wcf服务后的页 ...

  5. Silverlight同步(Synchro“.NET研究”nous)调用WCF服务

    Silverlight的RIA应用中访问远端的WebService或WCF服务,都是通过异步线程模式调用的.在某些情况下我们的调用是需要同步进行,虽然Silverlight没有内置同步线程模式调用远端 ...

  6. 学习 WCF (6)--学习调用WCF服务的各种方法

    根据不同的情况,我们可以用不同的方法调用WCF服务,本文简单总结了一下调用WCF的一些方法(代理类,Ajax...),分享给大家.开发工具调用WCF 这中方法很方便也很简单,很多工作VS就帮我们完成了 ...

  7. WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

    原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...

  8. ajax内调用WCF服务

    WCF可以当作WebService一样被调用,在html内通过ajax调用WCF服务的方法如下: 1.新建一个WCF服务的网站项目: 2.在项目内增加一个新项:启用了ajax的WCF服务: 3.在对应 ...

  9. 转载——Java与WCF交互(一):Java客户端调用WCF服务

    最近开始了解WCF,写了个最简单的Helloworld,想通过java客户端实现通信.没想到以我的基础,居然花了整整两天(当然是工作以外的时间,呵呵),整个过程大费周折,特写下此文,以供有需要的朋友参 ...

最新文章

  1. Python内置函数eval
  2. VS Code无法远程连接服务器的解决办法
  3. matlab中ahp方法,AHP及matlab程序.doc
  4. “手把手教你学linux驱动开发”OK6410系列之02---虚拟字符设备
  5. 值得思考:过去的中国大学
  6. 电工知识:常用电子元件名称及其对应图片实用大全
  7. 小学生c语言入门教程,小学生都看得懂的C语言入门(5): 指针
  8. Flux、Mono、Reactor 实战(史上最全)
  9. 2018上半年最火的微信公众号有哪些?
  10. 【学习小结】树上的概率、期望问题,树上高斯消元
  11. 世界上最好的加密软件
  12. 【转】MIT牛人解说数学体系
  13. Taro 和 uni-app选型对比
  14. JavaScript基础(4)_强制类型转换
  15. Linux 浏览器无法输入中文解决办法
  16. n的阶乘怎么用计算机语言表示,python中阶乘的表示方法
  17. 阻止事件冒泡,阻止默认事件,event.stopPropagation()和event.preventDefault(),return false的区别
  18. [python爬虫之路day5]:实战之电影天堂2019精选电影爬取
  19. linux clamav命令,linux下ClamAV使用
  20. DNS解析:腾讯云转战cloudfare解析记录

热门文章

  1. oracle按照时间过滤
  2. JQuery 自己主动触发事件
  3. Mac OS X 常用快捷键一览
  4. IIS支持http协议的:put、delete等方法
  5. NYOJ题目1048破门锁
  6. [刷题]Counting Bits
  7. android: SharedPreferences存储
  8. 关于Python中的yield(转载)
  9. BZOJ1503 [NOI2004]郁闷的出纳员
  10. LeetCode 56. Merge Intervals