2017-02-06 3 views
7

Ist es möglich, eine Service-Fabric-App so zu binden, dass mehrere Ports überwacht werden?Service-Fabric-Bindung an mehrere Endpunkte

Im Grunde versuche ich einen Public-Facing-Service zu haben, der auf http: 80 und https: 443 lauscht und HTTP-Anfragen an https weiterleitet.

Ich habe einen neuen ASP.net Core-Dienst erstellt, der individuell funktioniert. I.e. mit SSL 443 oder nur Nicht-SSL 80, aber wenn ich beide ServiceInstanceListeners hinzufügen, scheitert es einfach!

Service-Fabric Explorer sagt der folgende Fehler nach mehrmaligem Timeout:

Unhealthy event: SourceId='System.RA', Property='ReplicaOpenStatus', HealthState='Warning', ConsiderWarningAsError=false. 
Replica had multiple failures in API call: IStatelessServiceInstance.Open(); Error = System.Fabric.FabricElementAlreadyExistsException (-2146233088) 
Unique Name must be specified for each listener when multiple communication listeners are used 
    at Microsoft.ServiceFabric.Services.Communication.ServiceEndpointCollection.AddEndpointCallerHoldsLock(String listenerName, String endpointAddress) 
    at Microsoft.ServiceFabric.Services.Communication.ServiceEndpointCollection.AddEndpoint(String listenerName, String endpointAddress) 
    at Microsoft.ServiceFabric.Services.Runtime.StatelessServiceInstanceAdapter.d__13.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at Microsoft.ServiceFabric.Services.Runtime.StatelessServiceInstanceAdapter.d__0.MoveNext() 

die seltsam ist, weil beide Zuhörer unterschiedliche Namen haben - so wie es scheint. Gibt es irgendwo irgendwo den Namen des Zuhörers, den ich verpasst habe?

Ich benutze die Asp.net Core-Vorlage dafür. Mein Stateless Service-Code lautet wie folgt:

internal sealed class Web : StatelessService 
{ 
    public Web(StatelessServiceContext context) 
     : base(context) 
    { } 

    /// <summary> 
    /// Optional override to create listeners (like tcp, http) for this service instance. 
    /// </summary> 
    /// <returns>The collection of listeners.</returns> 
    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() 
    { 
     return new ServiceInstanceListener[] 
     { 
      new ServiceInstanceListener(serviceContext => 
       new WebListenerCommunicationListener(serviceContext, "ServiceEndpointHttps", url => 
       { 
        ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting WebListener on {url}"); 

        return new WebHostBuilder() 
           .UseWebListener() 
           .ConfigureServices(
            services => services 
             .AddSingleton<StatelessServiceContext>(serviceContext)) 
           .UseContentRoot(Directory.GetCurrentDirectory()) 
           .UseStartup<Startup>() 
           .UseUrls(url) 
           .Build(); 
       })), 


      new ServiceInstanceListener(serviceContext => 
       new WebListenerCommunicationListener(serviceContext, "ServiceEndpointHttp", url => 
       { 
        ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting WebListener on {url}"); 

        return new WebHostBuilder() 
           .UseWebListener() 
           .ConfigureServices(
            services => services 
             .AddSingleton<StatelessServiceContext>(serviceContext)) 
           .UseContentRoot(Directory.GetCurrentDirectory()) 
           .UseStartup<Startup>() 
           .UseUrls(url) 
           .Build(); 
       })) 
     }; 
    } 
} 

Antwort

6

Ich brauchte den Namen auf ServiceInstanceListener zu setzen, die den Konstruktor hat

public ServiceInstanceListener(Func<StatelessServiceContext, ICommunicationListener> createCommunicationListener, string name = ""); 

ich war nicht klar, extra params hatte :)

+1

Der Name sollte mit dem Namen des Endpunkts in den Einstellungen für den Dienst übereinstimmen. Für einen Dienst mit einem einzelnen Endpunkt eines Typs ist es in Ordnung, ihn leer zu lassen, aber sobald Sie mehrere haben, müssen Sie ihn anhand des Namens identifizieren. – yoape

+1

Gut zu wissen, danke! Wird mich in Zukunft nicht stolpern. – Mardoxx

1

Sie Sie können all dies automatisieren, indem Sie den unten stehenden Code verwenden und die erforderlichen Informationen auch dort protokollieren, wo sie benötigt werden.

var currentEndpoint = ""; 
try 
{ 
    IList<ServiceInstanceListener> listeners = new List<ServiceInstanceListener>(); 
    var endpoints = FabricRuntime.GetActivationContext().GetEndpoints(); 

    foreach (var endpoint in endpoints) 
    { 
    currentEndpoint = endpoint.Name; 
    logger.LogInformation("Website trying to LISTEN : " + currentEndpoint); 

    var webListner = new ServiceInstanceListener(serviceContext => 
     new WebListenerCommunicationListener(serviceContext, endpoint.Name, (url, listener) => 
     { 
     url = endpoint.Protocol + "://+:" + endpoint.Port; 
     logger.LogInformation("Website Listening : " + currentEndpoint); 
     return new WebHostBuilder().UseWebListener()  .UseContentRoot(Directory.GetCurrentDirectory()) 
       .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None) 
       .UseStartup<Startup>() 
       .UseUrls(url) 
       .Build(); 
     }), endpoint.Name.ToString()); 
    listeners.Add(webListner); 
    } 
    return listeners; 
} 
catch (Exception ex) 
{ 
    logger.LogError("Exception occured while listening endpoint: " + currentEndpoint, ex); 
    throw; 
} 
+0

fwiw, es sieht so aus, als ob "url" tatsächlich auf "endpoint.Protocol +": // +: "+ endpoint.Port;" gesetzt ist, also musste ich diesen Teil nicht machen. Danke für die ausführliche Antwort! – JohnnyFun

Verwandte Themen