Golang의 스트리밍 응답: 버퍼링된 ResponseWriter 장애
Golang에서 웹 애플리케이션을 생성할 때 http의 동작을 이해하는 것이 중요합니다. 응답작성기. 기본적으로 응답은 버퍼링됩니다. 즉, 요청이 완전히 처리되면 데이터가 블록 단위로 수집되어 전송됩니다. 그러나 클라이언트에 대한 응답을 한 줄씩 스트리밍하거나 버퍼링 용량을 초과하는 대용량 출력을 처리하려는 시나리오에서는 이 동작이 방해가 됩니다.
다음 예를 고려하세요.
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") }
클라이언트 입장에서는 "첫 번째 데이터 줄 전송"과 "두 번째 데이터 줄 전송" 메시지를 별도로 수신해야 합니다. 그러나 버퍼링으로 인해 두 줄이 모두 집계되어 동시에 전송됩니다.
이 문제를 해결하려면 각 쓰기 작업 후에 ResponseWriter를 수동으로 플러시할 수 있습니다. 이는 아래 설명된 것처럼 Flusher 인터페이스를 사용하여 달성할 수 있습니다.
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") }
이 수정을 통해 응답은 원하는 대로 클라이언트에 점진적으로 스트리밍됩니다.
고급 시나리오 : 외부 명령 파이핑
그러나 특정 상황에서는 수동 플러시만으로는 충분하지 않을 수 있습니다. 외부 명령의 출력을 클라이언트로 파이프하려는 시나리오를 고려하십시오. 이 명령은 버퍼링 용량을 초과하는 많은 양의 데이터를 생성합니다.
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 } } }
이 경우 데이터가 지연 없이 클라이언트로 스트리밍되도록 ResponseWriter를 "자동 플러시"해야 합니다. 이는 제공된 코드 조각을 사용하여 달성할 수 있습니다.
대체 솔루션
외부 명령의 출력을 직접 파이핑하는 대신 채널 기반 접근 방식을 사용할 수 있습니다.
// 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) } } } }
이 접근 방식에서 고루틴은 명령 출력을 비동기적으로 처리하여 채널. 그런 다음 handlerCmdOutput 함수는 출력을 ResponseWriter로 느리게 스트리밍하여 각 쓰기 작업 후에 플러시합니다.
Flusher 인터페이스를 활용하고 대체 접근 방식을 탐색하면 클라이언트에 데이터를 효과적으로 스트리밍하고 Golang ResponseWriter의 버퍼링 제한을 극복할 수 있습니다.
위 내용은 Golang에서 응답을 스트리밍하고 'http.ResponseWriter' 버퍼링 제한을 극복하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!