2
votes

Trying to set up a stateful reliable service with wcf in service fabric, I have copied this sample code:

protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new[] { new ServiceReplicaListener((context) =>
    new WcfCommunicationListener<IService1>(
        wcfServiceObject:this,
        serviceContext:context,            
        endpointResourceName: "ServiceEndpoint",
        listenerBinding: WcfUtility.CreateTcpListenerBinding()
    )
)};

In ServiceManifest.xml I have declared the endpoint:

  <Resources>
    <Endpoints>
      <Endpoint Name="ServiceEndpoint" Protocol="http" Port="8100" />   
      <Endpoint Name="ReplicatorEndpoint" />
    </Endpoints>
  </Resources>

But when I deploy to local cluster, and look at the node where the service is running in Service Fabric Explorer, the endpoint has this address:

net.tcp://localhost:8100/455d1c74-7734-449b-a567-47b749b3b822/88af6851-0285-4989-b0aa-c0cbe8c2d06a-131056235989980581

How do I get a http address?

2
The point is that your listenerBinding property is assigned with WcfUtility.CreateTcpListenerBinding(). I suppose it is the only possible way right now as there is no other method in WcfUtility, but look at this link, maybe it will help you a bit: azure.microsoft.com/en-us/documentation/articles/…Przemek Marcinkiewicz
Thanks for your reply, I suspected the problem was with the listener binding. The page you refer to is where I got my code from :-). I have not been able to find any other documentation about using service fabric with wcf.Kayak58

2 Answers

1
votes

On my team, we have been working with wcf in service fabric these days. Firstly, We tried to use WcfCommunicationListener form Microsoft.ServiceFabric.Services.Wcf but finally we decide to use our own implementation of ICommunicationListener in order to have a better control over the service host. We also use net.tcp as binding instead of http. We defined behaviors and endpoints programatically, not using app.config. I am going to share our approach. Hope this can help you.

First step, the ICommunicationListener implementation:

public class ServiceHostCommunicationListener : ICommunicationListener
{
    private string baseAddress;

    public ServiceHost Host { get; set; }

    public ServiceHostCommunicationListener(ServiceHost host, string baseAddress)
    {
        Host = host;
        this.baseAddress = baseAddress;
    }

    public void Abort()
    {
        Host.Abort();
    }

    public async Task CloseAsync(CancellationToken cancellationToken)
    {
        try
        {
            await Task.Factory.FromAsync(Host.BeginClose(null, null), ar =>
            {
                Host.EndClose(ar);
            });
        }
        catch (Exception)
        {
            Host.Abort();
        }
    }

    public Task<string> OpenAsync(CancellationToken cancellationToken)
    {
        return Task.Factory.FromAsync(Host.BeginOpen(null, null), ar =>
        {
            Host.EndOpen(ar);
            return baseAddress;
        });
    }
}

Second step, create instance of the listener inside CreateServiceInstanceListeners inside our Service Fabric Service. Here is where I created a service host instance, its endpoints and behaviours.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        yield return new ServiceInstanceListener(context =>
        {
            return CreateListener(context);
        });
    }

    private ICommunicationListener CreateListener(StatelessServiceContext context)
    {
        Uri baseUri = new Uri($"net.tcp://{configuration.Network.BaseAddress}");

        ServiceHost serviceHost = new ServiceHost(new SampleService(), baseUri);
        InitServiceDebugBehavior(serviceHost);
        if (configuration.Network.MetadataAddress != null)
        {
            AddMetadataEndpoint(baseUri, serviceHost);
        }
        InitServerCertificate(serviceHost);
        AddServiceEndpoint(serviceHost);

        return new ServiceHostCommunicationListener(serviceHost, baseUri.AbsoluteUri);
    }


    private void InitServiceDebugBehavior(ServiceHost host)
    {
        var serviceDebug = host.Description.Behaviors.Find<ServiceDebugBehavior>();
        if (serviceDebug == null)
        {
            serviceDebug = new ServiceDebugBehavior();
            host.Description.Behaviors.Add(serviceDebug);
        }
        serviceDebug.IncludeExceptionDetailInFaults = configuration.ServiceBehavior.ServerDebug.IncludeExceptionDetailInFaults;
    }

    private void AddMetadataEndpoint(Uri baseUri, ServiceHost serviceHost)
    {
        ServiceMetadataBehavior smb = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
        if (smb == null)
        {
            smb = new ServiceMetadataBehavior();
            serviceHost.Description.Behaviors.Add(smb);
        }
        serviceHost.AddServiceEndpoint(
            ServiceMetadataBehavior.MexContractName,
            MetadataExchangeBindings.CreateMexTcpBinding(),
            configuration.Network.MetadataAddress
        );
    }

    private void InitServerCertificate(ServiceHost host)
    {
        var serverCertificateConfig = configuration.ServiceBehavior.ServerCertificate;
        host.Credentials.ServiceCertificate.SetCertificate(
            serverCertificateConfig.Store,
            StoreName.My,
            serverCertificateConfig.FindType,
            serverCertificateConfig.FindValue
            );
    }

    private void AddServiceEndpoint(ServiceHost serviceHost)
    {
        var binding = new NetTcpBinding(SecurityMode.Transport);
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
        serviceHost.AddServiceEndpoint(typeof(SampleService), binding, configuration.Network.ServiceAddress);
    }

Here is the configuration file, in case you have any doubts about it. We storage it inside PackageRoot-Config folder.

    {
     "Network": {
       "BaseAddress": "localhost:1020/SampleService/",
       "ServiceAddress": "service",
       "MetadataAddress": "mex"
       },
     "ServiceBehavior": {
       "ServerCertificate": {
       "Store": "LocalMachine",
       "FindType": "FindBySubjectDistinguishedName",
       "FindValue": "CN=mycert.deploy.com"
       },
     "ServerDebug": {
       "IncludeExceptionDetailInFaults": true
       }
     }
    }
0
votes

The only thing i can think of is manually create the Http binding based in this example:

BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
            {
                SendTimeout = TimeSpan.MaxValue,
                ReceiveTimeout = TimeSpan.MaxValue,
                OpenTimeout = TimeSpan.FromSeconds(5),
                CloseTimeout = TimeSpan.FromSeconds(5),
                MaxReceivedMessageSize = 1024 * 1024
            };
            binding.MaxBufferSize = (int)binding.MaxReceivedMessageSize;
            binding.MaxBufferPoolSize = Environment.ProcessorCount * binding.MaxReceivedMessageSize;
            return binding;

With that binding the address is http in service fabric explorer