Golang читает из трубы читает тонны данных
Я пытаюсь прочитать архив, который передается в потоковом режиме в stdin, но я каким-то образом читаю гораздо больше данных в конвейере, чем отправляет tar.
Я запускаю свою команду так:
tar -cf - somefolder | ./my-go-binary
Исходный код выглядит так:
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)
}
Для заархивированной папки размером 100 МБ я получаю 1468 блоков по 4 МБ (это 6,15 ГБ)! Кроме того, не имеет значения, насколько велика data []byte
массив: если я установлю размер чанка 40 МБ, я все равно получу ~1400 чанков данных 40 МБ, что не имеет никакого смысла.
Есть ли что-то, что мне нужно сделать, чтобы прочитать данные из os.Stdin
правильно с го?
2 ответа
Ваш код неэффективен. Это распределение и инициализация data
каждый раз через цикл.
for {
data := make([]byte, 4<<20) // Read 4MB at a time
}
Код для вашего reader
как io.Reader
неправильно. Например, вы игнорируете количество байтов, прочитанных _, err := reader.Read(data)
и вы не справляетесь err
ошибки правильно.
import "io"
type Reader interface { Read(p []byte) (n int, err error) }
Reader - это интерфейс, который оборачивает базовый метод Read.
Read читает до len(p) байтов в p. Он возвращает количество прочитанных байтов (0 <= n <= len(p)) и обнаруженную ошибку. Даже если Read возвращает n
Когда Read успешно обнаруживает ошибку или условие конца файла после успешного чтения n > 0 байтов, он возвращает количество прочитанных байтов. Он может вернуть (не ноль) ошибку из того же вызова или вернуть ошибку (и n == 0) из последующего вызова. Примером этого общего случая является то, что Reader, возвращающий ненулевое число байтов в конце входного потока, может возвратить либо err == EOF, либо err == nil. Следующее чтение должно вернуть 0, EOF независимо.
Вызывающие абоненты должны всегда обрабатывать n > 0 байтов, возвращаемых до рассмотрения ошибки err. Это правильно обрабатывает ошибки ввода-вывода, возникающие после чтения некоторых байтов, а также оба допустимых поведения EOF.
Реализации Read не рекомендуется возвращать счетчик нулевых байтов с ошибкой nil, за исключением случаев, когда len(p) == 0. Вызывающие операторы должны обрабатывать возврат 0 и nil как указание того, что ничего не произошло; в частности это не указывает на EOF.
Реализации не должны сохранять с.
Вот программа чтения файла модели, которая соответствует 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)
}
Выход:
2014/11/29 10:00:05 Байты: 5589891 Куски: 1365
Прочитайте документацию для Read:
Read читает данные в p. Возвращает количество байтов, считанных в p. Он вызывает Read не более одного раза в базовом Reader, следовательно, n может быть меньше len(p). В EOF количество будет равно нулю, а err будет равно io.EOF.
Вы не читаете 4 МБ одновременно. Вы предоставляете буферное пространство и отбрасываете целое число, которое сообщило бы вам, сколько фактически прочитано Read. Буферное пространство является максимальным, но чаще всего кажется, что 128 Кбайт считывается за вызов, по крайней мере, в моей системе. Попробуйте сами:
// 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)
}
Вы должны реализовать логику для обработки различных количеств чтения.