Maison > Article > développement back-end > Lors de l'écriture de gestionnaires http, devons-nous écouter l'annulation du contexte de la demande ?
php Editor Xinyi Lors du traitement des requêtes HTTP, la question de savoir s'il est nécessaire de surveiller l'annulation du contexte de la requête est une question courante. Dans le développement réel, il n'est généralement pas nécessaire de surveiller explicitement l'annulation du contexte de la requête, car l'environnement d'exécution PHP gérera automatiquement le travail de libération des ressources associé. Cependant, dans certains cas particuliers, par exemple lorsque vous devez libérer manuellement des ressources ou effectuer certaines opérations de nettoyage, l'écoute de l'annulation du contexte de demande peut être un moyen efficace. Par conséquent, la nécessité ou non d’écouter l’annulation du contexte de demande dépend des exigences métier spécifiques et des scénarios de développement. Dans la plupart des cas, nous pouvons compter en toute sécurité sur le mécanisme de gestion automatique des ressources de PHP.
Supposons que j'écrive un gestionnaire http qui effectue d'autres opérations avant de renvoyer la réponse, dois-je configurer un écouteur pour vérifier si le contexte de la requête http a été annulé ? afin qu'il puisse revenir immédiatement, ou existe-t-il un autre moyen de quitter le gestionnaire lorsque le contexte de la demande est annulé ?
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 }
J'ai essayé de le tester, mais une fois le contexte annulé, la fonction dosomething
ne s'arrête pas et s'exécute toujours en arrière-plan.
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 } }
Je viens de refactoriser un peu la solution fournie et elle devrait fonctionner maintenant. Laissez-moi vous guider à travers les changements.
dosomething
fonctionfunc 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 } }
Il attend l'entrée d'annulation ou revient à l'appelant après un délai de 3
secondes. Il accepte un contexte à écouter.
handlesomething
fonctionfunc 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()) } }
La logique ici est très similaire à la vôtre. Dans select, nous vérifions si l'erreur reçue est nil
et renvoyons le code d'état http correct à l'appelant en conséquence. Si nous recevons une entrée d'annulation, nous annulons toutes les chaînes de contexte.
testhandler
fonctionfunc 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() }
Ici on démarre un serveur http et on passe http.client
向它发出 http 请求。可以看到有两条语句来设置上下文超时。如果您使用带有注释 // request canceled
puis tout sera annulé, sinon si vous en utilisez un autre la demande sera traitée.
J'espère que cela clarifie votre question!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!