DevOps Bootcamp · Lektion

Streaming-Pipelines und Named Pipes für hohen Durchsatz

Verwenden Sie FIFOs und Prozesssubstitution, um Daten ohne Zwischen­dateien zwischen Verarbeitungsschritten zu streamen.

Lektion 4 von 413 Schritte

Streaming-Pipelines und Named Pipes für hohen Durchsatz ist eine kostenlose DevOps Bootcamp-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des DevOps Bootcamp-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der DevOps Bootcamp-Kurs umfasst insgesamt 4 Lektionen.

Warum temporäre Dateien den Durchsatz verringern

Wenn Sie Befehle wie sort file.txt > tmp.txt && uniq tmp.txt > result.txt verketten, zahlen Sie einen versteckten Preis: Schreibvorgänge auf die Festplatte, Lesevorgänge von der Festplatte, und die Pipeline wartet, bis die erste Stufe vollständig abgeschlossen ist, bevor die nächste beginnt.

Streaming-Pipelines vermeiden diesen Aufwand. Die Daten fließen stufenweise und nebenläufig direkt vom Erzeuger zum Verbraucher im Speicher. Das ist die grundlegende Idee hinter Unix-Pipes — und Named Pipes (FIFOs) führen sie noch weiter.

  • Anonyme Pipe (|): verbindet zwei direkt aufeinanderfolgende Befehle in derselben Shell-Zeile.
  • Named Pipe (FIFO): eine spezielle Datei im Dateisystem, über die voneinander unabhängige Prozesse Daten aneinander streamen können.
  • Prozesssubstitution: ermöglicht es einem Befehl, die Ausgabe eines anderen Befehls so zu behandeln, als wäre sie eine Datei.

In dieser Lektion erfahren Sie, wie Sie alle drei Möglichkeiten einsetzen, um den Durchsatz in praxisnahen Bash-Workflows zu maximieren.

Aufbau einer Streaming-Pipeline

Eine anonyme Pipe verbindet die stdout-Ausgabe eines Prozesses mit der stdin-Eingabe des nächsten. Der Kernel lässt beide Prozesse gleichzeitig laufen und verwendet dafür einen fest dimensionierten Puffer im Arbeitsspeicher (unter Linux typischerweise 64 KB).

Die entscheidende Erkenntnis lautet: Die Pipeline ist so schnell wie ihre langsamste Stufe. Ist der Erzeuger schneller, wird er bei einem vollen Puffer blockiert. Ist der Verbraucher schneller, wird er bei einem leeren Puffer blockiert. Dieser Rückstau ist eine kostenlose, automatische Flusssteuerung.

Das folgende Beispiel zählt eindeutige IP-Adressen in einem großen Zugriffsprotokoll, ohne jemals eine temporäre Datei zu schreiben. Jede Stufe läuft nebenläufig:

#!/usr/bin/env bash
# Stream a 2 GB access log — all stages run in parallel
grep '"GET' /var/log/nginx/access.log \
  | awk '{print $1}' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -20

Named Pipes mit mkfifo erstellen

Eine Named Pipe (FIFO — First In, First Out) wird mit mkfifo erstellt. Sie erscheint im Dateisystem wie eine normale Datei, aber in sie geschriebene Daten werden niemals auf der Festplatte gespeichert — sie fließen direkt zum lesenden Prozess.

Wichtige Verhaltensweisen:

  • Ein Schreibvorgang in eine FIFO blockiert, bis ein Leser sie öffnet, und umgekehrt.
  • Der FIFO-Eintrag bleibt im Dateisystem bestehen; Sie müssen ihn anschließend mit rm löschen.
  • Mehrere Schreiber sind zulässig, aber ihre Reihenfolge ist nicht garantiert.

Im folgenden Beispiel komprimiert ein Erzeuger Daten in eine FIFO, während ein Verbraucher sie gleichzeitig in S3 hochlädt — eine temporäre Datei ist nicht erforderlich.

#!/usr/bin/env bash
mkfifo /tmp/stream_pipe

# Producer: compress in background
gzip -c /var/log/syslog > /tmp/stream_pipe &

# Consumer: read from FIFO (runs in foreground)
wc -l < /tmp/stream_pipe

wait
rm /tmp/stream_pipe

Prozesssubstitution: Einen Befehl als Datei behandeln

Prozesssubstitution verwendet die Syntax <(command) oder >(command). Bash erstellt im Hintergrund eine FIFO (oder eine /dev/fd/N-Datei beziehungsweise einen Dateideskriptor) und übergibt den Pfad an den äußeren Befehl.

Das ist besonders nützlich, wenn ein Tool ein Argument mit einem Dateinamen statt stdin erwartet. Ohne Prozesssubstitution bräuchten Sie eine temporäre Datei; mit Prozesssubstitution streamen Sie direkt.

  • <(cmd) — der äußere Befehl liest die Ausgabe von cmd.
  • >(cmd) — der äußere Befehl schreibt in die Eingabe von cmd.
#!/usr/bin/env bash
# diff two sorted streams without creating temp files
diff <(sort /etc/passwd) <(sort /etc/group)

# Compare live command output against a baseline
diff <(ls /usr/bin | sort) <(cat ~/bin_baseline.txt | sort)

Tee: Einen Stream auf mehrere Verbraucher aufteilen

tee liest stdin und schreibt die Daten sowohl nach stdout als auch in eine oder mehrere Dateien. In Kombination mit Prozesssubstitution können Sie einen einzelnen Stream gleichzeitig auf mehrere Verarbeitungspipelines verteilen — ganz ohne Festplattenzugriffe.

Dieses Muster ist nützlich, wenn Sie beispielsweise Rohdaten protokollieren und gleichzeitig verarbeiten möchten.

#!/usr/bin/env bash
# Generate 100000 random numbers, then simultaneously:
#  1. compute the sum
#  2. find the maximum
#  3. count lines (saved to a variable)
seq 1 100000 \
  | tee >(awk '{s+=$1} END{print "Sum:", s}') \
        >(awk 'BEGIN{m=0} $1>m{m=$1} END{print "Max:", m}') \
  | wc -l | xargs echo "Count:"

Fan-out-Muster: Ein Erzeuger, viele Verbraucher

Wenn eine einzelne Datenquelle mehrere unabhängige Verbraucher versorgen soll, kombinieren Sie tee mit mehreren >()-Prozesssubstitutionen. Jeder Verbraucher erhält den vollständigen Stream und läuft nebenläufig.

Dadurch muss die Quelldatei nicht mehrfach gelesen werden. Bei einer 10-GB-Datei ist der Unterschied enorm — ein Festplatten-Lesevorgang statt N Lesevorgängen.

#!/usr/bin/env bash
# Read a large CSV once; simultaneously:
#  - count rows
#  - extract column 2 to a file
#  - pass column 3 to a stats script
cat large_data.csv \
  | tee \
      >(wc -l > /tmp/row_count.txt) \
      >(cut -d',' -f2 > /tmp/col2.txt) \
      >(cut -d',' -f3 | awk '{sum+=$1} END{print sum}' > /tmp/col3_sum.txt) \
  > /dev/null

echo "Rows:" $(cat /tmp/row_count.txt)
echo "Col3 sum:" $(cat /tmp/col3_sum.txt)

Fan-in-Muster: Viele Erzeuger, ein Verbraucher

Das Gegenstück zu Fan-out ist Fan-in: Mehrere unabhängige Quellen streamen in einen einzelnen Verbraucher. Named FIFOs machen dies unkompliziert.

Ein häufiger Anwendungsfall ist das Zusammenführen von Protokollstreams mehrerer Server in Echtzeit oder das Aggregieren von Teilergebnissen paralleler Worker.

Beachten Sie, dass der Verbraucher bei mehreren Schreibern eine vermischte Ausgabe erhält — das ist für zeilenorientierte Daten geeignet, bei denen jede Zeile in sich abgeschlossen ist. Wenn die Reihenfolge relevant ist, müssen Sie sie jedoch selbst verwalten.

#!/usr/bin/env bash
mkfifo /tmp/fanin_pipe

# Three producers write concurrently into the same FIFO
for host in web1 web2 web3; do
  ssh "$host" 'tail -n 500 /var/log/app.log' > /tmp/fanin_pipe &
done

# Single consumer reads all merged output
grep 'ERROR' /tmp/fanin_pipe | sort | uniq -c | sort -rn

wait
rm /tmp/fanin_pipe

mkfifo für parallele Komprimierung verwenden

Eine der praktischsten Anwendungen von FIFOs ist die parallele Komprimierung. Tools wie pigz (paralleles gzip) oder pbzip2 lesen einen Stream; Sie können die Rohdaten direkt weiterleiten, ohne zunächst eine unkomprimierte Datei anzulegen.

Das folgende Muster archiviert ein Verzeichnis, komprimiert es mit allen CPU-Kernen und streamt das Ergebnis gleichzeitig an einen Remote-Host:

#!/usr/bin/env bash
# Tar + parallel compress + stream to remote — no temp files
# Requires: pigz (parallel gzip)
tar cf - /data/large_dir \
  | pigz -p 4 \
  | ssh backup-host 'cat > /backups/large_dir.tar.gz'

# Verify the remote file exists
ssh backup-host 'ls -lh /backups/large_dir.tar.gz'

Puffergröße und Blockierung steuern

Pipes verfügen über einen Kernel-Puffer (normalerweise 64 KB). Wenn der Puffer voll ist, blockiert der Schreiber; wenn er leer ist, blockiert der Leser. Normalerweise ist das erwünscht, aber in manchen Fällen führt die Blockierung zu einem Deadlock.

Deadlock-Risiko: Wenn Prozess A in FIFO1 schreibt und aus FIFO2 liest, während Prozess B in FIFO2 schreibt und aus FIFO1 liest, können beide blockieren, weil jeder darauf wartet, dass der andere zuerst Daten entnimmt.

Lösungen:

  • Führen Sie mindestens eine Seite im Hintergrund (&) aus, damit sie die Shell nicht blockiert.
  • Verwenden Sie mbuffer oder pv, um zwischen den Stufen einen größeren Puffer im Arbeitsspeicher einzufügen.
  • Verwenden Sie pv -q -B 128m, um einen 128-MB-Puffer einzufügen und so Durchsatzspitzen auszugleichen.
#!/usr/bin/env bash
# pv adds a 64 MB buffer and shows throughput
# Useful when producer and consumer have bursty speeds
dd if=/dev/urandom bs=1M count=200 \
  | pv -B 64m \
  | gzip \
  | wc -c

Praxisbeispiel: Aggregator für Protokolle in Echtzeit

Hier ist ein vollständiges, realistisches Muster: Mehrere Protokolldateien mit tail verfolgen, die Streams über eine Named Pipe zusammenführen, nach Fehlern filtern und eine Live-Zusammenfassung schreiben — alles ohne temporäre Dateien und mit gleichzeitig laufenden Stufen.

Eine solche Pipeline würde als Hintergrundskript zur Überwachung auf einem Produktionsserver ausgeführt.

#!/usr/bin/env bash
FIFO=/tmp/log_aggregator
mkfifo "$FIFO"

cleanup() { rm -f "$FIFO"; }
trap cleanup EXIT INT TERM

# Fan-in: tail multiple logs into the FIFO
tail -F /var/log/syslog /var/log/auth.log > "$FIFO" &
TAIL_PID=$!

# Consumer: filter and timestamp errors in real time
grep --line-buffered -i 'error\|fail\|crit' "$FIFO" \
  | while IFS= read -r line; do
      printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$line"
    done

kill "$TAIL_PID" 2>/dev/null

Pipelines und temporäre Dateien vergleichen

Mit time können Sie den tatsächlichen Durchsatzunterschied zwischen dem Streaming-Ansatz und dem Ansatz mit temporären Dateien messen. Der Pipeline-Ansatz ist bei großen Datenmengen überlegen, weil:

  • Die Stufen nebenläufig laufen — CPU und I/O überlappen sich.
  • Für Zwischendaten ist kein Festplatten-I/O erforderlich — nur die endgültige Ausgabe wird auf die Festplatte geschrieben.
  • Der Speicherbedarf bleibt unabhängig von der Eingabegröße konstant (Stream statt Puffer).

Ein einfacher Benchmark zum Vergleich beider Ansätze:

#!/usr/bin/env bash
# Approach 1: Temp file (sequential)
time bash -c '
  seq 1 5000000 > /tmp/nums.txt
  sort -n /tmp/nums.txt > /tmp/sorted.txt
  uniq /tmp/sorted.txt | wc -l
  rm /tmp/nums.txt /tmp/sorted.txt
'

echo '---'

# Approach 2: Streaming pipeline (concurrent)
time bash -c 'seq 1 5000000 | sort -n | uniq | wc -l'

Wissenscheck: Blockierungsverhalten von Named Pipes

Betrachten Sie das folgende Skript:

mkfifo /tmp/mypipe
echo 'hello' > /tmp/mypipe
echo 'done'

Was geschieht, wenn dieses Skript ohne Hintergrundprozesse oder Leser ausgeführt wird?

Rückblick: Streaming-Pipelines und Named Pipes

In dieser Lektion haben Sie untersucht, wie sich Daten effizient zwischen Prozessen verschieben lassen, ohne temporäre Dateien zu verwenden:

  • Anonyme Pipes (|) verbinden direkt aufeinanderfolgende Befehle und führen alle Stufen gleichzeitig mit automatischer Rückstaukontrolle aus.
  • Named Pipes (mkfifo) erstellen einen FIFO-Dateisystemeintrag, über den unabhängige oder im Hintergrund laufende Prozesse Daten als Stream austauschen können — Schreibvorgänge werden blockiert, bis ein Leser vorhanden ist.
  • Prozesssubstitution (<(cmd), >(cmd)) ermöglicht es Befehlen, die Dateinamen erwarten, Streams transparent zu lesen oder zu erzeugen.
  • tee + >() verteilt einen Stream auf mehrere gleichzeitig laufende Verbraucher, ohne die Quelle erneut zu lesen.
  • Fan-in führt mehrere Produzenten über eine gemeinsame FIFO zu einem Verbraucher zusammen.
  • Rückstaukontrolle und Blockieren sind Funktionen und keine Fehler — führen Sie jedoch immer mindestens eine Seite eines FIFO-Paars im Hintergrund aus, um einen Deadlock zu vermeiden.
  • Verwenden Sie pv oder mbuffer, um größere Puffer hinzuzufügen und den Durchsatz zu überwachen, wenn die einzelnen Stufen stoßweise arbeiten.

Diese Techniken bilden die Grundlage für die leistungsstarke Bash-Datenverarbeitung: Verarbeiten Sie Daten im GB-Maßstab mit konstantem Speicherbedarf und maximaler Parallelität von CPU- und I/O-Vorgängen.

Kostenlos starten

Lerne DevOps Bootcamp mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
142
Lektionen
568

Häufig gestellte Fragen

Ist die Lektion „Streaming-Pipelines und Named Pipes für hohen Durchsatz“ kostenlos?

Ja — der vollständige Text von „Streaming-Pipelines und Named Pipes für hohen Durchsatz“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des DevOps Bootcamp-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der DevOps Bootcamp-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Streaming-Pipelines und Named Pipes für hohen Durchsatz“?

Verwenden Sie FIFOs und Prozesssubstitution, um Daten ohne Zwischen­dateien zwischen Verarbeitungsschritten zu streamen. Du übst DevOps Bootcamp mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um DevOps Bootcamp zu starten?

Keine Vorkenntnisse erforderlich. DevOps Bootcamp auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Streaming-Pipelines und Named Pipes für hohen Durchsatz“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser DevOps Bootcamp-Lektion Code schreiben und ausführen?

Ja. Jede DevOps Bootcamp-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Skripte profilieren und unnötige Subshells vermeiden
  2. Parallelisierung mit xargs -P und Hintergrundjobs
  3. Workloads mit GNU parallel orchestrieren
  4. Streaming-Pipelines und Named Pipes für hohen Durchsatz
← Zurück zu DevOps Bootcamp