ホームページ > バックエンド開発 > Golang > Golang で応答をストリーミングし、`http.ResponseWriter` のバッファリング制限を克服するにはどうすればよいですか?

Golang で応答をストリーミングし、`http.ResponseWriter` のバッファリング制限を克服するにはどうすればよいですか?

Patricia Arquette
リリース: 2024-12-20 06:40:18
オリジナル
322 人が閲覧しました

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

Golang でのストリーミング レスポンス: バッファリングされた ResponseWriter ヒッチ

Golang で Web アプリケーションを作成する場合、http の動作を理解することが不可欠です。レスポンスライター。デフォルトでは、応答はバッファリングされます。つまり、リクエストが完全に処理されると、データが収集され、ブロック単位で送信されます。ただし、クライアントに 1 行ずつ応答をストリーミングしたり、バッファリング容量を超える大きな出力を処理したりするシナリオでは、この動作が障害になります。

次の例を考えてみましょう。

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")
}
ログイン後にコピー

クライアントの観点からは、「データの 1 行目を送信中」メッセージと「データの 2 行目送信」メッセージは別々に受信される必要があります。ただし、バッファリングにより、両方の行が集約されて同時に送信されます。

この問題を解決するには、各書き込み操作の後に 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)
      }
    }
  }
}
ログイン後にコピー

このアプローチでは、ゴルーチンはコマンド出力を非同期的に処理し、それをチャンネル。次に、handleCmdOutput 関数は出力を ResponseWriter に遅延ストリーミングし、書き込み操作のたびにフラッシュします。

Flusher インターフェイスを活用し、代替アプローチを検討することで、データを効果的にクライアントにストリーミングし、Golang の ResponseWriter のバッファリング制限を克服できます。

以上がGolang で応答をストリーミングし、`http.ResponseWriter` のバッファリング制限を克服するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート