0Pricing
DevOps Bootcamp · درس

خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية

استخدم FIFOs واستبدال العمليات لبث البيانات بين المراحل دون ملفات وسيطة

خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية درس مجاني في DevOps Bootcamp على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في DevOps Bootcamp، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.

لماذا تضرّ الملفات الوسيطة بمعدل النقل

عند ربط أوامر مثل sort file.txt > tmp.txt && uniq tmp.txt > result.txt، تتحملون تكلفة خفية: عمليات الكتابة على القرص والقراءة منه، إضافةً إلى توقّف مسار المعالجة حتى تنتهي المرحلة الأولى بالكامل قبل بدء المرحلة التالية.

تلغي مسارات المعالجة المتدفقة هذه التكلفة. فتنتقل البيانات مباشرةً من المنتج إلى المستهلك في الذاكرة، مرحلةً بعد أخرى، وبالتزامن. وهذه هي الفكرة الأساسية وراء أنابيب Unix، وتمتدّ لتشمل الأنابيب المُسمّاة (FIFOs).

  • الأنبوب المجهول (|): يربط أمرين متجاورين في سطر Shell نفسه.
  • الأنبوب المُسمّى (FIFO): ملف خاص في نظام الملفات يتيح لعمليات غير مرتبطة بثّ البيانات فيما بينها.
  • استبدال العمليات: يتيح لأمرٍ ما التعامل مع مخرجات أمر آخر كما لو كانت ملفًا.

يوضّح لكم هذا الدرس كيفية تطبيق الأساليب الثلاثة لزيادة معدل النقل إلى أقصى حد في مسارات عمل Bash الواقعية.

بنية مسار المعالجة المتدفقة

يربط الأنبوب المجهول stdout لعملية بـ stdin للعملية التالية. ويحافظ kernel على تشغيل العمليتين في الوقت نفسه داخل مخزن مؤقت ثابت الحجم في الذاكرة (يبلغ عادةً 64 كيلوبايت في Linux).

الفكرة الأساسية هي أن سرعة مسار المعالجة لا تتجاوز سرعة أبطأ مراحله. فإذا كان المنتج أسرع، فإنه يتوقف عند امتلاء المخزن المؤقت. وإذا كان المستهلك أسرع، فإنه يتوقف عند فراغ المخزن المؤقت. ويُعدّ هذا الضغط العكسي آلية تحكّم مجانية وتلقائية في التدفق.

يحسب المثال أدناه عناوين IP الفريدة في سجل وصول كبير من دون كتابة ملف مؤقت على الإطلاق. وتعمل كل مرحلة بالتزامن:

#!/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

إنشاء الأنابيب المُسمّاة باستخدام mkfifo

يُنشأ الأنبوب المُسمّى (FIFO — الوارد أولًا يخرج أولًا) باستخدام mkfifo. ويظهر في نظام الملفات كأنه ملف عادي، لكن البيانات المكتوبة فيه لا تُخزّن على القرص أبدًا، بل تنتقل مباشرةً إلى العملية التي تقرؤها.

تذكّروا السلوكيات الأساسية التالية:

  • تتوقف عملية الكتابة إلى FIFO مؤقتًا حتى يفتحه قارئ، والعكس صحيح.
  • يبقى إدخال FIFO موجودًا في نظام الملفات؛ ويجب حذفُه باستخدام rm عند الانتهاء.
  • يُسمح بوجود عدة كُتّاب، لكن لا يُضمن الترتيب فيما بينهم.

في المثال أدناه، يضغط منتج البيانات البيانات داخل FIFO، بينما يرفعها مستهلك إلى S3 في الوقت نفسه — من دون الحاجة إلى ملف مؤقت.

#!/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

استبدال العمليات: التعامل مع أمر كأنه ملف

يستخدم استبدال العمليات الصيغة <(command) أو >(command). ينشئ Bash خلف الكواليس FIFO (أو واصف ملف /dev/fd/N) ويمرّر المسار إلى الأمر الخارجي.

يكون هذا مفيدًا عندما تتوقع أداة ما وسيطًا يمثّل اسم ملف بدلًا من stdin. فمن دون استبدال العمليات، ستحتاجون إلى ملف مؤقت؛ أما معه، فستبثّون البيانات مباشرةً.

  • <(cmd) — يقرأ الأمر الخارجي من مخرجات cmd.
  • >(cmd) — يكتب الأمر الخارجي إلى مدخلات 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: تقسيم التدفق على عدة مستهلكين

يقرأ tee من stdin ويكتب البيانات إلى كل من stdout وملف واحد أو أكثر. وبدمجه مع استبدال العمليات، يمكنكم توزيع تدفق واحد على عدة مسارات معالجة في الوقت نفسه، من دون الكتابة إلى القرص.

يفيد هذا النمط عندما تريدون، على سبيل المثال، تسجيل البيانات الخام ومعالجتها في الوقت نفسه.

#!/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:"

نمط التوزيع: منتج واحد وعدة مستهلكين

عندما يجب على مصدر بيانات واحد تزويد عدة مستهلكين مستقلين، ادمجوا tee مع عدة عمليات استبدال >(). يحصل كل مستهلك على التدفق الكامل ويعمل بالتزامن.

يؤدي ذلك إلى تجنّب قراءة ملف المصدر عدة مرات. وبالنسبة إلى ملف حجمه 10 غيغابايت، يكون الفرق هائلًا — قراءة واحدة من القرص بدلًا من N قراءات.

#!/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)

نمط التجميع: عدة منتجين ومستهلك واحد

عكس التوزيع هو التجميع: عدة مصادر مستقلة تبث البيانات إلى مستهلك واحد. وتجعل FIFOs المُسمّاة ذلك أمرًا مباشرًا.

ومن حالات الاستخدام الشائعة دمج تدفقات السجلات من عدة خوادم في الوقت الفعلي، أو تجميع النتائج الجزئية من عمّال متوازيين.

لاحظوا أنه مع وجود عدة كُتّاب، سيرى المستهلك مخرجات متداخلة — وهذا مناسب للبيانات الموجّهة حسب الأسطر عندما يكون كل سطر مكتفيًا بذاته، لكن يجب عليكم التعامل مع الترتيب بأنفسكم إذا كان التسلسل مهمًا.

#!/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 للضغط المتوازي

من أكثر الاستخدامات العملية لـ FIFOs الضغط المتوازي. تقرأ أدوات مثل pigz (gzip المتوازي) أو pbzip2 تدفقًا من البيانات؛ ويمكنكم تمرير البيانات الخام مباشرةً من دون إنشاء ملف غير مضغوط مرحلي.

يؤرشف النمط أدناه مجلدًا، ويضغطه باستخدام جميع أنوية وحدة المعالجة المركزية، ويبث النتيجة إلى مضيف بعيد — وكل ذلك في الوقت نفسه:

#!/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'

التحكّم في حجم المخزن المؤقت والحظر

تحتوي الأنابيب على مخزن مؤقت في kernel (حجمه عادةً 64 كيلوبايت). وعندما يمتلئ المخزن المؤقت، تتوقف عملية الكتابة مؤقتًا؛ وعندما يكون فارغًا، تتوقف عملية القراءة مؤقتًا. وهذا هو المطلوب عادةً، لكن التوقف قد يسبب في بعض الحالات حالة توقف تام.

خطر التوقف التام: إذا كانت العملية A تكتب إلى FIFO1 وتقرأ من FIFO2، بينما تكتب العملية B إلى FIFO2 وتقرأ من FIFO1، فقد تتوقف العمليتان مؤقتًا بانتظار أن تقرأ الأخرى أولًا.

الحلول:

  • ضعوا جانبًا واحدًا على الأقل في الخلفية (&) حتى لا يحجب Shell.
  • استخدموا mbuffer أو pv لإضافة مخزن مؤقت أكبر في الذاكرة بين المراحل.
  • استخدموا pv -q -B 128m لإدراج مخزن مؤقت حجمه 128 ميغابايت، مما يخفف تقلبات معدل النقل.
#!/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

مثال عملي: مجمّع سجلات في الوقت الفعلي

إليكم نمطًا واقعيًا متكاملًا: قراءة عدة ملفات سجلات باستمرار، ودمج التدفقات عبر أنبوب مُسمّى، وتصفية الأخطاء، وكتابة ملخّص مباشر — وكل ذلك من دون ملفات وسيطة، مع تشغيل جميع المراحل بالتوازي.

هذا النوع من مسارات المعالجة يمكن تشغيله باعتباره نصًا برمجيًا للمراقبة في الخلفية على خادم إنتاج.

#!/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

قياس أداء مسارات المعالجة مقابل الملفات المؤقتة

يمكنكم قياس الفرق الفعلي في معدل النقل بين نهج التدفق ونهج الملفات المؤقتة باستخدام time. ويتفوّق نهج مسار المعالجة على مجموعات البيانات الكبيرة للأسباب التالية:

  • تعمل المراحل بالتزامن — فتتداخل عمليات وحدة المعالجة المركزية والإدخال والإخراج.
  • لا توجد عمليات إدخال وإخراج إلى القرص للبيانات الوسيطة — إذ تصل المخرجات النهائية فقط إلى القرص.
  • يبقى حجم الذاكرة المستخدم ثابتًا بغض النظر عن حجم الإدخال (تدفق، لا مخزن مؤقت).

اختبار أداء بسيط لمقارنة النهجين:

#!/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'

اختبار المعرفة: سلوك حظر الأنبوب المُسمّى

تأملوا النص البرمجي التالي:

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

ماذا يحدث عند تشغيل هذا النص البرمجي من دون أي عمليات في الخلفية أو أي قارئات؟

مراجعة: مسارات البث والأنابيب المسماة

في هذا الدرس استكشفتم كيفية نقل البيانات بكفاءة بين العمليات من دون استخدام ملفات وسيطة:

  • الأنابيب المجهولة (|) تصل بين الأوامر المتجاورة وتُشغّل جميع المراحل بالتزامن مع تطبيق الضغط العكسي تلقائيًا.
  • الأنابيب المسماة (mkfifo) تنشئ إدخالًا لنظام الملفات من نوع FIFO، ما يتيح للعمليات غير المرتبطة أو العاملة في الخلفية بث البيانات فيما بينها — إذ تتوقف عمليات الكتابة حتى يتوفر قارئ.
  • استبدال العمليات (<(cmd)، >(cmd)) يتيح للأوامر التي تتوقع أسماء ملفات استهلاك التدفقات أو إنتاجها بشفافية.
  • tee + >() يوزّع تدفقًا واحدًا على عدة مستهلكين متزامنين من دون إعادة قراءة المصدر.
  • الدمج الوارد يدمج عدة منتجين في مستهلك واحد عبر FIFO مشتركة.
  • الضغط العكسي والحجب ميزتان وليسا خطأين — لكن احرصوا دائمًا على تشغيل جانب واحد على الأقل من طرفَي FIFO في الخلفية لتجنب الجمود المتبادل.
  • استخدموا pv أو mbuffer لإضافة مخازن مؤقتة أكبر ومراقبة معدل النقل عندما تكون المراحل متقطعة.

تُشكّل هذه التقنيات أساس هندسة البيانات عالية الإنتاجية باستخدام Bash: إذ تتيح معالجة بيانات بحجم GB مع استخدام ثابت للذاكرة وتحقيق أقصى توازٍ بين وحدة المعالجة المركزية وعمليات الإدخال والإخراج.

الأسئلة الشائعة

هل درس «خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية» مجاني؟

نعم — نص درس «خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة DevOps Bootcamp، انتقل إلى CoddyKit PRO. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.

ماذا ستتعلم في «خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية»؟

استخدم FIFOs واستبدال العمليات لبث البيانات بين المراحل دون ملفات وسيطة تتمرن على DevOps Bootcamp مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ DevOps Bootcamp؟

لا تُشترط خبرة سابقة. DevOps Bootcamp على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس DevOps Bootcamp هذا؟

نعم. كل درس في DevOps Bootcamp يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تحليل أداء السكربتات وتجنّب subshells غير الضرورية
  2. التوازي باستخدام xargs -P والمهام الخلفية
  3. تنسيق أحمال العمل باستخدام GNU parallel
  4. خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية
← العودة إلى DevOps Bootcamp