亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何中斷 HTTP 處理程序?

如何中斷 HTTP 處理程序?

Go
飲歌長嘯 2023-07-31 16:30:01
假設我有一個像這樣的 http 處理程序:func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {        fmt.Fprintf(w, "Hello World!")        // run code that takes a long time here        // Executing dd command with cmd.Exec..., etc.})如果用戶刷新頁面或以其他方式終止請求而不運行后續代碼,有沒有辦法可以中斷此功能,我該如何做?我嘗試這樣做:notify := r.Context().Done()go func() {    <-notify     println("Client closed the connection")     s.downloadCleanup()     return}()但每當我中斷它之后的代碼仍然會運行。
查看完整描述

2 回答

?
侃侃爾雅

TA貢獻1801條經驗 獲得超16個贊

沒有辦法從該 goroutine外部的任何代碼中強制拆除該 goroutine。


因此,真正中斷處理的唯一方法是定期檢查客戶端是否消失(或者是否有另一個信號來停止處理)。


基本上這相當于構建你的處理程序是這樣的


func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {

    fmt.Fprintf(w, "Hello World!")


    done := r.Context().Done()


    // Check wheteher we're done


    // do some small piece of stuff


    // check whether we're done


    // do another small piece of stuff


    // …rinse, repeat

})

現在,檢查是否有內容寫入通道但不阻塞操作的方法是使用“使用默認值選擇”習慣用法:


select {

    case <- done:

        // We're done

    default:

}

當且僅當done被寫入或被關閉(上下文就是這種情況)時,此狀態才會執行“// We're done”塊中的代碼,否則default執行分支中的空塊。


所以我們可以將其重構為類似的東西


func ReallyLongFunction(w http.ResponseWriter, r *http.Request) {

    fmt.Fprintf(w, "Hello World!")


    done := r.Context().Done()

    closed := func () bool {

        select {

            case <- done:

                return true

            default:

                return false

        }

    }


    if closed() {

        return

    }


    // do some small piece of stuff


    if closed() {

        return

    }


    // do another small piece of stuff


    // …rinse, repeat

})

停止在 HTTP 處理程序中啟動的外部進程

為了解決OP的評論......

os/exec.Cmd類型具有Process字段,該字段具有類型os.Process并且該類型支持Kill強制關閉正在運行的進程的方法。

唯一的問題是exec.Cmd.Run阻塞直到進程退出,因此正在執行的 goroutine 無法執行其他代碼,并且如果exec.Cmd.Run在 HTTP 處理程序中調用,則無法取消它。

如何最好地處理以這種異步方式運行程序在很大程度上取決于進程本身的組織方式,但我會這樣滾動:

  1. 在處理程序中,準備進程,然后使用exec.Cmd.Start(而不是Run)啟動它。

    檢查已返回的錯誤值Start:如果是,則nil 進程已成功啟動。否則,以某種方式將失敗傳達給客戶端并退出處理程序。

    一旦知道流程已經啟動,該exec.Cmd值的一些字段就會填充與流程相關的信息;特別感興趣的是Process類型為 的字段 os.Process:該類型具有Kill可用于強制關閉進程的方法。

  2. 啟動一個 goroutine 并向其傳遞該exec.Cmd值和某種合適類型的通道(見下文)。

    該 Goroutine 應該調用Wait它,一旦返回,它應該通過該通道將該事實傳達回原始 Goroutine。

    到底要通信什么,是一個懸而未決的問題,因為它取決于您是否想要收集進程寫入其標準輸出和錯誤流的內容和/或可能是與進程活動相關的一些其他數據。

    發送數據后,該 goroutine 退出。

  3. 主 goroutine(執行處理程序)應該exec.Cmd.Process.Kill在檢測到處理程序應該終止時調用。

    終止進程最終會解除對 goroutine 的阻塞,該 goroutine在進程退出時Wait以相同的值執行。exec.Cmd

    終止進程后,處理程序 goroutine 會在通道上等待監聽該進程的 goroutine 的回復。處理程序對該數據執行某些操作(可能是記錄它或其他什么)并退出。


查看完整回答
反對 回復 2023-07-31
?
慕萊塢森

TA貢獻1810條經驗 獲得超4個贊

您應該從內部取消 goroutine,因此對于長時間的計算任務,您可以提供檢查點,以停止并檢查取消:


這是服務器的測試代碼,該代碼具有例如長計算任務和取消檢查點:


package main


import (

? ? "fmt"

? ? "io"

? ? "log"

? ? "net/http"

? ? "time"

)


func main() {

? ? http.HandleFunc(`/`, func(w http.ResponseWriter, r *http.Request) {

? ? ? ? ctx := r.Context()


? ? ? ? log.Println("wait a couple of seconds ...")

? ? ? ? for i := 0; i < 10; i++ { // long calculation

? ? ? ? ? ? select {

? ? ? ? ? ? case <-ctx.Done():

? ? ? ? ? ? ? ? log.Println("Client closed the connection:", ctx.Err())

? ? ? ? ? ? ? ? return

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? fmt.Print(".")

? ? ? ? ? ? ? ? time.Sleep(200 * time.Millisecond) // long calculation

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? io.WriteString(w, `Hi`)

? ? ? ? log.Println("Done.")

? ? })

? ? log.Println(http.ListenAndServe(":8081", nil))

}

這是超時的客戶端代碼:


package main


import (

? ? "io/ioutil"

? ? "log"

? ? "net/http"

? ? "time"

)


func main() {

? ? log.Println("HTTP GET")

? ? client := &http.Client{

? ? ? ? Timeout: 1 * time.Second,

? ? }

? ? r, err := client.Get(`http://127.0.0.1:8081/`)

? ? if err != nil {

? ? ? ? log.Fatal(err)

? ? }

? ? defer r.Body.Close()

? ? bs, err := ioutil.ReadAll(r.Body)

? ? if err != nil {

? ? ? ? log.Fatal(err)

? ? }

? ? log.Println("HTTP Done.")

? ? log.Println(string(bs))

}

您可以使用普通瀏覽器檢查是否取消,或者關閉、刷新、斷開連接,或者...,取消。



查看完整回答
反對 回復 2023-07-31
  • 2 回答
  • 0 關注
  • 166 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號