BACKEND ENGINE
SYS_v20.x.x // PRODUCTION
STATUS: LOGICAL_PIPELINE

مبادئ معمارية ونظم خوادم Node.js الحديثة

دليل تحليلي مصمم لترسيخ المفاهيم البرمجية لنظام التشغيل، وإدارة الذاكرة، وقنوات الإدخال/الإخراج (I/O Streams)، وصولاً إلى بناء ومعايرة خوادم الويب الموزعة.

// CORE

مبدأ التبعية البرمجية المنطقية

لا يمكن كتابة خادم ويب دون فهم قنوات البيانات (Streams). ولا يمكن قراءة البيانات دون فهم معمارية حلقة الأحداث (Event Loop). يعالج هذا الدليل المفاهيم تِباعاً من الأسفل للأعلى لضمان البناء السليم.

PHASE_01 // ENVIRONMENT & COMPILATION

1. بيئة التشغيل وهيكلة المترجم (Babel)

جافاسكريبت مصممة كأصل تزامني أحادي الخيط داخل المتصفح. لتشغيلها على خوادم المعالجة، نستخدم محرك V8 مدمجاً في Node.js. في هذا المستوى، نربط المترجمات للتحويل بين أنظمة الوحدات الحديثة والقديمة.

ES6 (ESModules) import / export Babel Transpiler ESLint / Babel-node Engine Run module.exports
01

مفارقة التصدير والاستيراد

تستخدم المشاريع الحديثة نمط ES6 Modules مثل import. ولكن، بيئة اختبارات المصحح الآلي تبحث عن أساليب الربط التقليدية لـ CommonJS. نقوم ببرمجة Babel للترجمة الوسيطة لحل هذا التضارب.

02

معايرة ESLint

أداة التدقيق الإملائي البرمجي ليست خياراً جمالياً؛ إنها صمام الأمان لبيئة تشغيل خالية من مشاكل الذاكرة والتداخل المتغير. تفحص ESLint الكود ضد التغيرات غير المستعملة والنهايات السائبة للمسارات.

حالة حافة حرجة: تعارض استدعاءات التصدير ومصحح بيئة الاختبار

إذا استخدمت صيغة export default في ملفات المشروع دون تهيئة Babel بالكامل للبيئة التنفيذية للاختبارات، سيفشل مصحح بيئة الاختبار الآلية (Jest) في فك تشفير الكود وسيُرجع خطأ Unexpected token 'export'. الحل دائماً يكمن في التأكد من تفعيل babel.config.js ومطابقة التصدير النهائي باستخدام التوافقية العكسية module.exports = myFunction عند التصدير للملفات الأساسية المطلوبة للاختبار.

PHASE_02 // OS STREAMING & INPUTS

2. الاتصال بالقنوات القياسية والوسيطة (Process API)

تتواصل العمليات البرمجية مع نظام التشغيل عبر ثلاثة مجارٍ رئيسية: المدخلات القياسية (stdin)، المخرجات القياسية (stdout)، والرسائل المباشرة عبر واجهة سطر الأوامر (process.argv).

01

إدارة سطر الأوامر via process.argv

أي متغير تمرره أثناء التشغيل يتم حفظه كنص في مصفوفة ممتدة. تذكر دائماً: العنصر الأول والثاني محجوزان للنظام والمسار التنفيذي، المتغير الفعلي يبدأ من الدليل رقم 2.

process_cli.js
const args = process.argv;
if (args.length < 3) {
  process.stdout.write("Error: Missing args\n");
  process.exit(1);
}
02

قنوات الإدخال المستمر stdin

تظل القناة القياسية للمدخلات مفتوحة للاستماع كدفق بيانات مستمر. نستخدم الأحداث للتحكم بلحظة انتهاء تدفق البيانات لاستكمال معالجة العمليات الأخرى.

stdin_listener.js
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
  const chunk = process.stdin.read();
  if (chunk !== null) {
    process.stdout.write(`Data: \${chunk}`);
  }
});

حالة حافة حرجة: ترميز بايتات الإدخال ومحارف التحكم بالنهاية (\r\n)

بيانات الإدخال عبر دفق stdin تصل دائماً على هيئة بايتات خام من النوع Buffer ما لم يتم فك تشفيرها صراحة بـ utf8. علاوة على ذلك، في أنظمة التشغيل المختلفة، تنتهي الأسطر بمحارف غير مرئية: محرف الانتقال لسطر جديد (\n) في يونكس، أو العودة لبداية السطر مع سطر جديد (\r\n) في ويندوز. إذا لم تقم بإزالتها باستخدام التابع .trim()، ستفشل عمليات المقارنة النصية الشرطية بالكامل في مشروعك (مثل التحقق من كلمة "exit").

PHASE_03 // DATA STREAMING & CONGESTION

3. قنوات البيانات الصامتة والمعالجة غير المتزامنة لملفات الـ CSV

في الأنظمة ذات الكفاءة العالية، لا يتم حجب السلسلة الأساسية للتطبيق مطلقاً لقراءة البيانات. نقارن هنا بشكل بنيوي بين الطريقتين لنرى كيف يمكن لقراءة ملف واحد متزامن أن يعطل استجابة الخادم لآلاف المستخدمين.

Client 1 Client 2 Single Thread Engine SYNC: readFile Blocking Thread Pool (Libuv) ASYNC Task Processing (Does not block Main Thread)
01

القراءة اللامتزامنة وإدارة تدفق الأخطاء

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

async_fs_reader.js
const fs = require('fs').promises;

function readDatabase(path) {
  return fs.readFile(path, 'utf8')
    .then(data => parseCSV(data))
    .catch(() => {
      throw new Error('Cannot load the database');
    });
}
02

منطق فرز وتصفية المصفوفات (CSV Parser)

أغلب الأخطاء البرمجية تحدث عند تقسيم الأسطر بسبب محارف السطر الجديد أو تصفية الحقول الفارغة الناتجة عن ترويسات محرر ملفات الـ CSV.

csv_parser.js
function parseCSV(data) {
  const lines = data
    .split('\n')
    .filter(line => line.trim() !== '');
  const header = lines.shift(); // إبعاد ترويسة الجدول
  return lines.map(line => line.split(','));
}

حل ذكي: الاستبعاد الاستباقي للأسطر الفارغة وتحسين الذاكرة التنفيذية

تفريغ البيانات النصية الكبيرة في الذاكرة عبر التابع .split() يستهلك الذاكرة العشوائية بشدة. لضمان أداء مستقر، نقوم بتنظيف المصفوفة الناتجة من البداية عبر التابع .filter() للتخلص من الحقول المشوهة والأسطر الفارغة تماماً، وبذلك نمنع تداخل السجلات والحقول الـ undefined التي تعطل وظائف الفلترة والإحصاء لعدد الطلاب.

PHASE_04 // NETWORKING & HANDSHAKES

4. بروتوكول الشبكة الخام وإنشاء المقابس (HTTP Module)

قبل أن تظهر أطر العمل لتسهيل المهمة، كان المطورون يتخاطبون مباشرة مع مأخذ الشبكة عبر بروتوكول HTTP الخام. هنا نقوم ببناء خادم يدوي يوضح دورة الطلب والاستجابة وكيفية صياغة الرؤوس اليدوية.

01

بناء الخادم اليدوي وإعادة تدوير المقابس

المفتاح هنا هو التوجيه القائم على المسارات الفرعية. يجب أن نحدد بدقة حالة الرمز (statusCode) ونوع المحتوى المرسل لكي يعرف المتصفح طريقة التفسير الصحيحة للبيانات المستقبلة.

native_http_server.js
const http = require('http');
const server = http.createServer((req, res) => {
  if (req.url === '/') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello Holberton School!');
  } else {
    res.statusCode = 404;
    res.end('Not Found');
  }
});
server.listen(1245);

الخطر القاتل: استهلاك المقبس دون إنهاء البث اليدوي

إذا تمت معالجة طلب العميل بنجاح دون استدعاء التابع المنهي للبث res.end()، فسيظل المقبص مفتوحاً في وضع معلق للاتصال. المتصفح لن يظهر له أي أخطاء صريحة، ولكنه سيظل يعرض مؤشر التحميل إلى الأبد حتى يتم إنهاء الجلسة بسبب تجاوز المهلة المسموح بها (Timeout)، مما يدمر أداء السيرفر ويستهلك خيوط المعالجة.

PHASE_05 // HIGH-LEVEL MIDDLEWARE

5. نمذجة المسارات وهيكلة المخدم الحديث (Express.js)

ينقلنا Express من كتابة شروط التوجيه اليدوية الطويلة والبدائية إلى عالم التوجيه البسيط والمنظم وقنوات التحقق البرمجية (Middlewares).

HTTP Request Express App Middleware Chain Structural Router Layer GET /students GET /status 404 Exception Fallback
01

التوجيه النظيف والتحويل التلقائي لبيانات الـ JSON

تتعامل بيئة Express مع صياغة أنواع الاستجابة بشكل تلقائي بالكامل، بالإضافة لإتاحة التوجيه المعتمد على المسارات المتشعبة وفصل الأجزاء المتكررة لملفات مستقلة.

express_routing.js
import express from 'express';
const app = express();

app.get('/students/:id', (req, res) => {
  // استخراج المتغيرات البرمجية من مسار الطلب تلقائياً
  const studentId = req.params.id;
  res.json({ id: studentId, status: 'active' });
});

الترتيب الحرج للمسارات والتبعات غير المتوقعة للتصفيات العامة

تعمل خوارزمية البحث في Express على معالجة المسارات خطياً من الأعلى للأسفل وبأولوية التطابق الأول. إذا قمت بصياغة مسار عام يعالج جميع الأخطاء أو المسارات الاستثنائية ووضعته في أعلى شجرة السجل، فسيقوم بتعطيل وحجب كافة المسارات الشرعية التي تليه. احرص دائماً على صياغة وبناء مساراتك من الأكثر تخصصاً في الأعلى وصولاً للمسارات الأكثر عمومية مثل الـ 404 في أسفل الملف.

PHASE_06 // INTEGRITY & SIMULATION

6. مراقبة العمليات ومعاينة بيئة الإنتاج التفاعلية

في هذا الجزء التفاعلي، سنقوم بمحاكاة دورة حياة الخادم الفعلي أثناء بدء التشغيل، قراءة قاعدة البيانات، استدعاء التابع غير المتزامن، واستقبال طلبات العملاء عبر واجهة الطرفية المصغرة.

الواجهة التفاعلية لتشغيل العمليات

توضح اللوحة التفاعلية المقابلة السجل المباشر للعمليات التنفيذية لخادم المطور المصغر. راقب التغير الفعلي والتحديث التلقائي الذي يتم عبر أداة Nodemon ومستخرجات الأحداث من محرك المعالجة.

التحقق المستمر وإدارة الجودة (ESLint & Jest)

قبل تسليم المشروع بشكل نهائي للمصحح الآلي، ينصح بتنفيذ فحص التدقيق الإملائي عبر تشغيل الأمر npm run lint لضمان خلو الملفات من المتغيرات البرمجية الضائعة والالتزام بقواعد التصدير الصارمة المعينة سلفاً.

nodemon ~ dev_process.log ● RUNNING