Der Standard-Client und die Statusklassen · Übung
Ein Timeout ist kein 504
Jede Antwort, die ankommt, hat jetzt eine Klasse und einen Body. In dieser Lektion geht es um die Anfrage, die keine Antwort bekommt. In Python in der Praxis haben ein Timeout und ein 504 Gateway Timeout vielleicht wie derselbe Fehlschlag ausgesehen: Etwas war langsam, und der Aufruf hatte keinen Erfolg. Es sind verschiedene Ereignisse, entschieden von verschiedenen Programmen, und ein Client, der das eine als das andere meldet, schickt jeden, der seine Ausgabe liest, an der falschen Stelle auf die Suche.
Zwei Szenarien, die sich Zeit lassen
Zwei Routen von practice-api warten, bevor sie antworten. /v1/scenarios/slow?delay_ms=250 wartet 250 Millisekunden und sendet dann ein 200. /v1/scenarios/timeout?delay_ms=750 wartet 750 Millisekunden und sendet dann ein 504 Gateway Timeout, den Status, den ein Server sendet, wenn etwas, auf das er gewartet hat, nicht rechtzeitig geantwortet hat. Hier spielt der Dienst diese Rolle absichtlich.
Probiere zuerst das langsame, mit einem Timeout von einer Sekunde:
python -c "import wire; print(wire.get_json('/v1/scenarios/slow?delay_ms=250', timeout=1.0)['status'])"
Es gibt nach einer kurzen Pause 200 aus. Eine Viertelsekunde passt in eine Sekunde, also war das Warten nichts weiter als ein Warten.
Jetzt das Timeout-Szenario, zweimal, mit zwei verschiedenen Timeouts:
python -c "import wire; print(wire.get_json('/v1/scenarios/timeout?delay_ms=750', timeout=2.0)['status'])"
python -c "import wire; print(wire.get_json('/v1/scenarios/timeout?delay_ms=750', timeout=0.5)['status'])"
Mit zwei Sekunden Wartezeit empfängt dein Client das 504 und gibt 504 aus. Mit einer halben Sekunde gibt er auf, bevor der Server antwortet: Der zweite Befehl endet mit einem TimeoutError: timed out ist. Dieselbe Anfrage, derselbe Server; nur dein Timeout hat sich geändert. Wenn auch der erste Befehl TimeoutError auslöst, hat die Antwort länger als zwei Sekunden gebraucht, um zu deiner Sandbox zurückzukommen; der Punkt unten hängt nicht davon ab.
Wer hat entschieden
Die beiden Läufe sehen auf einer Zeitleiste so aus. Vergleiche, wo jede Linie endet und wer sie beendet hat:

Mit timeout=0.5 vergeht eine halbe Sekunde ohne Kopf auf der Verbindung, und http.client löst in getresponse TimeoutError aus. Es gibt keinen Status, keine Header und keinen Body; dein Client hat beschlossen aufzuhören. Mit timeout=2.0 kommt die Antwort des Servers nach einer Dreiviertelsekunde an. Sie ist eine vollständige Antwort wie jede andere, mit Statuszeile, Headern und einem get_json gibt sie mit "class": "server error" zurück.
Die Frage “wer hat aufgegeben?” hat also in jedem Fall eine andere Antwort. Ein 504 bedeutet, dass ein Server bei etwas weiter hinten in der Kette aufgegeben und es dir gesagt hat. Ein TimeoutError bedeutet, dass dein Client beim Server aufgegeben hat, und der Server arbeitet vielleicht noch an der Anfrage. Das Erste ist eine Tatsache über den Server; das Zweite ist eine Tatsache über dein Timeout.
Mit timeout=0.5 löst ein GET von /v1/scenarios/timeout?delay_ms=750 TimeoutError aus. Wer hat aufgegeben?
report: sag, was davon passiert ist
report(target, *, make_connection, timeout) gibt eine Zeile zurück, die sagt, was mit einem GET von target passiert ist:
wenn
get_jsoneinen Datensatz zurückgibt, den Status und seine Klasse, wie in504 server erroroder200 success;wenn
get_jsonTimeoutErrorauslöst, eine Zeile, die sagt, dass beim Client das Timeout abgelaufen ist und wie lange er gewartet hat, und die keinen Status nennt, weil keiner angekommen ist.
Das try steht um den Aufruf von get_json, und das except nennt nur TimeoutError. Das ist die enge Grenze aus Python in der Praxis: Fang nur die
except OSErrorfängt auchConnectionRefusedError, weil beide Arten vonOSErrorsind. Eine abgelehnte Verbindung würde dann als Timeout gemeldet, und jemand würde nach einem langsamen Server suchen, wo gar kein Server gelauscht hat.except Exceptionfängt auch denValueError, denget_jsonfür einen als JSON ausgezeichneten Body auslöst, der kein JSON ist, und würde eine kaputte Antwort als Timeout melden.
Beide Fehlschläge sollten report als Ausnahmen verlassen, damit jeder, der es aufgerufen hat, sieht, was wirklich passiert ist.
get_json selbst braucht keine Änderung. Seit Lektion 1 verlässt TimeoutError es durch den Block with contextlib.closing(...), der die Verbindung auf dem Weg hinaus schließt, und report ist die einzige Stelle, die daraus eine Zeile macht. Fang es nicht innerhalb von get_json: Es gibt keine Antwort, die in einen Datensatz käme.
Deine Aufgabe
Schreibe in
wire.pydiereport(target, *, make_connection, timeout)wie oben beschrieben. Reichemake_connectionundtimeoutanget_jsonweiter.Führe es auf dem Timeout-Szenario mit beiden Timeouts aus:
python -c "import wire; print(wire.report('/v1/scenarios/timeout?delay_ms=750', timeout=0.5))"
python -c "import wire; print(wire.report('/v1/scenarios/timeout?delay_ms=750'))"
Die erste Zeile sagt in deinen eigenen Worten, dass beim Client nach 0,5 Sekunden das Timeout abgelaufen ist; die zweite lautet 504 server error. Wenn auch die zweite sagt, dass das Timeout deines Clients abgelaufen ist, hat das 504 länger als zwei Sekunden gebraucht, um deine Sandbox zu erreichen, wie im früheren Lauf; report hat beide Male die Wahrheit gesagt.
Drücke dann Check my work. Dein Client hat jetzt eine Antwort auf jede Antwort und auf keine Antwort. Die nächste Lektion prüft all das mit deinen eigenen Tests.
Aufgabe
Schreibe report(target, *, make_connection, timeout) in wire.py. Die get_json(target, make_connection=make_connection, timeout=timeout) auf und gibt eine Textzeile zurück:
wenn eine Antwort ankommt, den Status und seine Klasse, beginnend mit dem Status, wie in
504 server error;wenn
get_jsonTimeoutErrorauslöst, eine Zeile mit den Wörterntimed outund dem Timeout, das gewartet wurde, etwa0.5, und ohne.
Fang nur TimeoutError. Ein ConnectionRefusedError oder der report als Ausnahme verlassen. get_json lässt TimeoutError weiterhin durch, mit geschlossener Verbindung.
Führe report auf /v1/scenarios/timeout?delay_ms=750 mit timeout=0.5 und mit dem Standard-Timeout aus, wie die Lektion es zeigt.
Drücke dann Check my work. Die Prüfung ruft report und get_json mit einem Ersatz für make_connection auf, der ein geskriptetes 504 oder 200 zurückgibt, für einen stillen Server in getresponse() TimeoutError auslöst, in request() ConnectionRefusedError auslöst oder einen als JSON ausgezeichneten Body zurückgibt, der kein JSON ist. Sie prüft jede Zeile, dass das Timeout bei der Verbindung angekommen ist, dass die Verbindung nach dem Timeout geschlossen wurde und dass die anderen beiden Fehlschläge nicht gefangen wurden. Deine Formulierung um timed out herum ist deine Sache. Nichts aus deinem