Search code examples
c#wcfazure-service-fabricstatelesssuffix

Stateless WCF service listener on same port


We have a situation where we have been using ordinary windows service based on WCF where we host a number of listeners on the same address, port with just additional suffix on the address, such as:

http://localhost:9000/<listener1_name>/
http://localhost:9000/<listener2_name>/
http://localhost:9000/<listener3_name>/
http://localhost:9000/<listener4_name>/

However, now that we like to move over to asuze by using service fabric we like to take it step by step. And one of the step is to move this windows service into the Service Fabric environment...

What I fail to do is as you might realise, to setup a number of stateless services that listen to the same port, but with different suffix.

I thought that the PathSuffix attribute in the EndPoint would do the trick, but I get only one of the services up and running. The others states clearly that the port is already taken.

Hoped this would work:

<Resources>
  <Endpoints>
    <Endpoint Name="WcfService1Endpoint" Protocol="tcp" Port="9000" PathSuffix="Service1ListenerName" /
  <Endpoints>
</Resources>

and then in the next:

<Resources>
  <Endpoints>
    <Endpoint Name="WcfService2Endpoint" Protocol="tcp" Port="9000" PathSuffix="Service2ListenerName" />
  <Endpoints>
</Resources>

Etc, etc...

Is there any other way to solve my situation, or do I need to change the whole structure now?

Hoping someone out there has solved this!

Thanks!


Solution

  • Ok, here is how I did this:

    I build a base class

    public class StatelessServiceBase : StatelessService
    {
        .
        .
        .
    
        protected ICommunicationListener CreateListener(StatelessServiceContext context, object service, string interfaceName, AuthenticationInspector inspector = null)
        {
            Uri baseUri = new Uri($"{Util.GetBaseServerAddress()}{service.GetType().Name}");
            ServiceHost serviceHost = new ServiceHost(service.GetType(), baseUri);
            this.AddServiceEndpoint(serviceHost, service, interfaceName, inspector);
            return new ServiceHostCommunicationListener(serviceHost, baseUri.AbsoluteUri);
        }
    
        private void AddServiceEndpoint(ServiceHost serviceHost, object service, string interfaceName, AuthenticationInspector inspector)
        {
            var binding = new WSHttpBinding(SecurityMode.None);
            binding.SendTimeout = new TimeSpan(0, 10, 0);
            binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
            binding.MaxBufferPoolSize = 2147483647;
            binding.MaxReceivedMessageSize = 2147483647;
            binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas
            {
                MaxDepth = 2147483647,
                MaxStringContentLength = 2147483647,
                MaxArrayLength = 2147483647,
                MaxBytesPerRead = 2147483647,
                MaxNameTableCharCount = 2147483647
            };
    
            if (inspector == null)
            {
                serviceHost.AddServiceEndpoint(service.GetType().GetInterface(interfaceName), binding, string.Empty);
            }
            else
            {
                serviceHost.AddServiceEndpoint(service.GetType().GetInterface(interfaceName), binding, string.Empty).Behaviors.Add(inspector);
            }
        }
    }
    

    And then I called the CreateListener from the stateless service class:

    internal class MyStatelessService : StatelessServiceBase
    {
        public MyStatelessService(StatelessServiceContext context)
            : base(context)
        {
        }
    
        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            yield return new ServiceInstanceListener(context =>
                     this.CreateListener(context, new MyService(), "IMyService", new AuthenticationInspector()));
        }
    }
    

    And the Settings.xml looked like this:

    <?xml version="1.0" encoding="utf-8" ?>
    <Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
      <Section Name="Configuration">
        <Parameter Name="BaseServerAddress" Value="http://localhost:9000/"/>
      </Section>
    </Settings>
    

    Together with the reader function:

    public class Util
    {
        internal static string GetBaseServerAddress()
        {
            var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
    
            var baseServerAddress =
                configurationPackage.Settings.Sections["Configuration"].Parameters["BaseServerAddress"];
    
            return baseServerAddress.Value;
        }
    }
    

    Adding multiple services in service fabric and... Working like a charm! :)