php Editor Xinyi Apabila memproses permintaan HTTP, sama ada perlu untuk memantau pembatalan konteks permintaan adalah soalan biasa. Dalam pembangunan sebenar, biasanya tidak perlu memantau pembatalan konteks permintaan secara eksplisit, kerana persekitaran berjalan PHP secara automatik akan mengendalikan kerja keluaran sumber yang berkaitan. Walau bagaimanapun, dalam beberapa kes khas, seperti apabila anda perlu mengeluarkan sumber secara manual atau melakukan beberapa operasi pembersihan, mendengar pembatalan konteks permintaan boleh menjadi cara yang berkesan. Oleh itu, sama ada anda perlu mendengar pembatalan konteks permintaan bergantung pada keperluan perniagaan khusus dan senario pembangunan. Untuk kebanyakan kes, kami boleh bergantung pada mekanisme pengurusan sumber automatik PHP dengan selamat.
Andaikan saya sedang menulis pengendali http yang menjalankan operasi lain sebelum mengembalikan respons, adakah saya perlu menyediakan pendengar untuk menyemak sama ada konteks permintaan http telah dibatalkan? supaya ia boleh kembali serta-merta, atau adakah terdapat cara lain untuk keluar dari pengendali apabila konteks permintaan dibatalkan?
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 }
Saya cuba mengujinya, tetapi selepas konteks dibatalkan, fungsi dosomething
tidak berhenti dan masih berjalan di latar belakang.
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 } }
Saya baru sahaja memfaktorkan semula penyelesaian yang disediakan dan ia sepatutnya berfungsi sekarang. Biar saya membimbing anda melalui perubahan.
dosomething
fungsifunc 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 } }
Ia menunggu input pembatalan, atau kembali kepada pemanggil selepas kelewatan selama 3
saat. Ia menerima konteks untuk didengari.
handlesomething
fungsifunc 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()) } }
Logik di sini sangat serupa dengan anda. Dalam pilihan kami menyemak sama ada ralat yang diterima ialah nil
dan mengembalikan kod status http yang betul kepada pemanggil dengan sewajarnya. Jika kami menerima input batal, kami membatalkan semua rantaian konteks.
testhandler
fungsifunc 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() }
Di sini kita mulakan pelayan http dan lulus http.client
向它发出 http 请求。可以看到有两条语句来设置上下文超时。如果您使用带有注释 // request canceled
maka semuanya akan dibatalkan, sebaliknya jika anda menggunakan yang lain permintaan akan diproses.
Saya harap ini menjelaskan soalan anda!
Atas ialah kandungan terperinci Apabila menulis pengendali http, adakah kita perlu mendengar pembatalan konteks permintaan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!