Heim >Backend-Entwicklung >Golang >Müssen wir beim Schreiben von HTTP-Handlern auf die Stornierung des Anforderungskontexts achten?
php Editor Xinyi Bei der Verarbeitung von HTTP-Anforderungen stellt sich häufig die Frage, ob die Löschung des Anforderungskontexts überwacht werden muss. In der tatsächlichen Entwicklung besteht normalerweise keine Notwendigkeit, die Stornierung des Anforderungskontexts explizit zu überwachen, da die PHP-Laufumgebung die damit verbundene Arbeit zur Ressourcenfreigabe automatisch übernimmt. In einigen Sonderfällen, beispielsweise wenn Sie Ressourcen manuell freigeben oder einige Bereinigungsvorgänge durchführen müssen, kann das Abhören des Anforderungskontexts jedoch eine effektive Möglichkeit sein. Daher hängt es von den spezifischen Geschäftsanforderungen und Entwicklungsszenarien ab, ob Sie auf die Stornierung des Anforderungskontexts warten müssen. In den meisten Fällen können wir uns getrost auf den automatischen Ressourcenverwaltungsmechanismus von PHP verlassen.
Angenommen, ich schreibe einen HTTP-Handler, der andere Vorgänge ausführt, bevor die Antwort zurückgegeben wird. Muss ich einen Listener einrichten, um zu überprüfen, ob der HTTP-Anforderungskontext abgebrochen wurde? damit es sofort zurückkehren kann, oder gibt es eine andere Möglichkeit, den Handler zu verlassen, wenn der Anforderungskontext abgebrochen wird?
func handlesomething(w http.responsewriter, r *http.request) { done := make(chan error) go func() { if err := dosomething(r.context()); err != nil { done <- err return } done <- nil }() select { case <-r.context().done(): http.error(w, r.context().err().error(), http.statusinternalservererror) return case err := <-done: if err != nil { http.error(w, err.error(), http.statusinternalservererror) return } w.writeheader(http.statusok) w.write([]byte("ok")) } } func dosomething(ctx context.context) error { // simulate doing something for 1 second. time.sleep(time.second) return nil }
Ich habe versucht, es zu testen, aber nachdem der Kontext abgebrochen wurde, stoppt die dosomething
-Funktion nicht und läuft weiterhin im Hintergrund.
func TestHandler(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/something", handleSomething) srv := http.Server{ Addr: ":8989", Handler: mux, } var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() if err := srv.ListenAndServe(); err != nil { log.Println(err) } }() time.Sleep(time.Second) req, err := http.NewRequest(http.MethodGet, "http://localhost:8989/something", nil) if err != nil { t.Fatal(err) } cl := http.Client{ Timeout: 3 * time.Second, } res, err := cl.Do(req) if err != nil { t.Logf("error: %s", err.Error()) } else { t.Logf("request is done with status code %d", res.StatusCode) } go func() { <-time.After(10 * time.Second) shutdown, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() srv.Shutdown(shutdown) }() wg.Wait() } func handleSomething(w http.ResponseWriter, r *http.Request) { done := make(chan error) go func() { if err := doSomething(r.Context()); err != nil { log.Println(err) done <- err } done <- nil }() select { case <-r.Context().Done(): log.Println("context is done!") return case err := <-done: if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) } } func doSomething(ctx context.Context) error { return runInContext(ctx, func() { log.Println("doing something") defer log.Println("done doing something") time.Sleep(10 * time.Second) }) } func runInContext(ctx context.Context, fn func()) error { ch := make(chan struct{}) go func() { defer close(ch) fn() }() select { case <-ctx.Done(): return ctx.Err() case <-ch: return nil } }
Ich habe gerade die bereitgestellte Lösung ein wenig überarbeitet und es sollte jetzt funktionieren. Lassen Sie sich von mir durch die Veränderungen führen.
dosomething
Funktionfunc dosomething(ctx context.context) error { fmt.printf("%v - dosomething: start\n", time.now()) select { case <-ctx.done(): fmt.printf("%v - dosomething: cancelled\n", time.now()) return ctx.err() case <-time.after(3 * time.second): fmt.printf("%v - dosomething: processed\n", time.now()) return nil } }
Es wartet auf die Stornierungseingabe oder kehrt nach einer Verzögerung von 3
Sekunden zum Anrufer zurück. Es akzeptiert einen Kontext zum Anhören.
handlesomething
Funktionfunc handlesomething(w http.responsewriter, r *http.request) { ctx := r.context() fmt.printf("%v - handlerequestctx: start\n", time.now()) done := make(chan error) go func() { if err := dosomething(ctx); err != nil { fmt.printf("%v - handlerequestctx: error %v\n", time.now(), err) done <- err } done <- nil }() select { case <-ctx.done(): fmt.printf("%v - handlerequestctx: cancelled\n", time.now()) return case err := <-done: if err != nil { fmt.printf("%v - handlerequestctx: error: %v\n", time.now(), err) w.writeheader(http.statusinternalservererror) return } fmt.printf("%v - handlerequestctx: processed\n", time.now()) } }
Die Logik hier ist Ihrer sehr ähnlich. Bei der Auswahl prüfen wir, ob der empfangene Fehler nil
ist und geben dem Anrufer entsprechend den richtigen http-Statuscode zurück. Wenn wir eine Abbrucheingabe erhalten, brechen wir alle Kontextketten ab.
testhandler
Funktionfunc TestHandler(t *testing.T) { r := mux.NewRouter() r.HandleFunc("/demo", handleSomething) srv := http.Server{ Addr: ":8000", Handler: r, } var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() if err := srv.ListenAndServe(); err != nil { fmt.Println(err.Error()) } }() ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) // request canceled // ctx, cancel := context.WithTimeout(ctx, 5*time.Second) // request processed defer cancel() req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8000/demo", nil) client := http.Client{} res, err := client.Do(req) if err != nil { fmt.Println(err.Error()) } else { fmt.Printf("res status code: %d\n", res.StatusCode) } srv.Shutdown(ctx) wg.Wait() }
Hier starten wir einen http-Server und übergeben http.client
向它发出 http 请求。可以看到有两条语句来设置上下文超时。如果您使用带有注释 // request canceled
dann wird alles abgebrochen, andernfalls wird die Anfrage bearbeitet, wenn man einen anderen verwendet.
Ich hoffe, das klärt Ihre Frage!
Das obige ist der detaillierte Inhalt vonMüssen wir beim Schreiben von HTTP-Handlern auf die Stornierung des Anforderungskontexts achten?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!