究竟应该怎么调用WCF服务?
对于下面几种方式,哪一种是最合适的,我期望的是这样的:
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服务?相关推荐
- SharePoint 2013 调用WCF服务简单示例
内容比较简单,主要记录自己使用SharePoint 2013WCF服务遇到的小问题和小经验,分享给大家,希望能够给需要的人有所帮助.好吧,进入正题! 第一部分 SharePoint 2013调用自带W ...
- 实现在GET请求下调用WCF服务时传递对象(复合类型)参数
WCF实现RESETFUL架构很容易,说白了,就是使WCF能够响应HTTP请求并返回所需的资源,如果有人不知道如何实现WCF支持HTTP请求的,可参见我之前的文章<实现jquery.ajax及原 ...
- 实现jquery.ajax及原生的XMLHttpRequest调用WCF服务的方法
废话不多说,直接讲解实现步骤 一.首先我们需定义支持WEB HTTP方法调用的WCF服务契约及实现服务契约类(重点关注各attribute),代码如下: //IAddService.cs namesp ...
- WCF 入门之旅(4): 怎样用客户端调用WCF服务
WCF 入门之旅(4): 怎样用客户端调用WCF服务 前面的demo已经能够跑起来,现在开始考虑用客户端来测试所写的应用了,首先用个普通的应用程序来调用所写的wcf服务吧.其实最后运行wcf服务后的页 ...
- Silverlight同步(Synchro“.NET研究”nous)调用WCF服务
Silverlight的RIA应用中访问远端的WebService或WCF服务,都是通过异步线程模式调用的.在某些情况下我们的调用是需要同步进行,虽然Silverlight没有内置同步线程模式调用远端 ...
- 学习 WCF (6)--学习调用WCF服务的各种方法
根据不同的情况,我们可以用不同的方法调用WCF服务,本文简单总结了一下调用WCF的一些方法(代理类,Ajax...),分享给大家.开发工具调用WCF 这中方法很方便也很简单,很多工作VS就帮我们完成了 ...
- WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]
原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...
- ajax内调用WCF服务
WCF可以当作WebService一样被调用,在html内通过ajax调用WCF服务的方法如下: 1.新建一个WCF服务的网站项目: 2.在项目内增加一个新项:启用了ajax的WCF服务: 3.在对应 ...
- 转载——Java与WCF交互(一):Java客户端调用WCF服务
最近开始了解WCF,写了个最简单的Helloworld,想通过java客户端实现通信.没想到以我的基础,居然花了整整两天(当然是工作以外的时间,呵呵),整个过程大费周折,特写下此文,以供有需要的朋友参 ...
最新文章
- Python内置函数eval
- VS Code无法远程连接服务器的解决办法
- matlab中ahp方法,AHP及matlab程序.doc
- “手把手教你学linux驱动开发”OK6410系列之02---虚拟字符设备
- 值得思考:过去的中国大学
- 电工知识:常用电子元件名称及其对应图片实用大全
- 小学生c语言入门教程,小学生都看得懂的C语言入门(5): 指针
- Flux、Mono、Reactor 实战(史上最全)
- 2018上半年最火的微信公众号有哪些?
- 【学习小结】树上的概率、期望问题,树上高斯消元
- 世界上最好的加密软件
- 【转】MIT牛人解说数学体系
- Taro 和 uni-app选型对比
- JavaScript基础(4)_强制类型转换
- Linux 浏览器无法输入中文解决办法
- n的阶乘怎么用计算机语言表示,python中阶乘的表示方法
- 阻止事件冒泡,阻止默认事件,event.stopPropagation()和event.preventDefault(),return false的区别
- [python爬虫之路day5]:实战之电影天堂2019精选电影爬取
- linux clamav命令,linux下ClamAV使用
- DNS解析:腾讯云转战cloudfare解析记录