it-swarm.dev

Golang leyó de la tubería lee toneladas de datos

Estoy tratando de leer un archivo que está siendo alquitranado, transmitido, a stdin, pero de alguna manera estoy leyendo lejos más datos en la tubería de los que envía tar.

Ejecuto mi comando así:

tar -cf - somefolder | ./my-go-binary

El código fuente es así:

package main

import (
    "bufio"
    "io"
    "log"
    "os"
)

// Read from standard input
func main() {
    reader := bufio.NewReader(os.Stdin)
    // Read all data from stdin, processing subsequent reads as chunks.
    parts := 0
    for {
        parts++
        data := make([]byte, 4<<20) // Read 4MB at a time
        _, err := reader.Read(data)
        if err == io.EOF {
            break
        } else if err != nil {
            log.Fatalf("Problems reading from input: %s", err)
        }
    }
    log.Printf("Total parts processed: %d\n", parts)
}

¡Para una carpeta tarred de 100MB, obtengo 1468 fragmentos de 4MB (eso es 6.15GB)! Además, no parece importar cuán grande sea el data []byte array es: si configuro el tamaño del fragmento en 40 MB, todavía obtengo ~ 1400 fragmentos de datos de 40 MB, lo que no tiene ningún sentido.

¿Hay algo que deba hacer para leer datos de os.Stdin correctamente con Go?

13
atp

Tu código es ineficiente. Está asignando e inicializando data cada vez a través del ciclo.

for {
    data := make([]byte, 4<<20) // Read 4MB at a time
}

El código para su reader como io.Reader Es incorrecto. Por ejemplo, ignora el número de bytes leídos por _, err := reader.Read(data) y no maneja los errores err correctamente.

Paquete io

import "io" 

escriba Reader

type Reader interface {
        Read(p []byte) (n int, err error)
}

Reader es la interfaz que envuelve el método de lectura básico.

Lecturas de lectura hasta len (p) bytes en p. Devuelve el número de bytes leídos (0 <= n <= len (p)) y cualquier error encontrado. Incluso si Read devuelve n <len (p), puede usar todo p como espacio de memoria virtual durante la llamada. Si hay algunos datos disponibles pero no len (p) bytes, Read convencionalmente devuelve lo que está disponible en lugar de esperar más.

Cuando Read encuentra un error o una condición de fin de archivo después de leer con éxito n> 0 bytes, devuelve el número de bytes leídos. Puede devolver el error (no nulo) de la misma llamada o devolver el error (yn == 0) de una llamada posterior. Una instancia de este caso general es que un lector que devuelve un número de bytes distinto de cero al final de la secuencia de entrada puede devolver err == EOF o err == nil. La próxima lectura debería devolver 0, EOF independientemente).

Las personas que llaman siempre deben procesar los n> 0 bytes devueltos antes de considerar el error err. Hacerlo correctamente maneja los errores de E/S que ocurren después de leer algunos bytes y también los dos comportamientos permitidos EOF.

Se desaconseja que las implementaciones de lectura devuelvan un conteo de bytes cero con un error nulo, excepto cuando len (p) == 0. Las personas que llaman deben tratar un retorno de 0 y nulo como indicativo de que no sucedió nada; en particular no indica EOF.

Las implementaciones no deben retener p.

Aquí hay un programa de lectura de archivos modelo que se ajusta a la interfaz io.Reader:

package main

import (
    "bufio"
    "io"
    "log"
    "os"
)

func main() {
    nBytes, nChunks := int64(0), int64(0)
    r := bufio.NewReader(os.Stdin)
    buf := make([]byte, 0, 4*1024)
    for {
        n, err := r.Read(buf[:cap(buf)])
        buf = buf[:n]
        if n == 0 {
            if err == nil {
                continue
            }
            if err == io.EOF {
                break
            }
            log.Fatal(err)
        }
        nChunks++
        nBytes += int64(len(buf))
        // process buf
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }
    }
    log.Println("Bytes:", nBytes, "Chunks:", nChunks)
}

Salida:

 29/11/2014 10:00:05 Bytes: 5589891 Trozos: 1365 
31
peterSO

Lea la documentación para Leer:

Leer lee datos en p. Devuelve el número de bytes leídos en p. Llama a Leer como máximo una vez en el Reader subyacente, por lo tanto, n puede ser menor que len (p). En EOF, el recuento será cero y err será io.EOF.

No estás leyendo 4 MB a la vez. Está proporcionando espacio de búfer y descartando el número entero que le habría dicho cuánto leyó realmente la lectura. El espacio del búfer es el máximo, pero la mayoría de las veces parece que se leen 128k por llamada, al menos en mi sistema. Pruébalo tú mismo:

// Read from standard input
func main() {
    reader := bufio.NewReader(os.Stdin)
    // Read all data from stdin, passing the data as parts into the channel
    // for processing.
    parts := 0
    for {
        parts++
        data := make([]byte, 4<<20) // Read 4MB at a time
        amount , err := reader.Read(data)
        // WILL NOT BE 4MB!
        log.Printf("Read: %v\n", amount)
        if err == io.EOF {
            break
        } else if err != nil {
            log.Fatalf("Problems reading from input: %s", err)
        }
    }
    log.Printf("Total parts processed: %d\n", parts)
}

Debe implementar la lógica para manejar las cantidades de lectura variables.

5
user918176