Heim >Backend-Entwicklung >Golang >Wie streame ich Antworten in Golang und überwinde die Puffereinschränkungen von „http.ResponseWriter'?

Wie streame ich Antworten in Golang und überwinde die Puffereinschränkungen von „http.ResponseWriter'?

Patricia Arquette
Patricia ArquetteOriginal
2024-12-20 06:40:18293Durchsuche

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

Streaming-Antworten in Golang: Eine gepufferte ResponseWriter-Hitch

Beim Erstellen von Webanwendungen in Golang ist es wichtig, das Verhalten von http zu verstehen. ResponseWriter. Standardmäßig werden Antworten gepuffert, was bedeutet, dass Daten gesammelt und in Blöcken gesendet werden, sobald die Anfrage vollständig verarbeitet ist. In Szenarien, in denen Sie Antworten Zeile für Zeile an Clients streamen oder große Ausgaben verarbeiten möchten, die die Pufferkapazität überschreiten, wird dieses Verhalten jedoch zu einem Hindernis.

Betrachten Sie das folgende Beispiel:

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")
}

Aus Sicht des Clients sollten die Nachrichten „Senden der ersten Datenzeile“ und „Senden der zweiten Datenzeile“ getrennt empfangen werden. Aufgrund der Pufferung werden jedoch beide Zeilen aggregiert und gleichzeitig gesendet.

Um dieses Problem zu beheben, kann man den ResponseWriter nach jedem Schreibvorgang manuell leeren. Dies kann mithilfe der Flusher-Schnittstelle erreicht werden, wie unten gezeigt:

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")
}

Mit dieser Änderung wird die Antwort nach und nach nach Wunsch an den Client gestreamt.

Erweitertes Szenario : Externe Befehle weiterleiten

In bestimmten Situationen reicht die manuelle Spülung jedoch möglicherweise nicht aus. Stellen Sie sich ein Szenario vor, in dem Sie die Ausgabe eines externen Befehls an den Client weiterleiten möchten. Der Befehl generiert eine große Datenmenge, die die Pufferkapazität überschreitet.

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
    }
  }
}

In diesem Fall ist es notwendig, den ResponseWriter „automatisch zu leeren“, um sicherzustellen, dass die Daten ohne Verzögerung an den Client gestreamt werden. Dies kann mit dem bereitgestellten Code-Snippet erreicht werden.

Alternative Lösungen

Als Alternative zur direkten Weiterleitung der Ausgabe des externen Befehls kann man einen kanalbasierten Ansatz verwenden:

// 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)
      }
    }
  }
}

Bei diesem Ansatz verarbeitet die Goroutine die Befehlsausgabe asynchron und sendet sie an den Kanal. Die Funktion „handleCmdOutput“ streamt die Ausgabe dann langsam an den ResponseWriter und leert sie nach jedem Schreibvorgang.

Durch die Nutzung der Flusher-Schnittstelle und die Erkundung alternativer Ansätze können Sie Daten effektiv an Clients streamen und die Pufferbeschränkung im ResponseWriter von Golang überwinden.

Das obige ist der detaillierte Inhalt vonWie streame ich Antworten in Golang und überwinde die Puffereinschränkungen von „http.ResponseWriter'?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn