Menstrim Respons dalam Golang: Halangan Penimbal ResponseWriter
Apabila mencipta aplikasi web di Golang, adalah penting untuk memahami gelagat http. ResponseWriter. Secara lalai, Respons ditimbal, yang bermaksud data dikumpul dan dihantar dalam blok sebaik sahaja permintaan diproses sepenuhnya. Walau bagaimanapun, dalam senario di mana anda ingin menstrim respons kepada pelanggan baris demi baris atau mengendalikan output besar yang melebihi kapasiti penimbalan, tingkah laku ini menjadi penghalang.
Pertimbangkan contoh berikut:
func handle(res http.ResponseWriter, req *http.Request) { fmt.Fprintf(res, "sending first line of data") sleep(10) // Simulation of a long-running process fmt.Fprintf(res, "sending second line of data") }
Dari perspektif pelanggan, mesej "menghantar baris pertama data" dan "menghantar baris data kedua" harus diterima secara berasingan. Walau bagaimanapun, disebabkan penimbalan, kedua-dua baris akan diagregatkan dan dihantar serentak.
Untuk menyelesaikan isu ini, seseorang boleh mengepam ResponseWriter secara manual selepas setiap operasi tulis. Ini boleh dicapai menggunakan antara muka Flusher, seperti yang ditunjukkan di bawah:
func handle(res http.ResponseWriter, req *http.Request) { fmt.Fprintf(res, "sending first line of data") if f, ok := res.(http.Flusher); ok { f.Flush() } sleep(10) // Simulation of a long-running process fmt.Fprintf(res, "sending second line of data") }
Dengan pengubahsuaian ini, respons akan distrim secara progresif kepada klien, seperti yang dikehendaki.
Senario Lanjutan : Perintah Luaran Paip
Walau bagaimanapun, dalam situasi tertentu, curahan manual mungkin tidak mencukupi. Pertimbangkan senario di mana anda ingin menyalurkan output arahan luaran kepada pelanggan. Perintah menjana sejumlah besar data, melebihi kapasiti penimbalan.
pipeReader, pipeWriter := io.Pipe() cmd.Stdout = pipeWriter cmd.Stderr = pipeWriter go writeCmdOutput(res, pipeReader) err := cmd.Run() pipeWriter.Close() // Function to write command output to ResponseWriter func writeCmdOutput(res http.ResponseWriter, pipeReader *io.PipeReader) { buffer := make([]byte, BUF_LEN) for { n, err := pipeReader.Read(buffer) if err != nil { pipeReader.Close() break } data := buffer[0:n] res.Write(data) if f, ok := res.(http.Flusher); ok { f.Flush() } // Reset buffer for i := 0; i < n; i++ { buffer[i] = 0 } } }
Dalam kes ini, adalah perlu untuk "autoflush" ResponseWriter untuk memastikan data distrim kepada klien tanpa berlengah-lengah. Ini boleh dicapai menggunakan coretan kod yang disediakan.
Penyelesaian Alternatif
Sebagai alternatif kepada terus memaipkan output arahan luaran, seseorang boleh menggunakan pendekatan berasaskan saluran:
// Create a channel to communicate with the goroutine outputChan := make(chan string) // Start a goroutine to process the command output go func() { scanner := bufio.NewScanner(cmd.Stdout) for scanner.Scan() { outputChan <- scanner.Text() } if err := scanner.Err(); err != nil { log.Fatal(err) } close(outputChan) // Notify that all output has been processed }() // Stream output to ResponseWriter lazily func handleCmdOutput(res http.ResponseWriter, req *http.Request) { if f, ok := res.(http.Flusher); ok { for { select { case output := <-outputChan: res.Write([]byte(output + "\n")) f.Flush() default: time.Sleep(10 * time.Millisecond) } } } }
Dalam pendekatan ini, goroutine memproses output arahan secara tidak segerak dan menghantarnya ke saluran. Fungsi handleCmdOutput kemudian secara malas menstrim output ke ResponseWriter, mengepam selepas setiap operasi tulis.
Dengan memanfaatkan antara muka Flusher dan meneroka pendekatan alternatif, anda boleh menstrim data dengan berkesan kepada pelanggan dan mengatasi had penimbalan dalam ResponseWriter Golang.
Atas ialah kandungan terperinci Bagaimana untuk Menstrim Respons dalam Golang dan Mengatasi Had Penimbalan `http.ResponseWriter`?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!