首页 >后端开发 >Golang >如何在 Golang 中传输响应并克服'http.ResponseWriter”缓冲限制?

如何在 Golang 中传输响应并克服'http.ResponseWriter”缓冲限制?

Patricia Arquette
Patricia Arquette原创
2024-12-20 06:40:18293浏览

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

Golang 中的流式响应:缓冲的 ResponseWriter 挂钩

在 Golang 中创建 Web 应用程序时,了解 http.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)
      }
    }
  }
}

在这种方法中,goroutine 异步处理命令输出并将其发送到通道。然后,handleCmdOutput 函数将输出惰性地流式传输到 ResponseWriter,在每次写入操作后刷新。

通过利用 Flusher 接口并探索替代方法,您可以有效地将数据流式传输到客户端并克服 Golang ResponseWriter 中的缓冲限制。

以上是如何在 Golang 中传输响应并克服'http.ResponseWriter”缓冲限制?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn