3

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();
        }
    }
}
Henrik Cooke
  • 775
  • 5
  • 8

2 Answers2

3

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

  • Hi, thanks for the input. Is it possible to wrap a services host in another app domain when hosted in IIS/WAS? Have no experience with hands on experience with app domains. – Henrik Cooke Sep 23 '11 at 18:55
  • yes its possible! my .NET 2 WinForms code works in IIS .NET4. if you know how to do it in plain .NET apps, you can do the same in IIS. check out this article http://msdn.microsoft.com/en-us/magazine/cc163701.aspx –  Sep 26 '11 at 23:46
0

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?

Andre Luus
  • 3,692
  • 3
  • 33
  • 46
  • Thanks for the input! Your suggestion seems valid. Though, it will ofc increase the complexity. The service host can be created via a factory, so it is easily achievable. – Henrik Cooke Sep 23 '11 at 19:03