Laden...

[gelöst]WCF: Client braucht zu lange um zu merken, dass der Host nicht zu erreichen ist.

Erstellt von Gnozo vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.803 Views
G
Gnozo Themenstarter:in
141 Beiträge seit 2011
vor 10 Jahren
[gelöst]WCF: Client braucht zu lange um zu merken, dass der Host nicht zu erreichen ist.

Hallo Leute,

Ich habe ein kleines Tool, welches mit einem WCF Service kommuniziert. Es verbindet sich beim Start um sich die neuste Konfiguration vom Service zu holen.

Mein Problem ist, dass der Client ewig braucht, um zu merken, falls der Host nicht zu erreichen ist.

Ich verbinde mich mich dem Host:


using (HostFunctionsClient proxy = new HostFunctionsClient("myEndpoint", new System.ServiceModel.EndpointAddress("net.tcp://" + Config.ServiceConnection.IP + ":" + Config.ServiceConnection.Port + "/myHost")))
{
}

Ich habe die Verbindungs-Details in der App.config hinterlegt und übergebe dem Konstruktor nur den Endpoint und die tcp-Adresse.

Wenn ich jetzt eine Funktion aus dem Host aufrufe, obwohl dieser nicht gestartet ist, braucht es ca. 30 Sekunden, bis der Client mit einer Exception rausspringt und ich entsprechend darauf reagieren kann. Ich muss diese Zeit aber verkürzen, damit es zu keinen ewig langen Wartezeiten beim Start kommt. Ich habe schon versucht, alle möglichen Timeouts mitzugeben:


proxy.Endpoint.Binding.SendTimeout = TimeSpan.FromSeconds(10);
proxy.Endpoint.Binding.OpenTimeout = TimeSpan.FromSeconds(10);
proxy.InnerChannel.OperationTimeout = TimeSpan.FromSeconds(10);

Leider ohne Erfolg. Er hängst trotzdem noch fast 30 Sekunden in der Anfrage, bis er rausspringt und sagt, dass der Host weg ist.

Ein anderes Problem ist, dass wenn ich den Host beenden will. Mit


using (ServiceHost sh = new ServiceHost(typeof(HostFunctions), new Uri[] { new Uri("net.tcp://localhost:" + Config.ServiceConnection.Port + "/myHost") }))
{
   sh.Open();
   
   sh.Close();
   // oder
   sh.Abort();
}

Dann hängt er ebenfalls sehr lange an Close() oder Abort(), bis der host sich wirklich schließt. Ich habe schon im Internet nach vieschiedenen Timeout-Einstellungen gesucht, aber nichts, was auf meine Situation passt, gefunden. Zumal ich denke, dass er bei Abort den Host einfach abschießen sollte und dafür ja keine 20 Sekunden brauchen sollte.

Hier noch der Abschnitt meiner App.config:


<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="myBehavior">
                    <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8501/myHostMeta" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="myBehavior" name="my.service.controler.main.HostFunctions">
                <endpoint address="net.tcp://localhost:8500/myHost" binding="netTcpBinding"
                    bindingConfiguration="" name="myEndpoint" contract="my.service.controler.main.HostFunctions" />
            </service>
        </services>
    </system.serviceModel>
</configuration>

Hoffe, Ihr könnt mir dabei helfen 😃

Gruß
Gnozo

16.834 Beiträge seit 2008
vor 10 Jahren

Close erlaubt dem Host den Buffer zu leeren. Abort killt sofort.
Daher das Delay, das auch dokumentiert ist. Zusammen zu nutzen ist sinnfrei.

Ich weiß nicht, ob sich das mittlerweile geändert hat, aber eigentlich muss das Timeout vor Open() / Connect des Hosts gesetzt werden, da es zum Binding gehört.
Da Du das über das Proxy-Element setzt scheint mir, dass es in diesem Fall schon zu spät ist.

Wenn Du einen long running service hast wäre ohnehin zu überlegen ob Du nicht reliableSessions mit einem unendlichen Timeout verwendest.
Bei TCP WCF Channels war noch was, dass das Timeout schwanken kann. Das ist meines wissens beim HTTP Binding nicht so.

G
Gnozo Themenstarter:in
141 Beiträge seit 2011
vor 10 Jahren

Hallo Abt,

Vielleicht habe ich das etwas unverständlich erklärt.

Ich benutze nicht Abort und Close zusammen sondern nur eines davon, wobei Abort genauso lange braucht, wie Close.

Das Verbindungstimeout des Hosts ist nicht wichtig, sondern das Timeout des Clients. Der Client soll schnell merken, wenn der Host nicht zur Verfügung steht. Leider braucht dieser sehr lange, bis er das merkt. Dies will ich verhindern und daher dem Client einen Timeout übergeben, dass er nicht ewig nach dem host sucht und dadurch den Programmfluss stört.

Gibt es eine solche Einstellung?

16.834 Beiträge seit 2008
vor 10 Jahren

Sicher, dass das Abort() bei Dir blockiert und nicht was anderes?
Gerade mal mit einem WCF hier während dem Senden getestet: Abort killt sofort und Close wartet bis zum entleerten Buffer.

Was ich gerade erst sehe: es stimmt zwar, dass - wo man kann - using() verwenden sollte.
WCF ist hier aber eine Ausnahme, wo man das besser unterlassen und andere, bessere Methoden verwenden sollte.
Siehe dazu auch Do not use “using” in WCF Client
Aber beachte auch hier die Close/Abort konstellation.

Ist ja wurst ob Server oder Client. Das Timeout bezieht sich immer auf das Binding und das muss eben vor dem Open (Server)/Connect(Client) gesetzt werden.

Wenn ich die Timeouts bei mir in der Config definiere greifen sie jedenfalls.
Ich geh also bei Dir davon aus, dass die Werte beim Dienst gar nicht ankommen.
Den Client interessiert hier IIRC SendTimeout und OpenTimeout.

G
Gnozo Themenstarter:in
141 Beiträge seit 2011
vor 10 Jahren

Hallo Abt,

Bis eben, war ich mir sicher, dass er bei Abort hängt. Dann habe ich ein bisschen rumgetestet und er legt im dem using ein wirklich komisches Verhalten an den Tag. Wenn ich innerhalb des using´s andere ressourcen freigeben will, springt er in den catch-fall. Leider hilft mir die Exception nicht weiter, welche er rausgibt, da diese sofort verworfen wird und das Programm sich "durch zauberhand" beendet.

Ich werde hier mal meinen Code, welcher den Host öffnet zeigen:


using (ServiceHost sh = new ServiceHost(typeof(HostFunctions), new Uri[] { new Uri("net.tcp://localhost:" + Config.ServiceConnection.Port + "/myHost") }))
{
try
{
    sh.Open();
    lock (this)
    {
        Log.Write("----> Host started");
        System.Threading.Monitor.Wait(this);
    }
    // Filewatcher/Timer etc schließen
    sh.Close();
}
catch(Exception exp)
{
    Log.Write(exp.Message);
    sh.Abort();
}

Die Host-Schließen-Funktion:


public void CloseHost()
{
    lock (this)
    {
        System.Threading.Monitor.Pulse(this);
    }
}

Soweit sehe ich nichts falsches. Wenn ich jetzt CloseHost() aufrufe und ein Signal gebe, läuft er aus dem Wait(this) raus und beginnt die Timer/Filewatcher zu disposen. Schon beim ersten Dispose rennt er in das catch rein und verwirft auch die Exception....> Fehlermeldung:

Message = Der Ausdruck konnte nicht ausgewertet werden, da der Code optimiert wurde oder oben in der Aufrufliste ein systemeigener Frame enthalten ist.

Auch wenn ich das Abort() aufrufe, bevor ich Ressourcen freigebe, läuft er in das catch. Versucht dann noch die Fehlermeldung ins Log zu schreiben, wird in der Zeit aber beendet. An dem schreiben ins Log liegt es nicht, das funktioniert.

G
Gnozo Themenstarter:in
141 Beiträge seit 2011
vor 10 Jahren

Ich habe im Client alle usings durch die neue Variante aus deinem Artikel ersetzt. Ebenso habe ich der App.config folgende Paramter hinzugefügt:


        <bindings>
            <netTcpBinding>
                <binding name="myEndpoint" maxReceivedMessageSize="2147483647" openTimeout="00:00:02" sendTimeout="00:00:02" receiveTimeout="00:00:02"/>
            </netTcpBinding>
        </bindings>

Wenn ich jetzt mit dieser Methode eine falsche IP-Adresse angebe, hängt er trotzdem ca. 20 Sekunden fest, bis er in eine Exception springt.

Er bringt bei einer falschen IP-Adresse eine EndpopintNotFoundException. In der Exception sagt er mir, dass der Verbindungsversuch für einen Zeitraum von 00:00:02 angedauert hat, welchen ich ja in der App.config eingetragen habe.

Die Zeit, welche er jedoch gebraucht hat, bis er in der Exception gelandet ist, war aber viel länger.

EDIT: Wenn ich lokal einen Fehler hervorrufe, springt er sofort in eine Exception. Sei es, ob ich einen falschen Port angegeben habe oder "localhost" falsch geschrieben habe. Über das Netzwerk brauch er sehr lange, bis er das merkt. Ist dies allgemein so, oder gibt es wirklich eine Einstellung dafür, dass dies auch schneller geht?

16.834 Beiträge seit 2008
vor 10 Jahren

bei lock this werd ich immer hellhörig.
Was ist denn this und wieso verwendest Du keine "echte" lockVariable?

Ist dies allgemein so, oder gibt es wirklich eine Einstellung dafür, dass dies auch schneller geht?

Bei TCP WCF Channels war noch was, dass das Timeout schwanken kann. Das ist meines wissens beim HTTP Binding nicht so.

Hab ich jetzt 2 mal gesagt. Ich kanns auch ein drittes Mal sagen.

G
Gnozo Themenstarter:in
141 Beiträge seit 2011
vor 10 Jahren

Hallo Abt,

Ich habe lock(this) aus... faulheit genutzt. Für meine Zwecke reicht die Verwendung von this.

Das mit dem HTTP-Binding habe ich jetzt umgesetzt und dies hat wirklich Besserung gebracht. Den Timeout konnte ich, wie von dir erwähnt, mit SendTimeout und OpenTimeout einschränken.

Danke dafür! 😃