系统上线后WCF服务最近经常死掉的原因分析总结

前言  

  最近系统上线完修改完各种bug之后,功能上还算是比较稳定,由于最近用户数的增加,不知为何经常出现无法登录、页面出现错误等异常,后来发现是由于WCF服务时不时的就死掉了。后来就开始分析问题。得到的初步解决方案如下:

  1、在Web端调用WCF服务使用后,未释放未关闭导致新的链接无法访问

  2、增加默认的连接数,系统默认的链接数比较小

  3、提供同一个WCF服务的不同实例

1、在Web端调用WCF服务使用后,未释放未关闭导致新的链接无法访问  

首先保证客户端每次建立的连接在使用完成后进行关闭。那么请不要使用传统的using语句中来调用WCF,这里@dudu大神也曾经有遇到过这个问题 http://www.cnblogs.com/dudu/archive/2011/01/18/1938144.html。对其分析也比较全面,在此不再赘述。

不过自己感觉更好的处理方式可能是下面这样,也就是将@dudu中的方法进行了简单的封装,但自己感觉还有优化的空间,暂时还没试出来。

    public static class WcfExtensions
    {
        public static void Using<T>(this T client, Action<T> work)
            where T : ICommunicationObject
        {
            try
            {
                work(client);
                client.Close();
            }
            catch (CommunicationException e)
            {
                client.Abort();
            }
            catch (TimeoutException e)
            {
                client.Abort();
            }
            catch (Exception e)
            {
                client.Abort();
            }
        }
    }

进行调用看起来是如下的方式,看上去还是比较简练了,但是感觉还是有些繁琐,不知道能不能直接一行return代码搞定?

        public static DocAppend GetDocAppend(string dwid, string actionId)
        {
            DocAppend docAppend = new DocAppend();
            new DocumentServiceV2.DocumentServiceV2Client().Using(channel => docAppend = channel.GetDocAppend(dwid, actionId));
            return docAppend;
        }

另外一种关闭链接的方式,这种方式其实和上面那种大同小异,也是可以封装的,系统中暂且就使用的上面的方式。

            Document document = null;
            DocumentServiceClient client = new DocumentService.DocumentServiceClient();
            try
            {
                document= client.GetDocument(id);
                if (client.State != System.ServiceModel.CommunicationState.Faulted)
                {
                    client.Close();
                }
            }
            catch (Exception ex)
            {
                client.Abort();
            }
            return document;

2、增加默认的连接数,系统默认的链接数比较小  

如果采用的netTcp绑定,而在windows7中,并发连接数默认是10。

这是原来的配置文件

<binding name="netTcpBindConfig" closeTimeout="00:10:00" 
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" 
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" 
hostNameComparisonMode="StrongWildcard" listenBacklog="10" 
maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="10" 
maxReceivedMessageSize="2147483647"> 

将项目移植到服务器上之后

<binding name="netTcpBindConfig" closeTimeout="00:30:00" 
openTimeout="00:30:00" receiveTimeout="00:30:00" sendTimeout="00:30:00" 
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" 
hostNameComparisonMode="StrongWildcard" listenBacklog="100" 
maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="100" 
maxReceivedMessageSize="2147483647"> 

但有些时候还是不能解决问题,就想到是不是需要配置一下行为,于是将行为的连接数量也改变了

      <serviceBehaviors>
        <behavior name="ThrottledBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
          <serviceThrottling maxConcurrentCalls="5000" maxConcurrentSessions="5000" maxConcurrentInstances="5000" />
        </behavior>
      </serviceBehaviors>

maxConcurrentCalls:在同一时刻允许处理的最大服务器操作数。如果超过次数,则需要把其他方法调用插入队列中,以等待处理。

maxConcurrentSessions:同时传输或应用程序会话的最大个数。

maxConcurrentInstances:实例的最大个数。

增加连接数量

在Http协议中,规定了同个Http请求的并发连接数最大为2. 这个数值,可谓是太小了。而目前的浏览器,已基本不再遵循这个限制,但是Dot Net平台上的 System.Net 还是默认遵循了这个标准的。从而造成了,在使用HttpWebRequset 或者 WebClient 利用多线程的方式,访问某个网站时,经常出现 连接被异常关闭 的错误,大大降低了效率。

这个限制的值,是可以自己设置或配置的。此值设置后,只对以后发起的HTTP请求有效。

  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="5000"/>
    </connectionManagement>
  </system.net>

3、提供同一个WCF服务的不同实例

3、首先查看一个WCF服务类

里面有N多构造函数的重载版本,我们来具体看一下第二个构造函数

        public DocumentWriteServiceClient(string endpointConfigurationName) : 
                base(endpointConfigurationName) {
        }

即传入配置名生与代码类的实例,我们在web.config中的wcf配置节,做如下处理:

      <endpoint address="http://localhost:8700/Design_Time_Addresses/SinoSZJS.WebWCF/DocumentWriteService/"
 binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICommonBinding"
 contract="DocumentWriteService.IDocumentWriteService" name="1">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <endpoint address="http://localhost:8700/Design_Time_Addresses/SinoSZJS.WebWCF/DocumentWriteService/"
 binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICommonBinding"
 contract="DocumentWriteService.IDocumentWriteService" name="2">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <endpoint address="http://localhost:8700/Design_Time_Addresses/SinoSZJS.WebWCF/DocumentWriteService/"
 binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICommonBinding"
 contract="DocumentWriteService.IDocumentWriteService" name="3">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>

修改客户端的调用代码

DocumentWriteServiceClient client = new DocumentWriteServiceClient();

改为

DocumentWriteServiceClient client = new DocumentWriteServiceClient(new Random().Next(1, 4).ToString());

即客户端随机从多个wcf服务端的host中挑一个,生成代码类实例,说白了就是把一个wcf的host分身成了3个,并且客户端随机调用3者之一。

如果要考虑到大量并发的情况下,伪随机数可能确实有一些问题,不过,这个应该也不难解决,自己另外写一个类似伪随机数的算法,只要保证生成指定范围内不重复的数字(或字符)就可以了。

总结 

暂时这三种方式有效地防止了WCF服务的再次挂掉,至少最近几天服务一直在稳定的运行,没有太大的异常,很是让人欣慰。不知道针对WCF服务的处理是否还有其他方式,也让博客园的大牛们来指点一二吧。

英语小贴士

1、 Not yet. ——还没。 2、 See you. ——再见。 3、 Shut up! ——闭嘴! 4、 So long. ——再见。 5、 Why not? ——好呀! (为什么不呢?) 6、 Allow me. ——让我来。 7、 Be quiet! ——安静点! 8、 Cheer up! ——振作起来! 9、 Good job! ——做得好! 10、 Have fun! ——玩得开心!

作者:aehyok

出处:http://www.cnblogs.com/aehyok/

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,那不妨点个推荐吧,谢谢支持:-O。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码洞

求不更学不动之Redis5.0新特性Stream尝鲜

Redis5.0最近被作者突然放出来了,增加了很多新的特色功能。而Redis5.0最大的新特性就是多出了一个数据结构Stream,它是一个新的强大的支持多播的可...

22720
来自专栏狮乐园

高级 Angular 组件模式 (3b)

Kent C. Dodds的第四篇文章中的一个重要元素在上一篇文章中没有涉及,使用withToggle高阶组件(HoC, react中的常用模式)可以将<tog...

20310
来自专栏海说

6、Java包的命名与划分

包的命名与划分 (一)使用Java包的目的 在了解做一件事之前,需要了解做这件事的目的。而使用Java包的目的大概如下: 1    对类进行归类,便于开发查找。...

33800
来自专栏程序员互动联盟

【答疑解惑】如何知道要包含哪个头文件

之前遇到一个同学问一个问题,他在c源文件中使用bool变量,然后编译的时候提示bool没有定义。不知道怎么办。其实对于初学者来说,遇到的编译错误最多的就是某某变...

31970
来自专栏移动端开发

Socket学习总结系列(二) -- CocoaAsyncSocket

这是系列的第二篇 这是这个系列文章的第二篇,要是没有看第一篇的还是建议看看第一篇,以为这个是接着第一篇梳理的 先大概的总结一下在上篇的文章中说的些内容: 1、 ...

34670
来自专栏码洞

求不更学不动之Redis5.0新特性Stream尝鲜

Redis5.0最近被作者突然放出来了,增加了很多新的特色功能。而Redis5.0最大的新特性就是多出了一个数据结构Stream,它是一个新的强大的支持多播的可...

18860
来自专栏java一日一条

同步和异步的区别

答案一: 1.异步传输 通常,异步传输是以字符为传输单位,每个字符都要附加 1 位起始位和 1 位停止位,以标记一个字符的开始和结束,并以此实现数据传输同步...

10040
来自专栏Jackson0714

不惧面试:HTTP协议(3) - Cookie

14620
来自专栏Golang语言社区

从websocket看go的应用

Go是互联网时代的通用编程语言。这样它就和命令行时代的C语言、图示界面时代的C++、以及互联网早期的Java语言等有不同的侧重。它强调保持自身的精巧和独立,从而...

31960
来自专栏java一日一条

同步和异步的区别

答案一: 1.异步传输 通常,异步传输是以字符为传输单位,每个字符都要附加 1 位起始位和 1 位停止位,以标记一个字符的开始和结束,并以此实现数据传输同步...

9620

扫码关注云+社区

领取腾讯云代金券