Einführung
Die Downloadstelle muss schließen, ohne der Person, die bereits Feldnotizen empfängt, die Übertragung abzuschneiden. Sie ersetzen sofortiges Verbindungsschließen durch Gos gewöhnliche HTTP-Abschaltkoordination und erhalten einen echten vollständigen Download vom ursprünglichen Prozess.
Diese Einheit startet unabhängig mit vorbereitetem Handler und denselben zwei Dokumenten. Vorausgesetzt werden die gezielte PID- und Anfragebeobachtung aus der vorherigen Einheit oder gleichwertige Erfahrung sowie Handler, Fehler, Goroutines und Channels. Signalzustellung, endliche Kontexte, Fehlererkennung und mains Warteverantwortung werden nachfolgend erklärt. Behandelt wird gewöhnliches HTTP, keine WebSockets, verteilte Verkehrsumschaltung oder Produktionsgarantie ohne Ausfallzeit. Planen Sie etwa 60 Minuten ein; dies ist eine Autorenschätzung.
Lassen Sie main auf das Herunterfahren warten
In diesem Schritt implementieren Sie endliche Abschaltkoordination und bewahren den vorbereiteten HTTP-Handler.
Beginnen Sie in Downloads und betreten Sie den unabhängigen Arbeitsbereich:
cd /home/labex/project/download-desk
cat lifecycle.go
Nur lifecycle.go darf verändert werden. Bewahren Sie Handler in main.go, beide Dokumente, go.mod, vorbereitete Beobachter und ihre Aufzeichnungen unverändert. Die Funktion fängt derzeit SIGTERM ab, ruft jedoch Close auf und unterbricht aktive Verbindungen. Signalbehandlung allein macht einen Dienst nicht geordnet stoppbar.
Ein http.Server verwaltet Listener und aktive HTTP-Verbindungen. ListenAndServe blockiert, während es Arbeit annimmt. Shutdown schließt zuerst Listener und vorhandene inaktive Verbindungen und wartet dann, bis gewöhnliche aktive Anfragen inaktiv werden. Mit Beginn des Abschaltens gibt ListenAndServe sofort http.ErrServerClosed zurück, obwohl angenommene Handler weiterlaufen können. Kehrt main dann zurück, verschwinden sämtliche Goroutines und die Übertragung bricht ab. Deshalb muss der Hauptpfad auf die Rückkehr von Shutdown selbst warten.
Ein Kontext trägt Abbruch oder Zeitbudget. context.Background() liefert den Ausgangskontext. context.WithTimeout(..., 45*time.Second) erstellt ein endliches Abschaltbudget von 45 Sekunden und eine Funktion cancel; defer cancel() gibt zugehörige Ressourcen beim Funktionsende frei, auch wenn die Arbeit früher endet. Abbruch beweist keinen Abschluss. Das öffentliche Maximum beträgt 60 Sekunden nach der Stoppanforderung: Kann eine angenommene Antwort im gewählten Budget nicht fertig werden, melden Sie einen Fehler und beenden innerhalb dieses Maximums mit einem Status ungleich null. Die Referenz verwendet 45 Sekunden; gleichwertige endliche Budgets bis 60 Sekunden sind zulässig. Die Prüfung enthält eine endliche lange Antwort, die nicht in 60 Sekunden fertig werden kann; Herunterfahren nur mit context.Background() reicht daher nicht. In diesem isolierten Timeoutversuch sind Teilübertragung und gemeldeter Serverstatus ungleich null das korrekte Fehlerergebnis, kein erfolgreiches Abarbeiten. Bei Budgetablauf liefert Shutdown einen Fehler; unsere Funktion gibt ihn zurück und main endet mit Status ungleich null. Nennen Sie das nicht erfolgreiches Abarbeiten und starten Sie den Server nicht stillschweigend neu.
signal.Notify(stopped, syscall.SIGTERM) bittet Go, das Signal auf einen Channel zu senden. Dieser hat Puffergröße 1, weil die Signalzustellung nicht auf einen Empfänger warten darf; eine wartende Stoppanforderung reicht hier. signal.Stop(stopped) hebt beim Funktionsende die Registrierung dieses Channels auf; das ist etwas anderes als den HTTP-Server zu stoppen. Der gepufferte Ergebnis-Channel lässt entsprechend die Server-Goroutine ihr einziges Ergebnis melden, während main das Herunterfahren ausführt.
Das erste select wartet auf einen Serverfehler oder ein Stoppsignal. So bleibt ein Bindungs-/Startfehler nicht unbegrenzt hinter der Signalwartezeit verborgen. Nach erfolgreichem Herunterfahren erkennt errors.Is(err, http.ErrServerClosed) das erwartete Serverergebnis. Ein anderer Fehler wird zurückgegeben und nicht als Erfolg bezeichnet. main ruft runServer auf; erst nach dem Herunterfahren aus dieser Funktion zurückzukehren hält den Prozess am Leben.
Ersetzen Sie nur diese Datei. Der in Anführungszeichen gesetzte Here-Document-Begrenzer lässt die Shell Go-Quelltext wörtlich bis zum abschließenden alleinstehenden GO schreiben; > ersetzt nur lifecycle.go:
cat > lifecycle.go <<'GO'
package main
import (
"context"
"errors"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func runServer(server *http.Server) error {
stopped := make(chan os.Signal, 1)
signal.Notify(stopped, syscall.SIGTERM)
defer signal.Stop(stopped)
result := make(chan error, 1)
go func() { result <- server.ListenAndServe() }()
select {
case err := <-result:
return err
case <-stopped:
}
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
return err
}
err := <-result
if !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
}
GO
Formatieren Sie mit gofmt -w; -w schreibt die formatierte Datei. Kompilieren Sie das aktuelle Paket über einen expliziten Programmpfad, damit ein neues Terminal nicht von einer versteckten PATH-Einstellung abhängt:
/usr/local/go/bin/gofmt -w lifecycle.go
/usr/local/go/bin/go build -o desk .
Ein Syntaxfehler nennt Datei und Zeile. Vergleichen Sie den vollständigen Block und ersetzen Sie nötigenfalls nur lifecycle.go; setzen Sie die VM nicht zurück. Die Prüfung kompiliert Ihre unveränderte Funktion auch in isolierten temporären Testumgebungen und variiert echte Downloaddauern und Abschlussreihenfolgen. Sie prüft die lange Fehlerantwort ebenso wie den normalen Erfolg; rechnen Sie mit etwa einer Minute. Sie sendet Ihrem laufenden Server kein Signal und verändert Ihren Quelltext nicht.
Erhalten Sie den Download und schließen Sie den Listener
In diesem Schritt erhalten Sie einen angenommenen echten Download, während neue Verbindungen abgelehnt werden und der ursprüngliche Server abgewartet wird.
Wechseln Sie zu Server, betreten Sie den Arbeitsbereich und starten Sie einen neuen beobachteten Versuch. Dieses Terminal bleibt bis zum Ende des ursprünglichen Prozesses belegt:
cd /home/labex/project/download-desk
python3 desk.py serve drained
Warten Sie auf die tatsächliche Bereitschaftsmeldung und wechseln Sie zu Downloads. Lesen Sie den gesamten Schritt einschließlich erwartetem Prüfverbindungsfehler und Wiederherstellung, bevor Sie die folgende endliche langsame Übertragung starten. Führen Sie danach die Blöcke nacheinander aus; lernen Sie keine neuen Konzepte im Übertragungsfenster. Starten Sie wie zuvor eine neue endliche langsame Übertragung im Hintergrund. Die 300ms zwischen 32-Byte-Schreibvorgängen lassen normale Lesezeit; sie warten nicht unbegrenzt auf ein Signal.
python3 desk.py download drained completed north-pier.txt 300 & download_job=$!
python3 desk.py accepted drained completed
Der Beobachter liest die echte angenommene Anfrage vor jedem Stopp. Ermitteln und prüfen Sie die Identität des ursprünglich kompilierten Prozesses und senden Sie SIGTERM aus diesem Terminal:
server_pid=$(python3 desk.py pid drained)
python3 desk.py identity drained
kill -TERM "$server_pid"
Testen Sie während des laufenden angenommenen Downloads eine neue Verbindung. Die vorbereitete Prüfung unternimmt höchstens zwei Sekunden lang begrenzte echte Verbindungsversuche und bewahrt jedes native Ergebnis bis zur Ablehnung. So kann Go das zugestellte Signal vor dem ersten beobachteten Fehler verarbeiten; die Prüfung nimmt nicht an, dass kill den Listener synchron schließt. Der nächste Befehl soll 7 zurückgeben, zusammen mit einer Verbindungsablehnung:
python3 desk.py probe drained
Das ist die Ablehnung einer neuen Verbindung, kein HTTP 403 und keine beschädigte angenommene Übertragung. Der ursprüngliche Download soll weiterlaufen. Warten Sie auf seinen Hintergrundjob und vergleichen Sie die tatsächlich gespeicherten Bytes mit dem Original:
wait "$download_job"
cmp documents/north-pier.txt downloads/completed.txt
cat downloads/completed.txt
Erwartet werden nativer curl-Status 0, keine Ausgabe von cmp und das vollständige letzte Kapitel. Wechseln Sie zu Server: Dort sollte jetzt Original server native exit: 0 stehen. Der Server kann das Senden beenden, bevor der Client sein Lesen oder Schreiben gepufferter Daten abgeschlossen hat; das Herunterfahren wartet nicht auf eine Bestätigung der Clientanwendung. Es sind getrennte Beobachtungen, keine vorgeschriebene Reihenfolge zweier Terminalmeldungen.
Kehren Sie zu Downloads zurück. Behalten Sie lifecycle.go, den vollständigen Download und die nativen Beobachtungen. Die Prüfung liest dieses tatsächliche Ergebnis und testet denselben Quelltext auf isolierten Zufallsports mit einer vorhandenen inaktiven Verbindung und angenommenen Anfragen in verschiedenen Reihenfolgen. Direktes Close, Rückkehr aus main bei noch laufendem Shutdown, ein bis zum Arbeitsende offener Listener oder eine geschätzte Schlafdauer erfüllen den Vertrag nicht.
Ist der Download schon vor accepted oder dem Stopp beendet, bewahren Sie die Datei auf und wählen im noch laufenden Versuch eine neue Bezeichnung. Ist der Server bereits beendet, starten Sie in Server ausdrücklich einen neuen Versuchsnamen und verwenden ihn für die neue langsame Ausgabe sowie identity, accepted und probe. Eine erst nach Prozessende abgelehnte Prüfverbindung zeigt kein frühes Listener-Schließen; wiederholen Sie den beobachteten aktiven Ablauf mit neuen Namen. Überschreiben Sie keine frühere erfolgreiche Datei und setzen Sie den Beobachtungsverlauf nicht zurück.
Für einen vollständigen Neuversuch nach Ende des vorherigen Versuchs verwenden Sie Server:
python3 desk.py serve drained-retry
Nutzen Sie nach der Bereitschaftsmeldung Downloads. Lesen Sie zuerst alle Blöcke. Diese neuen Namen erhalten die bisherigen Dateien und Beobachtungen:
python3 desk.py download drained-retry completed-retry north-pier.txt 300 & download_job=$!
python3 desk.py accepted drained-retry completed-retry
server_pid=$(python3 desk.py pid drained-retry)
python3 desk.py identity drained-retry
kill -TERM "$server_pid"
Die folgende Verbindungsprüfung soll 7 zurückgeben:
python3 desk.py probe drained-retry
wait "$download_job"
cmp documents/north-pier.txt downloads/completed-retry.txt
Erwartet werden curl 0, gleiche vollständige Bytes und ursprünglicher nativer Exitstatus0 in Server. Läuft der frühere Server noch, lesen Sie erst fertig, bevor Sie dort eine neue langsame Ausgabe starten; verwenden Sie in download und accepted eine neue Bezeichnung und behalten den Versuchsnamen für identity und probe. Existiert eine frühere Verbindungsprüfung bereits, behalten Sie sie, stoppen ausdrücklich Ihren eigenen alten Versuch und lassen ihn enden, bevor Sie den vollständigen Neuversuch oben ausführen.
Ein echter Startfehler erscheint im vorhandenen stderr des Servers und liefert einen Status ungleich null; korrigieren Sie einen erläuterten Quelltext-/Buildfehler oder warten Sie auf das Ende Ihres alten Versuchs, bevor Sie 8080 erneut verwenden. Beenden Sie keinen gleichnamigen Plattformprozess. Ein Abschaltzeitfehler bedeutet, dass das endliche Budget keinen Abschluss belegt hat; lesen Sie ihn ehrlich, statt einen Ersatzprozess oder eine kopierte frühere Datei vorzuschieben.
Zusammenfassung
Sie haben Listener, angenommene HTTP-Handler, einen endlichen Abschaltkontext und main koordiniert. Der ursprüngliche Prozess nahm keine neuen Verbindungen mehr an und beendete seine bestehende Arbeit. Exakte abschließende Bytes und native Ergebnisse machten den Erfolg beobachtbar.



