Maison > développement back-end > Golang > le corps du texte

Lors de l'écriture de gestionnaires http, devons-nous écouter l'annulation du contexte de la demande ?

WBOY
Libérer: 2024-02-08 23:03:28
avant
1175 Les gens l'ont consulté

Lors de lécriture de gestionnaires http, devons-nous écouter lannulation 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.

Contenu de la question

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
}
Copier après la connexion

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
    }
}
Copier après la connexion

Solution de contournement

Je viens de refactoriser un peu la solution fournie et elle devrait fonctionner maintenant. Laissez-moi vous guider à travers les changements.

dosomething fonction

func 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
    }
}
Copier après la connexion

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 fonction

func 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())
    }
}
Copier après la connexion

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 fonction

func 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()
}
Copier après la connexion

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!

source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!