如何解决IIS托管的WCF服务中超出的MaxConcurrentSessions问题

时间:2019-01-10 19:26:31

标签: c# wcf iis

我不在我的舒适范围内,请耐心提供相关信息。我们刚刚将IIS托管的WCF服务移至新服务器,并且调用此服务的客户端开始出现超时。回收应用程序池后,它运行大约10分钟,然后一切都开始超时。我们启用了WCF跟踪,在该跟踪中,我可以看到它说已超出MaxConcurrentSessions。该文档说该值默认为2 x [处理器数量],因此对我们来说应该为200。

该服务器位于负载均衡器后面,但当前是唯一的服务器。我们注意到在Performance Monitor中,连接以每秒大约6的速度挂出,但是当超时发生时,连接将爬升到大约30,并从那里继续爬上。

客户端使用wsHttpBinding TransportWithMessageCredential安全性进行连接。该服务使用自定义UserNamePasswordValidator中的asp.net成员资格提供程序验证消息中提供的凭据,该自定义reliableSession用于服务器绑定行为。客户端未在其绑定上启用SessionMode。该服务使用默认的InstanceContextModeAllowed,我相信它们分别是PerSessionClose?我们不会在服务代理上调用Processing message n,因为在过去的调查中,我发现这仅在选项上设置了一个标志,阻止该选项被重复使用,并且无论如何我们总是超出范围...但是现在做测试以查看是否确实关闭了连接。

如果我正确解释了WCF跟踪日志(并且我不明白我在此处阅读的大部分内容),则似乎我们每分钟处理大约30至40条消息,而每个请求的完成时间都更少少于300毫秒(通常少得多,在极少的情况下接近1秒钟)。我通过在1分钟的跨度内计算Close消息来确定消息的数量。因此,如果我们每分钟40个,并且这些连接/会话超时和关闭需要100秒钟,那么在第一个连接/会话开始超时之前,我们仍然只能一次打开约68个。未接近200个限制。单个客户端请求的连接是否获得多个会话?

奇怪的是,我们之前没有任何超时,因此将服务和web.config直接复制到了新服务器上。我认为服务器和IIS版本已升级(服务器2016,IIS 10)。能否请您帮我确定并提供相关信息,以查找导致这些超时的问题?

修改
从我的阅读来看,一切似乎都表明客户端必须调用Close,否则服务器将保持连接打开状态直到超时。但是,在我们的测试中,我们看到在perf中创建了一个连接。星期一但无论如何Close被调用后它仍然保持打开状态。因此,我无法确定是否需要致电关闭是谣言,还是我们误解了我们的监控手段。真正的测试将是在任何地方调用MaxConcurrentSessions,看看它是否消除了我们的超时时间。

在性能监控器中将select * from my_table where id > @last_written_id or @last_written_id > 999999/ 2 and id < @last_written_id / 2 增加到400后,我们发现并发会话和实例的数量稳定地每秒增加约1,直到达到约225,最终稳定下来并徘徊在此附近。因此,似乎会话没有关闭。

1 个答案:

答案 0 :(得分:0)

我们弄清楚了。没有什么突然出现并告诉我们问题出在哪里,这花了很多时间来思考,但这是我们所做的:

  1. 启用WCF跟踪。仔细研究了这些痕迹,并能够足够了解,基本上可以看出流量并没有与众不同。所有事件似乎都与预期的服务呼叫数量和类型有关。在svctraceviewer中查看,这似乎不是DOS攻击或类似的攻击。我们只是使用了该链接中的默认配置,但看起来很容易定制,如果知道的话可以提供所需的特定信息。

  2. 在这种情况下真正有用的是找到WCF Performance Counters。最初,我们使用ASP.NET性能计数器来查看打开的会话,这不是正确的指标。 This codeproject guide帮助我们启用了WCF性能计数器,使我们可以实时了解会话数和限制。

  3. 它还有助于重新了解WCF会话和实例之间的关系以及安全上下文的创建:

我们能够看到正在使用的最大WCF会话的百分比,并观察到它逐渐向默认限制200(每个处理器100)攀升,但最终在150到200之间稳定下来。在给定时间存在的会话要比WCF跟踪中看到的每分钟平均请求数多得多,这表明会话正在关闭,但似乎一直保持打开状态直到它们超时,而不是在服务器完成请求后立即关闭。

在堆栈溢出的某个地方,我一直找不到,我曾经问过[ClientBase<TChannel>.Close][4]方法(也就是WCF服务代理的close方法)的用途,但有些错误地得出了结论它所做的全部工作就是在代理对象上设置一个标志,将其标记为关闭,从而使其无法再次使用。该方法的文档说明似乎与此相符:

  

使ClientBase对象从其当前状态过渡   进入关闭状态。

好吧,我将之称为Close,无论如何我的引用总是超出范围,允许垃圾收集清理它,因此似乎毫无意义。但是我认为一个关键因素是关于无状态的basicHttpBindings的问题。在这种情况下,我们使用的是有状态的wsHttpBindings,这意味着服务器在完成请求后离开保持会话并保持连接打开,以便可以在同一连接上进行来自客户端的后续调用。因此,尽管我找不到任何文档或无法找到源代码中的文档,但似乎WCF客户端在上次发出请求后,必须必须在其服务代理上调用Close为了告诉服务器它可以关闭连接并释放该会话插槽。我没有机会通过调用Close来查找发送到服务器的消息,但是我们可以使用Performance Counter观察到会话数从1减少到0在客户致电该服务后,它将保持为1。

但是我们说的是,我们可能无法控制的WCF客户端能够损害服务器性能,并且如果他们不勤奋地编写代码并记得调用{{1},则可能创建拒绝服务。且服务器无法控制自己的性能??这听起来像是灾难的秘诀。那么,您可以在服务器上做两件事来减轻这种情况。首先,您可以增加最大会话数。在我们的案例中,我们徘徊在175左右,但偶尔会遇到超过200的流量高峰。我们暂时将其增加到800,以确保不会超过最大值。折衷方案是将更多的服务器资源专用于保存那些会话,直到会话超时,这些会话将永远不会再使用。幸运的是,服务器还控制超时。该服务可以使用ReceiveTimeout and the InactivityTimeout控制这些会话保持打开状态的时间。两者默认为10分钟,但两者中的较小者将被使用。如果您在考虑“接收超时听起来是错误的。那控制了服务接收一条大消息所花费的时间”,那么您并不孤单。但是,that's incorrect。在服务器端:

  

ReceiveTimeout –由服务框架层用来初始化会话空闲超时,该超时控制会话在超时之前可以空闲多长时间。

并且在客户端不使用它。因此,我们将Close设置为30秒,会话显着下降。这实际上可能太低了,因为确实重用了服务代理的代码中的某些点(例如,在循环中进行了多次调用,或者在两次调用之间进行了一些数据处理)现在在尝试调用服务时出现错误。会话关闭后。因此,您将必须找到合适的平衡。但是看来,最佳做法是关闭您的连接。

需要注意的一个陷阱是在服务代理上使用ReceiveTimeout。我一直尝试输入Dispose来查看intellisense是否会在我的代理上弹出.dispo方法,并发现它不是以为它没有实现Dispose并且不需要关闭或处置。事实证明,它确实实现了IDisposable,但它明确地实现了它,因此您必须将其强制转换为IDisposable才能在其上调用IDisposable。可是等等!暂时不要将您的代理放入Dispose语句中。 using的实现只是在代理上调用Dispose,如果代理处于故障状态(即服务调用引发异常),则会抛出异常。因此,您不能安全地执行以下操作:

Close

由于using(MyWcfClient proxy = new MyWcfClient()) { try { proxy.Calculate(); } catch(Exception) { } } 引发异常,因此Calculate块的右括号在尝试处置代理时也会引发异常。取而代之的是,您只需要在上次调用服务方法后调用using。显然,您也可以在Close中调用Abort,但是我不确定是否确实与服务器通信以结束会话。

catch

希望这可以帮助处于类似情况的人!

附录
我们推测,在移动服务器时开始遇到这种情况,而以前没有经历过这种情况的原因是,我们以前使用的是梭子鱼产品,现在使用的是Oracle,也许旧的负载平衡器或防火墙正在为我们关闭开放的连接。