WCF 服务主机关闭被打开的会话阻止,并且设置 receiveTimeout 没有帮助
我需要 WAS 托管服务(PerCall、Concurrency.Multiple)正常关闭/回收,但任何不活动(但打开)的客户端代理都会阻止服务正常关闭。
我原以为 receiveTimout 会启动并丢弃不活动的会话,但看起来它不起作用。
IIS/WAS 回收将调用 ServiceHost.BeginClose,并将关闭超时设置为 TimeSpan.MaxValue。
我需要使用 netTcpBinding 允许长期存在的客户端代理(我无法真正控制),因为吞吐量和低延迟是必须的。
我已经重现了下面的问题,并且很高兴获得有关该问题的任何解决方法和帮助。
using System;
using System.ServiceModel;
namespace Test
{
[ServiceContract(Name = "MyService", SessionMode = SessionMode.Allowed)]
public interface IHelloWorldService
{
[OperationContract]
void PrintHelloWorld();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HellowWorldService : IHelloWorldService
{
[OperationBehavior]
public void PrintHelloWorld()
{
Console.WriteLine("Hello world!");
}
}
public class ThaProgram
{
static void Main(string[] args)
{
const string ServiceAddress = "net.tcp://localhost:12345/HelloWorld";
var netTcpBinding = new NetTcpBinding(SecurityMode.None, false);
netTcpBinding.ReceiveTimeout = TimeSpan.FromSeconds(3);
var serviceHost = new ServiceHost(typeof(HellowWorldService), new Uri("net.tcp://localhost:12345"));
serviceHost.AddServiceEndpoint(typeof(IHelloWorldService), netTcpBinding, ServiceAddress);
serviceHost.Open();
Console.WriteLine("Service host state: {0}", serviceHost.State);
netTcpBinding.ReceiveTimeout = TimeSpan.FromSeconds(10);
var channel = new ChannelFactory<IHelloWorldService>(netTcpBinding, ServiceAddress).CreateChannel();
channel.PrintHelloWorld();
// Uncomment to make everything work (then the session will be closed before the service enters the closing state)
// Thread.Sleep(4000);
// Simulate application pool shutdown
var asyncResult = serviceHost.BeginClose(TimeSpan.MaxValue, null, null);
Console.WriteLine("Service host state: {0}", serviceHost.State);
serviceHost.EndClose(asyncResult);
Console.WriteLine("Service host state: {0}", serviceHost.State);
Console.WriteLine("Hit Enter to close the application");
Console.ReadLine();
}
}
}
I need my WAS hosted service (PerCall, Concurrency.Multiple) to shutdown/recycle gracefully but any inactive (but open) client proxies will block the service from shuttingdown gracefully.
I had expected the receiveTimout to kick in and toss out the inactive sessions but it looks like it doesn't work that way.
The IIS/WAS recycle will call ServiceHost.BeginClose with closing timeout set to TimeSpan.MaxValue.
I need to allow long lived client proxies (which I can not really control) with netTcpBinding, since throughput and low latency is a must.
I have reproduced the problem below and would be glad for any workarounds and help regarding the problem.
using System;
using System.ServiceModel;
namespace Test
{
[ServiceContract(Name = "MyService", SessionMode = SessionMode.Allowed)]
public interface IHelloWorldService
{
[OperationContract]
void PrintHelloWorld();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HellowWorldService : IHelloWorldService
{
[OperationBehavior]
public void PrintHelloWorld()
{
Console.WriteLine("Hello world!");
}
}
public class ThaProgram
{
static void Main(string[] args)
{
const string ServiceAddress = "net.tcp://localhost:12345/HelloWorld";
var netTcpBinding = new NetTcpBinding(SecurityMode.None, false);
netTcpBinding.ReceiveTimeout = TimeSpan.FromSeconds(3);
var serviceHost = new ServiceHost(typeof(HellowWorldService), new Uri("net.tcp://localhost:12345"));
serviceHost.AddServiceEndpoint(typeof(IHelloWorldService), netTcpBinding, ServiceAddress);
serviceHost.Open();
Console.WriteLine("Service host state: {0}", serviceHost.State);
netTcpBinding.ReceiveTimeout = TimeSpan.FromSeconds(10);
var channel = new ChannelFactory<IHelloWorldService>(netTcpBinding, ServiceAddress).CreateChannel();
channel.PrintHelloWorld();
// Uncomment to make everything work (then the session will be closed before the service enters the closing state)
// Thread.Sleep(4000);
// Simulate application pool shutdown
var asyncResult = serviceHost.BeginClose(TimeSpan.MaxValue, null, null);
Console.WriteLine("Service host state: {0}", serviceHost.State);
serviceHost.EndClose(asyncResult);
Console.WriteLine("Service host state: {0}", serviceHost.State);
Console.WriteLine("Hit Enter to close the application");
Console.ReadLine();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
奇怪的是,TCP 绑定 *WCF 服务主机* 无法随时断开连接,这就是 TCP 的优点,与命名管道不同。
无论如何,我喜欢使用的一种好模式是将所有服务主机操作包装在单独的 .net AppDomain 中。正是在这个辅助 AppDomain 中托管服务,而不是在主 AppDomain 中。将其视为沙箱。然后,当您希望关闭时,您可以使用正常的服务主机关闭方法关闭,然后让主应用程序域执行AppDomain.Unload(),这不仅保证了内存清理的好方法< /em> 而且还切断所有那些无法正确关闭的讨厌的客户端。
这种模式在命名管道 WCF 中是绝对必须的,因为客户端可以阻止服务器再次启动,正如我在之前的项目中发现的那样。 (由于孤立的连接)
希望一切顺利
strange that TCP-bound *WCF service host* cant sever the connection when-ever it likes, that's the nice-ity of TCP unlike say named pipes.
in any event, one good pattern that i like to use is to wrap all service host operation in a separate .net AppDomain. it is in this secondary AppDomain that hosts the service and not the Primary AppDomain. think of it as a sandbox. then when you wish to shutdown, you shutdown using normal service host close methods then have the primary appdomain do a AppDomain.Unload() which guarentees not only a nice way of memory clean-up but also severing all those pesky clients who dont close properly.
such a pattern is an absolute must in named pipes WCF because clients can prevent the server from starting up again as i found in a previous project. (due to orphaned connections)
hoping all goes well
我假设由于您使用的是 net.tcp,因此您的客户端不会通过 Web 代理连接到您的服务器。在这种情况下,我会让您考虑切换到双工 net.tcp 绑定来解决问题。
使用双工绑定,您可以让客户端在连接并存储回调合约的实例时调用订阅方法。在回调合约中,您可以添加一个操作,让客户端知道您想要关闭服务器。
为此,我的项目中已经有一个双工合同,但我仍然需要实现上面的解决方案,其中 Windows 服务中的 OnStop 要求客户端断开连接。不幸的是我不知道如何在 IIS 托管情况下拦截 BeginClose。也许使用自定义 ServiceHost?
I'll assume that since you are using net.tcp, your client doesn't connect through a web proxy to your server. In that case, I'd let you consider switching to a duplex net.tcp binding to solve the problem.
Using a duplex binding, you could let the clients call a subscribe method when they connect and store an instance of the callback contract. In the callback contract you can add an operation that lets the client know you want to close the server.
I already have a duplex contract in our project for this purpose but I still need to implement the solution above where OnStop in our Windows Service asks the clients to disconnect. Unfortunately I don't know how to intercept the BeginClose in the IIS hosting situation. Perhaps using a custom ServiceHost?