Saturday, February 20, 2010

الأداة ServerService


عن الحزمة

تم تصميم هذة الأداة لضمان اداء جيد بكلفة معقولة في مسألة التعامل مع الإتصالات الواردة للخادم وقراءة طلبات العملاء
هذة الأداة عبارة عن بنية تحتية infrastructure لبرامج الخادم servers , وظيفتها الأساسية هو التنصت على طلبات الإتصال القادمة للعتاد الذي تعمل علية , ومن ثم تمرير الطلبات requests القادمة من كل عميل الى واجهة خارجية .
البنية الداخلية :
تستخدم الأداة داخليا 2 threadsفقط , تتزامنان على طابور دائري circular queue
1- تقوم الاولى A بالتنصت على الإتصالات القادمة للعتاد , ووضع كل اتصال جديد في ذيل الطابور الدائري (وهو الموضع الذي يلي اجدد اتصال مفترض).
2- تقوم الثانية B بمسح الطابور بحثا عن اتصال مغلق وتزيلة من الطابور .في حالة وجود اتصال غير مغلق , تقوم بقرائة جميع البيانات الواصلة عبر قناة الإتصال دفعة واحدة , ومن ثم تمررها للواجهة الخارجية.
تستمر العمليتان 1 و2 حتى يتم استدعاء الدالة الخاصة بالإغلاق , حيث يتم اغلاق جميع الإتصالات السابقة , وانهاء التنصت للإتصالات الجديدة , وتحرير جميع الموارد المستخدمة (لا تتضمن موارد الواجهة الخارجية).
الواجهة الخارجية:
وهي من نوع AbstractTask class , هذة الواجهة الخارجية تعمل في الخلفية Background process لمعالجة الطلبات وارسال الردود responses . ولذلك هي مسؤولة عن البروتوكول التخاطبيApplication protocol بين الخادم و العميل.
هذه الواجهة تحتوي على دالتين :
processLater(Message) وهي دالة صلبة concrete
process(Message) وهي دالة مجردة abstract
تستخدم الدالة الأولى لأستدعاء الثانية بشكل غير متزامن Asynchronous
في هذة الأداة يتم استخدام الدالة الأولى لتمرير طلبات العملاء requests حيث يتم معالجتها بشكل منفصل وغير متزامن مع البنية الداخلية للخادم.
اوجة القصور في الأداة:
1- يضطر العميل للإنتظار بينما يتم خدمة عميل أخر.
بسبب استخدام thread واحدة فقط للتنصت على requests فإن كل عميل قد ينتظر مدة اقصاها m * (N-1)
حيث أن N هي العدد الكلي للعملاء و m هي المدة الزمنية المستغرقة لخدمة عميل واحد (استيعاب طلبة وتمريرة للواجهة الخارجية).
نستطيع حساب m بطريقة تقريبية عبر حساب الفرق بين قيمة System.currentTimeMillis() في اللحظة التي يتم فيها معرفة أن الاتصال غير مغلق , واللحظة التي يتم فيها الإنتهاء من تمرير request الخاص بذلك الإتصال للواجهة الخارجية.
وبعد القيام بحساب هذة القيمة اكثر من مرة اتضح الاتي:
عند تشغيل الخادم لأول مرة وخدمة اول اتصال m=31ms
بعد ذلك وعند الطلب الثاني نزلت قيمة m الى 0 وظلت هكذا .
(على المستخدم أن يقوم بتجربة الأمر بنفسة )
يمكن تفسير القيمة العالية لm في البداية هو أن الواجهة الخارجية تستخدم لأول مرة .
(راجع البنية الداخلية لAbstractTask ) .
طبعا m=0 هي قيمة تقريبية وبالتأكيد ليست صفرا مطلقا وإنما تدل على أن المدة المستغرقة لخدمة عميل واحد هي اقل من 1ms (اقل من واحد مللي ثانية) .
إذا افترضنا أن m تساوي 0.5 مللي ثانية, فمن للمكن حساب اقصى مدة قد يستغرقها العميل بينما يتم خدمة عميل اخر من العلاقة *m(N-1):

عدد العملاء    اقصى مدة تأخير (ثواني)  
0.49                          1000

4.9995                    10,000 


49.9995                 100,000 
ستلاحظ أن التأخير المحتمل في حالة خدمة مائة الف عميل في نفس الوقت غير مقبولة حيث انها في اقصاها اقرب لخمسين ثانية كاملة.
في الواقع و من الناحية العملية فإن المدة الزمنية هي اقل بكثير , ولا تتحقق كما في الجدول الا عندما يكون الحوار مع كل عميل يتضمن اكثر من طلب واحد, وواقع ان العميل عادة ما يقضي وقتا في معالجة البيانات القادمة من الخادم اكبر بكثير من الوقت الذي يقضية في تكوين الطلبات, يجعل متوسط مدة التأخير في استيعاب الطلبات اقرب لقيمة m (0.0005 ثانية). مهما كان عدد العملاء .
هذا يعني ان القيم في الجدول ممكنة possible , ولكن غير محتملة unlikely
اذا كنت تعتقد أن مسألة التأخير هذة مؤثرة بشكل غير مقبول بالنسبة للخادم الذي تريد أن تنشأة, فلا تستخدم هذة الأداة (على الأقل في نسختها الحالية).
بقية اوجة القصور متعلقة – بشكل او بأخر – بالواجهة الخارجية حيث انها مسؤولة عن اكثر من 50% من الأداء النهائي للخادم.
نصائح للواجهة الخارجية:
الكود الذي ستكتبة هنا في الواجهة الخارجية ( للدالة process()) هو الذي سيحدد فعالية خادمك النهائية.
تلك الدالة سيتم استدعائها بشكل متتالي مع طلبات العملاء , وهذا لايعني أن تقوم بمعاجة هذة الطلبات بشكل متتالي ايضا ,نصيحتي هي أن تبتعد عن المعالجة المتتالية وتستخدم حوض خيوط fixed thread pool ثم تقوم بتمرير كل طلب للحوض حيث يتم معالجة كل طلب بشكل منفصل.
الفائدة من هذة الطريقة , بغض النظر عن السرعة , فهي ستمكنك من معالجة الطلبات القادمة من نفس قناة الإتصال بشكل متوازي. وهذة ميزة من الصعب اضافتها للخوادم التي تستخدم thread لكل عميل لأنها مكلفة .
قد تقول ماذا لو ارسل العميل طلبات كثيرة وقام بشغل اكثر من thread ؟
واللة هذا – وامور اخرى كثيرة مثل من سيغلق الإتصال انا ام العميل – راجع لك وللهدف من الخادم والبروتوكول الذي تستخدمة .
نقطة أخيرة : قناة الإتصال مع العميل هي للطلبات requests والردود response فقط , يعني خاصة بالبروتوكول التخاطبي بين العميل و الخادم . لا تستخدمها لنقل البيانات الثنائية.
في حالة الحاجة (كإستلام طلب من العميل لرفع ملف مثلا (, قم بإنشاء إتصال جديد مع جهاز العميل واستخدمة لنقل البيانات. (تماما مثل بروتوكول FTP حيث يتم إستخدام قناتي اتصال مع كل عميل, واحدة للأوامرcommands وواحدة للبيانات ).

No comments:

Post a Comment