AW: Avanti: bind() failed
Thomas Berger
ThB at gymel.com
Di Jul 29 11:29:25 CEST 2003
Hallo Herr Butkus,
> > Ich gehe derzeit davon aus, dass
> > - die Meldung zeigt, dass bei Neustart von Avanti der Port 4949 nicht verfügba
> > r war,
> > - das Problem behoben ist, wenn man den Server nach kurzer Pause (ca. 2 Min.)
> > neu startet.
>
> Was Sie beobachten, hat nur mit der Architektur des TCP/IP-Protokolls
> zu tun, aber nichts mit Avanti.
>
> TCP realisiert einen kontinuierlichen "Datenstrom" auf einemo
> paket-orientierten Protokoll (IP). Dazu teilt TCP den Datenstrom
> in einzelne Pakete, nummeriert diese, versieht sie mit der
> Ziel-Adresse (IP und Portnummer), sowie einer Prüfsumme und
> versendet sie schließlich via IP. Auf der Empfängerseite wird
> wieder alles zusammengesetzt.
nicht ganz korrekt: Die Adressen sind Bestandteile des IP-Headers,
Segmentierung und Zusammensetzung passiert auf diversen
Ebenen.
> Nun garantiert IP aber nicht, daß Pakete "in der richtigen Reihenfolge"
> beim Empfänger ankommen (oder daß sie überhaupt ankommen).
>
> Wenn nun nach einem Absturz sofort wieder ein neuer Daemon auf
> Port 4949 lauschen dürfte, könnte es sein, daß er Pakete erhält,
> die eigentlich für seinen Vorgänger bestimmt waren, und sie
> irrtümlich in seinen eigenen Datenstrom einbaut.
Das Problem ist halt, dass avanti seinen turnusmaessigen
Restart als "Absturz" veranstaltet. Denn wenn ein Prozess
(auch ein Server-Prozess) ein korrektes close() auf den
Socket macht, kann man sich ihn sofort krallen.
Das prinzipielle Problem ist hier allerdings auch, dass der
Server fuer den Restart natuerlich abwarten muss, bis keine
Kindprozesse mehr laufen, an die er die Verarbeitung
einzelner Requests delegiert hat.
> Um das zu verhindern, wartet das Betriebssystem nach dem Close
> eine gewisse Zeitlang ab (sog. TIME_WAIT Zustand in der
> TCP-State-Machine). Pakete, die in dieser Zeit eintreffen,
> werden verworfen. Programme, die in dieser Zeit versuchen,
> sich an den Port 4949 zu "binden", werden abgewiesen.
Nicht korrekt: TIME_WAIT ist ein Zustand von TCP/IP
Verbindungen, d.h. eine konkrete Kombination von
Server- und Client Ports- und Addressen ist eine gewisse
Zeitlang nicht moeglich.
Das Problem mit "bind() failed" ist jedoch, dass der neue Server
sich nicht registrieren kann, die Betriebssysteme ueberpruefen
ausserhalb der TCP/IP-Spezifikation turnusmaessig, ob zu jedem
registrierten Socket der zugehoerige Prozess noch existiert, sonst
geben sie ihn von sich aus frei.
> Man kann also sagen, daß es sich um ein ganz normales,
> und absolut standardkonformes Verhalten handelt.
>
> Die Avanti-Problematik ist damit natürlich nicht gelöst.
>
> Um eine vorläufige Abhilfe wegen der Abstürze unter Windows zu
> schaffen, möchte ich einen Wrapper für den avanti-cl bauen,
> der ja - soweit ich Herrn Eversberg da richtig verstanden habe -
> das Speicherleck nicht aufweist.
>
> Der Wrapper wird der Art eines "Inet-Daemons" wie unter Unix
> bekannt funktionieren, d.h. er lauscht auf Port 4949, und startet
> für jede eingehende Verbindung im Hintergrund eine Instanz des
> Avanti-CL. Die über die TCP/IP-Verbindung eingehenden Daten bekommt
> der avanti-cl dann als Standard-Input geliefert, sein Standard-Output
> wird dagegen abgefangen und über die TCP/IP-Verbindung versendet.
Das ist eine gute Nachricht. So haette avanti von Anfang an
implementiert werden muessen. Eine spaetere, optionale
Verfeinerung waere, dass der Avanti-Daemon den Socket an
den avanti-CL weitergibt, so dass das zusaetzliche Umleiten
nicht mehr erforderlich ist.
Zu ueberlegen ist, wie man in diesen Szenarios das Loggen
veranstaltet: Im Prinzip sind einzeilige Eintraege bis zu
2kB beim Anhaengen an Logdateien "atomar", d.h. der
Server-Prozess koennte auf diese Weise die Verbindungsdaten
(incl. Datenbank und Passwort) und Exit-Stati vermerken (Kind
gecrasht).
Das Vermerken der Jobs und irgendwelcher Resultate des
bisherigen avanti taugte absolut nichts (irgendwo hinten
ein "nichts gefunden" sagt wirklich nichts darueber aus,
welcher find-Befehl im Job das gemeldet hat), ausserdem
wurden die verschiedenen Ausgaben gleichzeitiger jobs ja
nett ineinandergewuerfelt. Auch das Pseudo-XML von avanti-CL
ist nicht so ueberzeugend, hoechstens als diagnostischer
Output, der zusaetzlich eingesammelt und abgelegt werden
kann: Dann haette man ein Verbindungs- etc. Log der
Serverkomponente und noch ein optionales Tracelog der
einzelnen avanti-Jobs, die voneinander getrennt blieben.
Ungluecklicherweise hat das Avanti-"Protokoll" ja einige
Merkwuerdigkeiten:
zuerst kommt die &-Zeile, die Informationen enthaelt
(reale Pfade auf der Zielmaschine), die der Client
eigentlich nicht wissen duerfte. Dann kommt der Job
und erst zum Schluss die Zeile, die ueber gewuenschte
Datenbank und Berechtigungen Aufschluss gibt. Diese
Zeile erst gibt dem Prozess die Moeglichkeit, sich
datenbankspezifisch zu initialisieren, dafuer muss
er also erst abwarten, bis die einzelnen Pakete des
Jobs alle eingetraeufelt sind. D.h. ohne Aenderungen
im Protokoll muss die Angelegenheit so implementiert
werden, dass der Server erst den gesamten Job einsammelt,
dann avanti-CL startet, dann den gesamten Job uebermittelt.
Schoen waere, schon nach der ersten Zeile avanti-CL
starten zu koennen und diesem dann die spaeter eintreffenden
Restbestandteile des Jobs uebermitteln zu koennen, sobald
sie anfallen...
viele Gruesse
Thomas Berger
Mehr Informationen über die Mailingliste Allegro