Maison >développement back-end >Golang >Comment diffuser des réponses dans Golang et surmonter les limitations de mise en mémoire tampon « http.ResponseWriter » ?

Comment diffuser des réponses dans Golang et surmonter les limitations de mise en mémoire tampon « http.ResponseWriter » ?

Patricia Arquette
Patricia Arquetteoriginal
2024-12-20 06:40:18293parcourir

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

Streaming de réponses dans Golang : un accroc de ResponseWriter tamponné

Lors de la création d'applications Web dans Golang, il est essentiel de comprendre le comportement du http. RéponseWriter. Par défaut, les réponses sont mises en mémoire tampon, ce qui signifie que les données sont collectées et envoyées par blocs une fois la demande entièrement traitée. Cependant, dans les scénarios où vous souhaitez diffuser les réponses aux clients ligne par ligne ou gérer des sorties volumineuses qui dépassent la capacité de mise en mémoire tampon, ce comportement devient un obstacle.

Considérez l'exemple suivant :

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

Du point de vue du client, les messages « envoi de la première ligne de données » et « envoi de la deuxième ligne de données » doivent être reçus séparément. Cependant, en raison de la mise en mémoire tampon, les deux lignes seront agrégées et envoyées simultanément.

Pour résoudre ce problème, on peut vider manuellement le ResponseWriter après chaque opération d'écriture. Ceci peut être réalisé à l'aide de l'interface Flusher, comme démontré ci-dessous :

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

Avec cette modification, la réponse sera progressivement diffusée au client, comme souhaité.

Scénario avancé : Commandes externes de tuyauterie

Cependant, dans certaines situations, le rinçage manuel peut ne pas suffire. Considérez un scénario dans lequel vous souhaitez transmettre la sortie d’une commande externe au client. La commande génère une grande quantité de données, dépassant la capacité de mise en mémoire tampon.

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

Dans ce cas, il devient nécessaire de « vider automatiquement » le ResponseWriter pour garantir que les données sont transmises au client sans délai. Ceci peut être réalisé en utilisant l'extrait de code fourni.

Solutions alternatives

Au lieu de rediriger directement la sortie de la commande externe, vous pouvez utiliser une approche basée sur les canaux :

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

Dans cette approche, la goroutine traite de manière asynchrone la sortie de la commande et l'envoie au canal. La fonction handleCmdOutput diffuse ensuite paresseusement la sortie vers ResponseWriter, en vidant après chaque opération d'écriture.

En tirant parti de l'interface Flusher et en explorant des approches alternatives, vous pouvez efficacement diffuser des données vers les clients et surmonter la limitation de mise en mémoire tampon dans ResponseWriter de Golang.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn