|
ملاحظة: الموضوع برمجي بحت! المعذرة لغير المبرمجين،،،
اليوم تم إرسالة الحلقة الثانية من مسلسل ليه أنا للمشتركين في القائمة البريدية، و هذه الرسالة تختلف عن الرسائل التي قبلها في أن القائمة البريدية و صل مشتريكها لعدد يحمل ثلاث أصفار :)... بكلام آخر،،، تم القيام بـعملية أختبار قدرة Robustness Test للقائمة البريدية :)....
اممممم،،، حسناً، أعتقد أن المهتمين في الموضوع يريدون معرفة نتيجة الإختبار، و لكن قبل ذلك، سأتحدث عن الهيكل العام للقائمة،، للفائدة العامة، و لتبرير النتيجة D: ...
في الأيام الأولى لتطوير الموقع، قال لي ثامر انه يريد قائمة بريدية في الموقع، و وقتها حاولت أن أتهرب، لأنني أعرف ان هذه القائمة ستسبب لي بالكثير من المتاعب، فكتابة قائمة بريدية سهل جدا، و لكن هل ستكون هذه القائمة قادرة على تحمل مشتركين بالالاف؟؟!!،،
هذا كان السؤال الحقيقي،،،و هذا ما كان يخيفني،،،،
عموما،، أصر الرجل إصرار الأطفال على القائمة البريدية، و تورطت في المسألة، و أخذت اجوب الإنترنت لأطلع على تصاميم من سبقوني في هذا المجال، و أستعير الجيد منها، و بعد جولة في الإنترنت، و مطالعة أكثر من قائمة بريدية مبرمجة بلغات مختلفة، توصلت إلى نتيجة واحدة، و هي ان الرسالة التي سترسل من الموقع يجب حفظها في مكان ما، أو بالأصح في جدول في قاعدة البيانات، لان إرسال عدد هائل من الرسائل يحتاج إلى ساعي بريد مفتول العضلات، و بينه و بين نظام التشغيل علاقة رومانسية بحيث يعطيه الكثير من CPU Time، و هذا الشيء غير موجود في شركات الإستضافة المشتركة، لذلك، تركت فكرة الساعي القوي، و قررت الإستعاضة عنه بساعي كسول Lazy Sender، يرسل الرسائل في فترات متقطعة، بحيث يقوم في كل فترة بإرسال عدد من الرسائل المحفوظة في هذا الجدول، ثم يقوم بتعليمها flagging على أنها تم إرسالها..
طبعا قمت بتصميم هذا الجدول، و اسميته Outgoing_Emails و تمت إضافة الحقول الخاصة بأي رسالة بريد في هذا الجدول، و هي كالتالي: :
|
From: المرسل To: المرسل إليه CC: نسخة كربونية BCC: نسخة كربونية مخفية Subject: الموضوع Body: نص الرسالة Attachments: المرفقات
|
 |
| صورة1: جدول Outgoing_Emails |
بالإضافة لهذه الحقول، أعتقدت انني احتاج إلى حقل إضافي، و هو SendAttempts يوضح كم محاولة تمت لإرسال الرسالة، لأنه يمكن ان يحدث الكثير من الأخطاء خلال عملية الإرسال، فالرسالة تحتوي على مرفق Attachment كبير الحجم، و يمكن ان يكون حجم بريد المستقبل قد و صل إلى سعته العليا Maximum Usage، لذلك، يجب أن اعرف كم محاولة تمت لإرسال رسالة معينة، و بعد أن تتجاوز عدد المحاولات رقم معين threshold، سوف ارمي هذه الرسالة في سلة المهملات، لأنها ببساطة، غير قابلة للإرسال!... و من الأفضل أن يقوم صاحب البريد بتسجيل ايميل جديد في gmail ثم يعيد تسجيل نفسه!..
بعد ذلك قمت بتصميم الجدول المشتركين في القائمة، و هو جدول بسيط، يحتوي على ثلاث حقول فقط، و هي كالتالي:
|
Email: البريد الإلكتروني JoinDate: تاريخ الإشتراك في القائمة MailListName: اسم القائمة البريدية
|  |
| صورة2: جدول MailList |
أعتقد انكم ستسغربون الحقل الأخير - اسم القائمة البريدية MailListName -، هذا الحقل قم بإضافته لأنه من المناسب ان اقوم ببرمجة قوائم بريدية متعددة و ليس قائمة بريدية واحدة، بمعنى انه يمكن ان يشترك زوار الموقع في قائمة "الحلقات الجديدة" أو قائمة "الأخبار الجديدة" حسب اهتمام المشترك،،،، و بهذا الشكل، تكون القائمة البريدية متعددة الإستخدام, و مرنة جداً..
حتى الان كلام جميل، و لكن لم أغطي كل جوانب المشكلة، فالمشتركين تم حفظهم، و الرسائل التي سوف ترسل لهم بإذن الله تم حفظها في طابور Queue لتنتظر فيها وقت إرسالها، و لكن كيف سأقوم بتعبئة هذا الطابور، و متى و كيف سيتم إرسالها بالفعل؟؟.... هنا، أخذت المشكلة شكل آخر،،،،
في تطبيقات الويب بشكل عام، في الـ ASP.NET بشكل خاص، يتم تنفيذ الأكواد الخاص بالصفحات عندما يقوم زائر فضولي بزيارة موقعك، و لا يمكنك تنفيذ أكواد خارج نطاق هذا السياق التنفيذي Execution Context...
اممممم،، اسف، أعتقد أن هذا الكلام غير صحيح، خصوصا مع بداية نقل مرض المسالك التنفيذية الإضافية Background Threads إلى عالم تطبيقات الويب،، حيث أستطيع استخدام كلاس System.Thread، في صنع مسلك Thread خارج نطاق HttpContext، و اجعله يقوم بهذه المهمة الصعبة!!!.. و لكن أين و متى سأقوم بصنع هذا المسلك Thread السحري!!!؟؟ ... من هنا،،، دخلت HttpModule في الصورة... حيث انها المكان الوحيد، الذي تستطيع التلاعب فيه على قوانين اللعبة D:.... فهي تعمل عند بداية تشغيل ال AppDomain الخاص بالموقع، حيث يقوم ASP.NET Runtime بمناداة الدالة Init و يسمح لها بالقيام بالتعبير عن مشاعرها عند تشغيل الموقع....
قمت بكتابة HttpModule اسميته EmailModule، يقوم بعمل Timer عند مناداة الدالة Init، بالشكل التالي:
private static Timer _timer;
public void Init(HttpApplication HttpApplication)
{
if (_timer == null)
{
_timer = new Timer(new TimerCallback(SendEmails),
HttpApplication.Context, #Interval#, #Interval#);
}
} |