wcf服务有个方法,每次调用都返回一个数据集合,肯定这个方法是不需要耗时的
客户端单线程调用这个方法时每次大概耗时100毫秒
如果采用多线程 例如5个线程 最快的一个线程需要200毫秒,最慢的一个线程大概400毫秒
几个线程的执行时长呈现递增的情况,且如果服务端返回的数据集合内容越大,
各个线程相互间的执行时间差越大,感觉好像网络传输会有阻塞(wcf使用nettcp连接)
另外还有个问题:如何提高wcf的网络传输性能呢,我现在服务跟客户端都在一台机子上
应该说传输是0毫秒的,时间估计都消耗在数据的转换上吧。关于这个试了很多方法,一直无法提高下性能,减少时间。
经过反复的测试发现当有多个线程同时调用wcf服务时,网络传输会出现阻塞
模拟场景是这样的
创建二十个线程,每个线程里都进行wcf连接的创建
然后只统计调用wcf方法的时长
分析发现
第18个线程
调用wcf方法的时间为:2015-07-23 18:14:51;910
Wcf服务端接收到的时间为:2015-07-23 18:14:52;446
这个过程用了将近500毫秒
如果是单线程,则这个时间基本可以忽略不计,为2毫秒
如果线程越多,这个时间会越长
当然我wcf服务是支持多线程访问的,且连接数也是足够的,不应该出现排队才对
不知道为什么会出现这种情况、
相关代码及执行情况(突然想了下我客户端是winform程序,难难道跟这个有关系??)
[GlobalExceptionHandlerBehaviour(typeof(GlobalExceptionHandler))] [ServiceBehavior( InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple,//多线程 UseSynchronizationContext = false //你需要评估的service的整个操作是否真的需要依赖于当前UI线程,如果不需要或者只有部分操作需要,将UseSynchronizationContext 设成false,将会提高service处理的并发量。对于依赖于当前UI线程的部分操作,可以通过SynchronizationContext实现将操作Marshal到UI线程中处理,对于这种操作,应该尽力那个缩短执行的时间 )] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]//这个地方最好是 allowed ,而不是required. public class BaseDataServ : IBaseDataServ { public List<CarTree> GetUserPowerCarTree(int userID, string sessionID) { Common.Log.DeBugLog(sessionID + "-开始获取权限车辆树-" + userID.ToString(), sessionID); System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch(); st.Start(); List<CarTree> re = new List<CarTree>(); for (int i = 0; i <= 10000; i++) { re.Add(new CarTree()); } Thread.Sleep(500); Common.Log.DeBugLog(sessionID + "-完成获取权限车辆树-" + userID.ToString() + "耗时:" + st.ElapsedMilliseconds.ToString(), sessionID); return re; } }
for (int i = 0; i < 5; i++) { listRunConfig.Add(new RunConfig { key = "", ThreadNum = i, TotalNum =1 }); } listRunConfig.ForEach(p => AsynGetUserPowerCarTree(p)); private void AsynGetUserPowerCarTree(RunConfig config) { try { var myThread = new Thread(GetUserPowerCarTree); myThread.IsBackground = true; myThread.Start(config); } catch (Exception e) { Common.Log.ErrLog("AsynGetCompany", e); } } private void GetUserPowerCarTree(object obj) { var config = (RunConfig)obj; UseService(p => p.GetUserPowerCarTree(config.Key, config.ThreadNum), string.Format("获取权限车辆树-{0}-{1}", config.Key, config.ThreadNum)); } /// <summary> /// 执行wcf服务方法 /// </summary> /// <param name="comID">公司ID 用于辨别调用哪个wcf服务</param> /// <param name="action"></param> void UseService( Action<BaseData.BaseDataServClient> action, string souce) { serv = new BaseData.BaseDataServClient("NetTcpBinding_IBaseDataServ872"); serv.Open(); System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch(); st.Start(); Common.Log.DeBugLog("获取wcf数据-开始-" + souce, string.Format("耗时{0}毫秒", st.ElapsedMilliseconds)); action(serv); Common.Log.DeBugLog("获取wcf数据-完成-" + souce, string.Format("耗时{0}毫秒", st.ElapsedMilliseconds)); serv.Close(); } }
<services> <service behaviorConfiguration="OwnTCPBehavior" name="WCF.BaseData.BaseDataServ"> <!-- address="net.tcp://127.0.0.1:872/BaseData" --> <endpoint binding="netTcpBinding" bindingConfiguration="OwnTcpBinding" contract="WCF.BaseData.IBaseDataServ" /> <endpoint binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IServer" contract="WCF.BaseData.IBaseDataServ" address=""> <identity> <dns value="localhost" /> </identity> </endpoint> <!--<endpoint binding="mexHttpBinding" contract="IMetadataExchange" address="mex"/>--> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:871/BaseData" /> <add baseAddress="net.tcp://127.0.0.1:872/BaseData" /> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="OwnTCPBehavior"> <!-- 为避免泄漏元数据信息, 请在部署前将以下值设置为 false --> <serviceMetadata httpGetEnabled="true" /> <!-- 要接收故障异常详细信息以进行调试, 请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 --> <serviceDebug includeExceptionDetailInFaults="true" /> <!-- 使用 ServiceThrottlingBehavior 类可控制各种吞吐量设置,这些设置可以让您优化服务性能,以帮助避免应用程序内存不足。 http://msdn.microsoft.com/zh-cn/library/system.servicemodel.description.servicethrottlingbehavior(v=vs.110).aspx MaxConcurrentCalls 属性可限制当前在整个 ServiceHost 中处理的消息数目。默认为处理器计数的 16 倍。 MaxConcurrentInstances 属性可限制在整个 ServiceHost 中一次执行的 InstanceContext 对象数。默认为 MaxConcurrentSessions 的值和 MaxConcurrentCalls 值的总和。 MaxConcurrentSessions 属性可限制 ServiceHost 对象可以接受的会话数。服务主机可接受的最大会话数。 默认为处理器计数的 100 倍。 因为运行时负载平衡需要运行应用程序的经验,所以,通过应用程序配置文件使用 ServiceThrottlingBehavior 是修改执行过程以获得最佳服务性能的最常用方法。 配置文件中使用 <serviceThrottling> 元素来设置此属性的值。 --> <serviceThrottling maxConcurrentCalls="100" maxConcurrentSessions="100" maxConcurrentInstances="200"/> <dataContractSerializer maxItemsInObjectGraph="6553600" /> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="NewBehavior"> <dataContractSerializer maxItemsInObjectGraph="6553600" /> </behavior> </endpointBehaviors> </behaviors> <netTcpBinding> <binding name="OwnTcpBinding" closeTimeout="00:01:03" openTimeout="00:01:04" receiveTimeout="00:05:01" sendTimeout="00:05:02" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxBufferSize="6553600" maxReceivedMessageSize="6553600"> <!--定义可由采用此绑定配置的终结点进行处理的 SOAP 消息的复杂性约束 maxArrayLength:一个正整数,指定所允许的最大数组长度。默认值为 16384。 maxBytesPerRead:一个正整数,指定每项读取操作返回的所允许的最大字节数。默认值为 4096。 maxDepth:一个正整数,指定每项读取操作的最大嵌套节点深度。默认值为 32。 maxNameTableCharCount:一个正整数,指定表名称中允许的最大字符数。默认值为 16384。 maxStringContentLength:一个正整数,指定 XML 元素内容中允许包含的最大字符数。默认值为 8192。 --> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <!--指定是否在通道终结点之间建立可靠会话。 inactivityTimeOut:一个 TimeSpan,指定通道在出错之前允许其他通信方不发送任何消息的最大持续时间。 通道上的活动被定义为接收应用程序或基础结构消息。如果在此属性指定的时间内未检测到活动,则基础结构会中止会话,且通道会出错。可靠会话被中止。 默认值为 00:10:00。 Ordered:一个布尔值,指定是否保证消息以其发送顺序抵达。默认值为 true。--> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <!--定义绑定的安全设置 <security mode="None"> <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /> </security>--> </binding> </netTcpBinding>
客户端--获取wcf数据-开始--线程0 2015-07-24 08:50:03;324 客户端--获取wcf数据-开始--线程2 2015-07-24 08:50:03;326 客户端--获取wcf数据-开始--线程1 2015-07-24 08:50:03;331 客户端--获取wcf数据-开始--线程4 2015-07-24 08:50:03;334 客户端--获取wcf数据-开始--线程3 2015-07-24 08:50:03;336 客户端--获取wcf数据-完成--线程0 2015-07-24 08:50:04;027 耗时701毫秒 客户端--获取wcf数据-完成--线程1 2015-07-24 08:50:04;039 耗时706毫秒 客户端--获取wcf数据-完成--线程2 2015-07-24 08:50:04;046 耗时718毫秒 客户端--获取wcf数据-完成--线程4 2015-07-24 08:50:04;429 耗时1094毫秒 客户端--获取wcf数据-完成--线程3 2015-07-24 08:50:04;523 耗时1191毫秒
服务端-接收到客户端请求-线程0 2015-07-24 08:50:03;328 服务端-接收到客户端请求-线程2 2015-07-24 08:50:03;332 服务端-接收到客户端请求-线程1 2015-07-24 08:50:03;347 服务端-接收到客户端请求-线程4 2015-07-24 08:50:03;815 服务端-返回到客户端请求-线程0 2015-07-24 08:50:03;831 耗时:501 服务端-返回到客户端请求-线程2 2015-07-24 08:50:03;837 耗时:502 服务端-返回到客户端请求-线程1 2015-07-24 08:50:03;851 耗时:502 服务端-接收到客户端请求-线程3 2015-07-24 08:50:03;903 服务端-返回到客户端请求-线程4 2015-07-24 08:50:04;322 耗时:506 服务端-返回到客户端请求-线程3 2015-07-24 08:50:04;408 耗时:503
Stopwatch的使用有问题,在 st.Start(); 之前要进行reset—— st.Reset();
你是不是对返回数据的方法做单例或者lock了? 理论上不可能的把
没有,wcf服务端直接返回数据,没有做任何处理的
理论上来说 单个线程执行时长100毫秒
N个线程的话应该每个线程也接近100毫秒才对
现在是每个线程执行时长都是递增的