Node.js / دليل حلول تحريري

EADDRINUSE: تشخيص المستمع وإغلاق ما تملكه

حدد سبب فشل ربط HTTP محلي، وميّز تعارض المنفذ من تسرب دورة الحياة، وتحقق من إصلاح محدود دون إنهاء عمليات لا تخصك.

وقت القراءة التقريبي: 3 دقيقةتاريخ مراجعة المحتوى

قاعدة معرفة للقراءة فقط · بلا تعليقات عامة

تعرّف على الأعراض

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

افهم الأسباب المحتملة

  • يمتلك مستمع آخر العنوان والمنفذ، أو لم يغلق اختبار سابق خادمه، أو بدأ التطبيق مرتين. وقد يتداخل ربط كل الواجهات وربط واجهة محددة حسب النظام. يحدد EADDRINUSE تعارض الربط، ولا يحدد العملية التي يجب إيقافها.

تأكيد الربط الذي فشل

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

إعادة الإنتاج داخل مختبر مملوك فقط

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

javascript مثال تشخيصي: إعادة الإنتاج داخل مختبر مملوك فقط
import { createServer } from 'node:http';
import { once } from 'node:events';
const owner = createServer((req, res) => res.end('lab'));
owner.listen(0, '127.0.0.1'); await once(owner, 'listening');
try {
  const competing = createServer();
  const failed = once(competing, 'error');
  competing.listen(owner.address().port, '127.0.0.1');
  const [error] = await failed;
  console.log(error.code);
} finally { await new Promise((resolve, reject) => owner.close(e => e ? reject(e) : resolve())); }
الناتج المتوقع
EADDRINUSE

ينتمي الخادمان إلى العرض القصير؛ ويحدث التعارض على منفذ محلي مؤقت فقط.

إصلاح الملكية والإعدادات

للاختبارات المتوازية اختر المنفذ صفر واقرأ server.address().port بعد حدث listening، واحتفظ به داخل الاختبار ومرره للعميل. للخدمة المنشورة اختر المنفذ الموثق وعالج المالك عبر مدير النشر؛ فقد يكسر الانتقال العشوائي التوجيه والفحوص. أزل مسارات البدء المكررة وتأكد من أن استيراد مساعد لا يبدأ مستمعا كأثر جانبي. يعتمد الإصلاح على عزل الاختبار أو البدء المكرر أو إعداد الخدمة المقصود.

الإغلاق في جميع النتائج

يغلق مثال الإصلاح خادمه حتى إذا فشل العميل. وتحتاج الخدمة الطويلة أيضا إلى تصريف آمن للتكرار ومهلة وإغلاق العمال والمجمعات والمؤقتات والاتصالات المرقاة. لا يثبت إغلاق HTTP وحده خلو العملية من الموارد. تجنب process.exit الفورية بديلا عن الملكية؛ فقد تقطع الردود وتخفي التسرب. وقد يعيد مراقب التطوير بدء ابن قبل تصريف القديم؛ فنسق إشارته وإعادة تشغيله مع دورة التطبيق.

javascript مثال تشخيصي: الإغلاق في جميع النتائج
import { createServer } from 'node:http';
import { once } from 'node:events';
const server = createServer((req, res) => res.end('owned'));
server.listen(0, '127.0.0.1'); await once(server, 'listening');
try {
  const response = await fetch(`http://127.0.0.1:${server.address().port}`);
  console.log(await response.text());
} finally { await new Promise((resolve, reject) => server.close(e => e ? reject(e) : resolve())); }
console.log(server.listening);
الناتج المتوقع
owned
false

تكتمل الاستجابة وتحرر finally المستمع. تؤكد false الأخيرة أن هذا الخادم لم يعد يستمع.

تحقق من نجاح الإصلاح

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

امنع تكرار المشكلة

  • استخدم منافذ مؤقتة للاختبارات وسجل التنظيف فورا وافصل البدء عن الاستيراد. وثّق مدير الخدمة وعنوان الربط وأمر الإيقاف المنضبط. عامل التعارض دليلا للتحقيق لا سببا لإنهاء كل عمليات Node.

المراجع الرسمية