لبنات البيانات الموزّعة
صمّم زاحف ويب
كل محرك بحث، وكل راصد أسعار، وكل أرشيف يبدأ بالآلة المتواضعة نفسها: نزّل صفحة، اقرأ روابطها، وكرّر إلى الأبد. الخوارزمية سطر واحد. أما الهندسة فهي كل ما حولها — مليارات الصفحات، ومضيفون سيحظرونك، وطابور يجب ألا ينسى أين كان.
تبدأ من حفنة روابط بذرة (seed URLs) — صفحات قليلة موثوقة غنية بالروابط — وكل ما عداها يُكتشف في الطريق. لنفترض أن الهدف مليار صفحة شهرياً: هذا يعني نحو 400 صفحة في الثانية، بلا توقف، ليلاً ونهاراً. جهاز واحد يجلب 50 صفحة في الثانية يحتاج أسبوعاً لما تنجزه مجموعة صغيرة في ساعات، فهذا نظام موزّع منذ أول رسمة.
الـfrontier هو طابور الروابط التي تنتظر الجلب، ويجيب عن سؤالين: أي صفحة تالية، وبأي سرعة يجوز لنا سؤال كل مضيف. اللباقة (politeness) تعني طوابير لكل مضيف (per-host queues) — كل روابط مضيف واحد تقف في طابوره الخاص، ولا يُبقي العامل أكثر من طلب واحد قيد التنفيذ لكل مضيف مع مهلة بين الطلبات، فموقع من ألف صفحة يُزار بانتظام ولا يُقصَف أبداً. والأولوية تجري بالتوازي: الصفحات التي تتغير كثيراً أو تهمّ أكثر (صفحة أخبار رئيسية تتقدم على موضوع منتدى منسيّ) تقفز إلى الأمام. طابور عام واحد سيفشل في المهمتين معاً — سيطلق خمسين طلباً على مضيف واحد مسكين في الثانية نفسها.
الجالبات (fetchers) عمال عديمو الحالة: يسحب أحدهم رابطاً من طابور مضيفه، ينزّل الصفحة، ويسلّم البايتات لمن بعده. عنق الزجاجة الخفي هو DNS: كل اسم مضيف جديد يعني استعلاماً قد يكلف مئات المللي ثانية ويقصف الخوادم العامة حتى تخنقك. لذلك يحتفظ كل جالب بـDNS cache — من اسم المضيف إلى عنوان IP يُحدَّث عند انتهاء صلاحيته — وزحف لمس عشرة ملايين مضيف يحلّ معظم الأسماء من الذاكرة. وملف robots.txt ينال المعاملة نفسها: اجلبه مرة لكل مضيف، خزّنه، وأطعه.
يستخرج المُحلّل (parser) النص والروابط الخارجة، ثم يحميك مرشّحان من نفسك. الأول بصمة المستند (fingerprint): طبّق hash على محتوى الصفحة وقارن — روابط كثيرة تقدّم المحتوى نفسه (نسخ مرآة، معاملات تتبّع)، وبصمة سبق رؤيتها توفّر عليك تخزين الصفحة ذاتها ومعالجتها من جديد. الثاني مجموعة الروابط المُشاهدة: قبل أن يدخل أي رابط مستخرج إلى الـfrontier، افحص هل سبق إدراجه. عند مليار رابط لا مكان لهذه المجموعة في hash map — أما Bloom filter فيحفظها في بضعة GB من الذاكرة، مع إيجابيات كاذبة نادرة ثمنها الوحيد تخطّي صفحة سيعرضها الويب عليك غداً.
بعض المواقع يولّد فضاءات روابط لا نهائية قصداً أو سهواً: صفحة تقويم تربط بالشهر التالي إلى الأبد، وصفحة بحث بتركيبات مرشّحات لا تنتهي. وزاحفك سيمتلئ طابوره بسرور بقمامة موقع واحد بينما تنتظر بقية الويب. الدفاعات مملة وفعّالة: حد أقصى لعمق الزحف لكل بذرة، وميزانية صفحات لكل مضيف، وحدود لطول الرابط. المصائد ليست حالة حدّية — إنها الحالة الافتراضية للويب المفتوح.
زحف يمتد شهراً سيصادف عمالاً موتى وأقراصاً ممتلئة ونشرة واحدة على الأقل تسوء. إن كان الـfrontier يعيش في الذاكرة فقط، فانهيار واحد يكلفك كل رابط مكتشَف ولم يُجلَب بعد — أياماً من العمل. لذلك خزّن الـfrontier بثبات: لقطات إلى القرص أو مخزن مكرَّر، ونقاط تفتيش (checkpoint) للتقدم — ما اكتمل وما قيد التنفيذ — بتكرار يجعل التعافي استئنافاً لا بداية من الصفر. الجالبات يمكن أن تموت بحرية؛ أما الطابور فلا.
إن جرّدت النظام من حجمه، فالزاحف طابور له أخلاق. الـfrontier يقرر ما التالي وبأي رفق يسأل، والجالبات عضلات قابلة للتبديل، وإزالة التكرار فحصان رخيصان يُبقيان الطابور أميناً. كل ما هو صعب في هذا النظام يسكن في ذلك الطابور — الأولوية واللباقة والثبات — وكل ما هو سهل يسكن خارجه.
لماذا يحتفظ الـfrontier بطابور مستقل لكل مضيف بدل طابور أولويات عام واحد كبير؟ (اللباقة: طوابير المضيفين تتيح فرض طلب واحد قيد التنفيذ لكل مضيف مع مهلة بين الطلبات، فلا يُقصَف أي موقع؛ أما الطابور العام فقد يرسل خمسين طلباً للمضيف نفسه في ثانية واحدة، فيحظرك، وما زال يجوّع الصفحات عالية الأولوية خلف موقع واحد صاخب.)
ارسمه حلقة وسيفضح التصميم نفسه: بذور تدخل، وfrontier ينظّم الإيقاع لكل مضيف، وجالبات بـDNS cache، ومُحلّل، وطبقتا إزالة تكرار، وروابط تعود إلى الـfrontier. ثم اقضِ المقابلة في الأشياء الثلاثة التي تكسر الزواحف فعلاً — اللباقة والمصائد والانهيارات — لأن هناك يرسم المبتدئون صناديق بينما يرسم الخبراء أنماط الفشل.
يوم الاثنين هذا، اكتب زاحفاً مهذباً من خمسين سطراً بلغتك المفضلة: صفحة بذرة واحدة، وخريطة لكل مضيف تفرض مهلة ثانية واحدة بين الطلبات، ومجموعة روابط مُشاهدة، وحد أقصى للعمق مقداره ثلاثة. ثم وجّهه إلى موقع فيه أداة تقويم وراقب حد العمق وهو ينقذك — هذا البرنامج الصغير يحوي كل فكرة في هذا الدرس ما عدا الحجم.
لبنات البيانات الموزّعة