>백엔드 개발 >Golang >Golang에서 응답을 스트리밍하고 'http.ResponseWriter' 버퍼링 제한을 극복하는 방법은 무엇입니까?

Golang에서 응답을 스트리밍하고 'http.ResponseWriter' 버퍼링 제한을 극복하는 방법은 무엇입니까?

Patricia Arquette
Patricia Arquette원래의
2024-12-20 06:40:18324검색

How to Stream Responses in Golang and Overcome `http.ResponseWriter` Buffering Limitations?

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.