مقدمة
في بيئة عديمة الحالة مثل تطبيق الويب، ليس لفهم مفهوم حالة الجلسة أي معنى حقيقي. ومع ذلك، تعتبر الإدارة الفعالة للحالة ميزة ضرورية لمعظم تطبيقات الويب. يوفر Microsoft ASP.NET، بالإضافة إلى العديد من بيئات البرمجة الأخرى من جانب الخادم، طبقة تجريد تسمح للتطبيقات بتخزين البيانات المستمرة على أساس كل مستخدم وكل تطبيق.
من المهم ملاحظة أن حالة جلسة تطبيق الويب هي البيانات التي يقوم التطبيق بتخزينها مؤقتًا واستردادها عبر طلبات مختلفة. تمثل الجلسة كافة الطلبات التي يرسلها المستخدم أثناء اتصاله بالموقع، وحالة الجلسة هي مجموعة البيانات المستمرة التي تم إنشاؤها واستهلاكها من قبل المستخدم أثناء الجلسة. حالة كل جلسة مستقلة عن بعضها البعض وتتوقف عن الوجود عند انتهاء جلسة المستخدم.
لا تحتوي حالة الجلسة على أي مراسلات مع أي من الكيانات المنطقية التي تشكل بروتوكول HTTP ومواصفاته. الجلسات عبارة عن طبقة تجريد تم إنشاؤها بواسطة بيئات التطوير من جانب الخادم مثل ASP وASP.NET التقليدي. تعتمد الطريقة التي يعرض بها ASP.NET حالة الجلسة وكيفية تنفيذ حالة الجلسة داخليًا على البنية الأساسية للنظام الأساسي. لذلك، يقوم ASP وASP.NET التقليدي بتنفيذ حالة الجلسة بطرق مختلفة تمامًا، ومن المتوقع إجراء المزيد من التحسينات والتحسينات في الإصدار التالي من ASP.NET.
تتناول هذه المقالة كيفية تنفيذ حالة الجلسة في ASP.NET 1.1 وكيفية تحسين إدارة حالة الجلسة في تطبيقات الويب المُدارة.
نظرة عامة على حالة جلسة ASP.NET
حالة الجلسة ليست جزءًا من البنية التحتية لـ HTTP. أي أنه يجب أن يكون هناك مكون هيكلي يربط حالة الجلسة بكل طلب وارد. يمكن لبيئة التشغيل (ASP أو ASP.NET التقليدية) قبول كلمات رئيسية مثل الجلسة واستخدامها للإشارة إلى كتلة البيانات المخزنة على الخادم. لحل الاستدعاءات إلى كائن الجلسة بنجاح، يجب أن تضيف بيئة وقت التشغيل حالة الجلسة إلى سياق الاستدعاء للطلب الذي تتم معالجته. تختلف كيفية القيام بذلك بين الأنظمة الأساسية، ولكنها أساسية لتطبيقات الويب ذات الحالة.
في ASP التقليدي، يتم تنفيذ حالة الجلسة ككائنات COM ذات ترابط حر موجودة في مكتبة asp.dll. (هل أنت فضولي؟ إن CLSID لهذا الكائن هو في الواقع D97A6DA0-A865-11cf-83AF-00A0C90C2BD8.) يقوم هذا الكائن بتخزين البيانات المنظمة كمجموعة من أزواج الاسم/القيمة. يمثل العنصر النائب "الاسم" المفتاح المستخدم لاسترداد المعلومات، بينما يمثل العنصر النائب "القيمة" ما تم تخزينه في حالة الجلسة. يتم تجميع أزواج الاسم/القيمة حسب معرف الجلسة بحيث يرى كل مستخدم فقط أزواج الاسم/القيمة التي قام بإنشائها.
في ASP.NET، تكون واجهة البرمجة لحالة الجلسة هي نفسها تقريبًا في ASP التقليدي. لكن تطبيقاتها الأساسية مختلفة تمامًا، فالأولى أكثر مرونة وقابلة للتطوير ولديها قدرات برمجية أقوى من الأخيرة. قبل أن نتعمق في حالة جلسة ASP.NET، دعنا نراجع بإيجاز بعض القدرات الهيكلية للبنية التحتية لجلسة ASP.NET.
في ASP.NET، يتم إرسال أي طلب HTTP وارد عبر وحدة HTTP النمطية. يمكن لكل وحدة تصفية وتعديل الكمية الكبيرة من المعلومات التي يحملها الطلب. تسمى المعلومات المرتبطة بكل طلب "سياق الاستدعاء" الذي يمثله كائن HttpContext في البرمجة. لا ينبغي لنا أن نفكر في سياق الطلب باعتباره حاوية أخرى لمعلومات الحالة، على الرغم من أن مجموعة العناصر التي يوفرها هي مجرد حاوية بيانات. يختلف كائن HttpContext عن كافة كائنات الحالة الأخرى (على سبيل المثال، الجلسة والتطبيق وذاكرة التخزين المؤقت) حيث أن له عمرًا محدودًا يتجاوز الوقت المطلوب لمعالجة الطلب. عندما يمر طلب عبر سلسلة من وحدات HTTP المسجلة، سيحتوي كائن HttpContext الخاص به على مرجع لكائن الحالة. عندما يمكن معالجة الطلب أخيرًا، يرتبط سياق الاستدعاء المرتبط بالجلسة المحددة (الجلسة) وكائنات الحالة العامة (التطبيق وذاكرة التخزين المؤقت).
وحدة HTTP المسؤولة عن تحديد حالة جلسة كل مستخدم هي SessionStateModule. تم تصميم هيكل هذه الوحدة بناءً على واجهة IHttpModule، التي توفر عددًا كبيرًا من الخدمات المتعلقة بحالة الجلسة لتطبيقات ASP.NET. يتضمن إنشاء معرفات الجلسة، وإدارة الجلسة بدون ملفات تعريف الارتباط، واسترداد بيانات الجلسة من موفري الحالة الخارجيين، وربط البيانات بسياق الاتصال الخاص بالطلب.
لا تقوم وحدة HTTP بتخزين بيانات الجلسة داخليًا. يتم حفظ حالة الجلسة دائمًا في مكون خارجي يسمى "موفر الحالة". يقوم موفر الحالة بتغليف بيانات حالة الجلسة بالكامل ويتواصل مع الأجزاء الأخرى من خلال أساليب واجهة IStateClientManager. تستدعي وحدة HTTP الخاصة بحالة الجلسة الأساليب الموجودة على هذه الواجهة لقراءة حالة الجلسة وحفظها. يدعم ASP.NET 1.1 ثلاثة موفري حالة مختلفين، كما هو موضح في الجدول 1.
الجدول 1:وصف موفر
موفر حالة العميل
تظل قيم جلسة InProc كائنات نشطة في ذاكرة العملية المنفذة لـ ASP.NET (aspnet_wp.exe أو w3wp.exe في Microsoft® Windows Server® 2003). هذا هو الخيار الافتراضي.
يتم إجراء تسلسل لقيم جلسة StateServer وتخزينها في الذاكرة في عملية منفصلة (aspnet_state.exe). يمكن أيضًا تشغيل العملية على أجهزة كمبيوتر أخرى.
يتم إجراء تسلسل لقيم جلسة SQLServer وتخزينها في جداول Microsoft® SQL Server®. يمكن تشغيل مثيلات SQL Server محليًا أو عن بعد.
تقرأ وحدة HTTP لحالة الجلسة موفر الحالة المحدد حاليًا من قسم <sessionState> في ملف web.config.
<sessionState mode="InProc | StateServer | SQLServer />؛
اعتمادًا على قيمة سمة الوضع، سيتم استرداد حالة الجلسة من عمليات مختلفة وتخزينها في عمليات مختلفة من خلال خطوات مختلفة. افتراضيًا، يتم تخزين حالة الجلسة في الحالة المحلية عملية عاملة لـ ASP.NET، في حالات خاصة، يتم تخزينها في فتحة مخصصة لكائن ASP.NET Cache (لا يمكن الوصول إليها برمجيًا). ويمكن أيضًا تخزين حالة الجلسة خارجيًا، حتى في عملية بعيدة (على سبيل المثال، في نظام التشغيل Windows.NET). NT المسمى aspnet_state.exe). الخيار الثالث هو تخزين حالة الجلسة في جدول قاعدة بيانات مخصص يديره SQL Server 2000.
تقوم وحدة HTTP بإلغاء تسلسل قيمة الجلسة في بداية الطلب، مما يجعلها كائنات القاموس (في الواقع، يتم الوصول إلى الكائنات من النوع HttpSessionState) برمجيًا من خلال خاصية الجلسة التي تعرضها الفئات (على سبيل المثال، HttpContext وPage). ويستمر الارتباط حتى انتهاء الطلب. إذا اكتمل الطلب بنجاح، فسيتم إجراء تسلسل لجميع قيم الحالة إلى موفر الحالة والمتاح للطلبات الأخرى،
يوضح الشكل 1 الاتصال بين صفحة ASP.NET المطلوبة وقيم الجلسة، ويرتبط الكود الذي تستخدمه كل صفحة بسمة الجلسة في فئة الصفحة نفس ما هو الحال مع ASP التقليدية.
الشكل 1: بنية حالة الجلسة في ASP.NET 1.1
القيمة الفعلية لحالة الجلسة مقفلة للوقت المطلوب لإكمال الطلب. تتم إدارة هذا القفل داخليًا بواسطة وحدة HTTP ويستخدم لمزامنة الوصول إلى حالة الجلسة.
تعمل وحدة حالة الجلسة على إنشاء مثيل لموفر حالة التطبيق وتهيئته بالمعلومات المقروءة من ملف web.config. بعد ذلك، سيواصل كل مزود عمليات التهيئة الخاصة به. اعتمادًا على نوع الموفر، ستختلف عمليات التهيئة بشكل كبير. على سبيل المثال، سيقوم مدير حالة SQL Server بفتح اتصال بقاعدة بيانات معينة، بينما سيقوم مدير خارج العملية بالتحقق من منفذ TCP المحدد. من ناحية أخرى، سيقوم مدير حالة InProc بتخزين مرجع إلى وظيفة رد الاتصال. يتم تنفيذ هذا الإجراء عند إزالة عنصر من ذاكرة التخزين المؤقت واستخدامه لتشغيل حدث Session_OnEnd الخاص بالتطبيق.
الوصول المتزامن إلى حالة الجلسة
ماذا يحدث عندما تقوم صفحة ويب بإجراء اتصال بسيط وبديهي للغاية لخاصية الجلسة؟ يتم تنفيذ العديد من العمليات في الخلفية، كما هو موضح في التعليمات البرمجية المرهقة التالية:
int siteCount = Convert.ToInt32(Session["Counter"]);
يصل الكود أعلاه فعليًا إلى قيمة الجلسة التي أنشأتها وحدة HTTP في الذاكرة المحلية يقرأ البيانات من مزود حالة محدد (انظر الشكل 1). ماذا يحدث إذا حاولت الصفحات الأخرى أيضًا الوصول إلى حالة الجلسة بشكل متزامن؟ في هذه الحالة، قد يتوقف الطلب الحالي عن معالجة البيانات غير المتناسقة أو القديمة. لتجنب ذلك، ستنفذ وحدة حالة الجلسة آلية قفل القارئ/الكاتب وقائمة الانتظار للوصول إلى قيم الحالة. ستحتفظ الصفحات التي تتمتع بأذونات الكتابة في حالة الجلسة بقفل الكاتب لتلك الجلسة حتى ينتهي الطلب.
يمكن للصفحة أن تطلب إذن الكتابة لحالة الجلسة عن طريق تعيين خاصية EnableSessionState لتوجيه @Page إلى true. (هذا هو الإعداد الافتراضي). ومع ذلك، يمكن أن يكون للصفحة أيضًا حق الوصول للقراءة فقط إلى حالة الجلسة، على سبيل المثال، عند تعيين الخاصية EnableSessionState إلى ReadOnly. في هذه الحالة، ستحتفظ الوحدة بقفل القارئ لتلك الجلسة حتى ينتهي طلب تلك الصفحة. ونتيجة لذلك سوف تحدث القراءات المتزامنة.
إذا قام طلب الصفحة بتعيين قفل للقارئ، فلن تتمكن الطلبات المتزامنة الأخرى في نفس الجلسة من تحديث حالة الجلسة، ولكنها ستكون قادرة على القراءة على الأقل. أي أنه إذا كان طلب القراءة فقط قيد المعالجة حاليًا لجلسة ما، فسيكون لطلب القراءة فقط المعلق أولوية أعلى من الطلب الذي يتطلب الوصول الكامل. إذا قام طلب صفحة بتعيين قفل كاتب لحالة الجلسة، فسيتم حظر جميع الصفحات الأخرى بغض النظر عما إذا كانت تريد قراءة المحتوى أو كتابته. على سبيل المثال، إذا حاول إطاران الكتابة إلى الجلسة في نفس الوقت، فيجب أن ينتظر إطار واحد حتى ينتهي الإطار الآخر قبل أن يتمكن من الكتابة.
مقارنة موفري الحالة
بشكل افتراضي، تقوم تطبيقات ASP.NET بتخزين حالة الجلسة في ذاكرة العملية المنفذة، وتحديدًا في فتحة مخصصة لكائن ذاكرة التخزين المؤقت. عند تحديد وضع InProc، يتم تخزين حالة الجلسة في فتحات داخل كائن ذاكرة التخزين المؤقت. تم وضع علامة على هذه الفتحة على أنها فتحة خاصة ولا يمكن الوصول إليها برمجيًا. بمعنى آخر، إذا قمت بتعداد كافة العناصر الموجودة في ذاكرة التخزين المؤقت لبيانات ASP.NET، فلن يتم إرجاع أي كائنات مشابهة لحالة جلسة العمل المحددة. توفر كائنات ذاكرة التخزين المؤقت نوعين من الفتحات: الفتحات الخاصة والفتحات العامة. يمكن للمبرمجين إضافة الفتحات العامة والتعامل معها، ولكن لا يمكن استخدام الفتحات الخاصة إلا بواسطة النظام (على وجه التحديد، الفئات المحددة في جزء system.web).
تشغل حالة كل جلسة نشطة فتحة مخصصة في ذاكرة التخزين المؤقت. تتم تسمية الفتحة بناءً على معرف الجلسة، وقيمتها هي مثيل لفئة داخلية غير معلنة تسمى SessionStateItem. يحصل موفر حالة InProc على معرف الجلسة ويسترد العنصر المقابل في ذاكرة التخزين المؤقت. يتم بعد ذلك إدخال محتويات كائن SessionStateItem في كائن قاموس HttpSessionState ويمكن الوصول إليها بواسطة التطبيق من خلال خاصية الجلسة. لاحظ أن هناك خطأ في ASP.NET 1.0 يجعل الفتحات الخاصة لكائن ذاكرة التخزين المؤقت قابلة للتعداد برمجيًا. إذا قمت بتشغيل التعليمة البرمجية التالية ضمن ASP.NET 1.0، فستتمكن من تعداد العناصر المقابلة للكائنات الموجودة في كل حالة جلسة عمل نشطة حاليًا.
foreach (عنصر إدخال القاموس في ذاكرة التخزين المؤقت)
{
Response.Write(elem.Key + ": " + elem.Value.ToString());
}
تم حل هذا الخطأ في ASP.NET 1.1 وعندما تقوم بتعداد المحتويات المخزنة مؤقتًا، لن يتم سرد أية فتحات للنظام بعد الآن.
ربما يكون InProc هو خيار الوصول الأسرع على الإطلاق. لكن ضع في اعتبارك أنه كلما زاد عدد البيانات المخزنة في الجلسة، زاد استهلاك خادم الويب للذاكرة، مما قد يزيد من خطر تدهور الأداء. إذا كنت تخطط لاستخدام أي حل خارج العملية، فيجب عليك دراسة التأثيرات المحتملة للتسلسل وإلغاء التسلسل بعناية. يستخدم الحل خارج العملية خدمة Windows NT (aspnet_state.exe) أو جدول SQL Server لتخزين قيم جلسة العمل. ولذلك، تظل حالة الجلسة خارج العملية المنفذة لـ ASP.NET، ويلزم وجود طبقات إضافية من التعليمات البرمجية لإجراء تسلسل وإلغاء التسلسل بين حالة الجلسة ووسيط التخزين الفعلي. يحدث هذا عندما تتم معالجة الطلب ويجب بعد ذلك تحسينه إلى أعلى درجة.
نظرًا لأن بيانات الجلسة تحتاج إلى النسخ من المستودع الخارجي إلى قاموس الجلسة المحلية، فإن الطلب يؤدي إلى تدهور الأداء يتراوح من 15% (خارج العملية) إلى 25% (SQL Server). لاحظ أنه على الرغم من أن هذا مجرد تقدير تقريبي، إلا أنه يجب أن يكون قريبًا من الحد الأدنى من التأثير، وسيكون التأثير الأقصى أعلى بكثير من هذا. في الواقع، هذا التقدير لا يأخذ في الاعتبار بشكل كامل مدى تعقيد الأنواع المحفوظة بالفعل في حالة الجلسة.
في سيناريو التخزين خارج العملية، تظل حالة الجلسة لفترة أطول، مما يجعل التطبيق أكثر قوة لأنه يحمي من فشل خدمات معلومات الإنترنت Microsoft® (IIS) وASP.NET. من خلال فصل حالة الجلسة عن التطبيقات، يمكنك أيضًا توسيع التطبيقات الموجودة بسهولة أكبر إلى معماريات Web Farm وWeb Garden. بالإضافة إلى ذلك، يتم تخزين حالة الجلسة في عملية خارجية، مما يؤدي بشكل أساسي إلى التخلص من مخاطر فقدان البيانات بشكل دوري بسبب حلقات العملية.
فيما يلي كيفية استخدام خدمات Windows NT. كما هو مذكور أعلاه، فإن خدمة NT هي عملية تسمى aspnet_state.exe، وتقع عادةً في المجلد C:WINNTMicrosoft.NETFrameworkv1.1.4322.
يعتمد الدليل الفعلي على إصدار Microsoft® .NET Framework الذي تقوم بتشغيله بالفعل. قبل استخدام خادم الحالة، يجب عليك التأكد من أن الخدمة جاهزة وتعمل على الكمبيوتر المحلي أو البعيد الذي يتم استخدامه كجهاز تخزين الجلسة. تعد خدمة الحالة جزءًا من ASP.NET ويتم تثبيتها باستخدامه، لذا لا تحتاج إلى تشغيل برنامج تثبيت إضافي. افتراضيًا، خدمة الحالة ليست قيد التشغيل ويجب تشغيلها يدويًا. سيحاول تطبيق ASP.NET إنشاء اتصال بخادم الحالة فورًا بعد تحميله. ولذلك، يجب أن تكون الخدمة جاهزة وقيد التشغيل، وإلا سيتم طرح استثناء HTTP. الصورة التالية توضح مربع حوار الخصائص الخاص بالخدمة.
الشكل 2: مربع حوار خصائص خادم حالة ASP.NET
تحتاج تطبيقات ASP.NET إلى تحديد عنوان TCP/IP للكمبيوتر الذي توجد به خدمة حالة الجلسة. يجب إدخال الإعدادات التالية في ملف web.config الخاص بالتطبيق.
<التكوين>؛
<system.web>;
<sessionState
الوضع = "خادم الدولة"
stateConnectionString="tcpip=expoware:42424" />;
</system.web>;
</configuration>;
تحتوي سمة StateConnectionString على عنوان IP الخاص بالكمبيوتر والمنفذ المستخدم لتبادل البيانات. عنوان الكمبيوتر الافتراضي هو 127.0.0.1 (مضيف محلي) والمنفذ الافتراضي هو 42424. يمكنك أيضًا الإشارة إلى الكمبيوتر بالاسم. يعد استخدام جهاز كمبيوتر محلي أو بعيد أمرًا شفافًا تمامًا بالنسبة للتعليمات البرمجية. لاحظ أنه لا يمكن استخدام أحرف غير ASCII في الاسم، كما أن رقم المنفذ إلزامي.
إذا كنت تستخدم وحدة تخزين جلسة العمل خارج العملية، فستظل حالة الجلسة موجودة وستكون متاحة للاستخدام المستقبلي بغض النظر عما يحدث لعملية ASP.NET العاملة. في حالة انقطاع الخدمة، سيتم الاحتفاظ بالبيانات واسترجاعها تلقائيًا عند استعادة الخدمة. ومع ذلك، إذا توقفت خدمة موفر الحالة أو فشلت، فسيتم فقدان البيانات. إذا كنت تريد أن يكون تطبيقك قويًا، فاستخدم وضع SQLServer بدلاً من وضع StateServer.
<التكوين>؛
<system.web>;
<sessionState
الوضع = "SQLسيرفر"
sqlConnectionString="server=127.0.0.1;uid=<معرف المستخدم>;;pwd=<كلمة المرور>;;"
</system.web>;
</configuration>؛
يمكنك تحديد سلسلة الاتصال من خلال سمة sqlConnectionString. لاحظ أن سلسلة السمة يجب أن تحتوي على معرف المستخدم وكلمة المرور واسم الخادم. لا يمكن أن تحتوي على علامات مثل قاعدة البيانات والكتالوج الأولي لأن هذه المعلومات افتراضية باسم ثابت. يمكن استبدال معرفات المستخدم وكلمات المرور بإعدادات الأمان المتكاملة.
كيفية إنشاء قاعدة بيانات؟ يوفر ASP.NET زوجين من البرامج النصية لتكوين بيئة قاعدة البيانات. تمت تسمية الزوج الأول من البرامج النصية باسم InstallSqlState.sql وUninstallSqlState.sql، وهما موجودان في نفس المجلد مثل خدمة Session State NT. يقومون بإنشاء قاعدة بيانات باسم ASPState والعديد من الإجراءات المخزنة. ومع ذلك، يتم تخزين البيانات في قاعدة بيانات TempDB لمنطقة التخزين المؤقتة لـ SQL Server. وهذا يعني أنه في حالة إعادة تشغيل جهاز الكمبيوتر SQL Server، سيتم فقدان بيانات الجلسة.
للتغلب على هذا القيد، استخدم زوجًا ثانيًا من البرامج النصية. تمت تسمية الزوج الثاني من البرامج النصية باسم InstallPersistSqlState.sql وUninstallPersistSqlState.sql. في هذه الحالة، يتم إنشاء قاعدة بيانات ASPState، ولكن يتم إنشاء الجداول في نفس قاعدة البيانات وتكون ثابتة أيضًا. عند تثبيت دعم SQL Server لجلسات العمل، يتم أيضًا إنشاء مهمة لحذف جلسات العمل منتهية الصلاحية في قاعدة بيانات حالة جلسة العمل. تُسمى المهمة ASPState_Job_DeleteExpiredSessions وهي قيد التشغيل دائمًا. يرجى ملاحظة أنه لكي تعمل هذه المهمة بشكل صحيح، يجب تشغيل خدمة SQLServerAgent.
بغض النظر عن الوضع الذي تختاره، فإن طريقة ترميز عمليات حالة الجلسة لا تتغير. يمكنك دائمًا العمل على خاصية الجلسة وقراءة القيم وكتابتها كالمعتاد. يتم التعامل مع جميع الاختلافات في السلوك على مستوى أدنى من التجريد. ربما يكون تسلسل الحالة هو الفرق الأكثر أهمية بين أوضاع الجلسة.
تسلسل الحالة وإلغاء التسلسل
عند استخدام وضع التشغيل، يتم تخزين الكائنات في حالة الجلسة كمثيلات نشطة للفئات الخاصة بها. إذا لم يحدث أي تسلسل أو إلغاء تسلسل حقيقي، فهذا يعني أنه يمكنك بالفعل تخزين أي كائن تقوم بإنشائه في الجلسة (بما في ذلك الكائنات التي لا يمكن إجراء تسلسل لها وكائنات COM)، ولن يكون الوصول إليها مكلفًا للغاية. إذا اخترت مزود حالة خارج العملية، فهذه قصة أخرى.
في البنية خارج العملية، يتم نسخ قيم الجلسة من وسائط التخزين المحلية (قاعدة بيانات AppDomain الخارجية) إلى ذاكرة AppDomain التي تعالج الطلب. مطلوب طبقة تسلسل/إلغاء تسلسل لإنجاز هذه المهمة وتمثل إحدى التكاليف الرئيسية لموفري الحالة خارج العملية. التأثير الرئيسي لهذا الموقف على التعليمات البرمجية الخاصة بك هو أنه يمكن تخزين الكائنات القابلة للتسلسل فقط في قاموس الجلسة.
يستخدم ASP.NET طريقتين لإجراء تسلسل للبيانات وإلغاء تسلسلها، اعتمادًا على نوع البيانات المعنية. بالنسبة للأنواع الأساسية، يستخدم ASP.NET مُسلسلًا داخليًا محسّنًا؛ أما بالنسبة للأنواع الأخرى، بما في ذلك الكائنات والفئات المعرفة من قبل المستخدم، يستخدم ASP.NET المنسق الثنائي .NET. تتضمن الأنواع الأساسية السلاسل وأوقات التاريخ والقيم المنطقية والبايتات والأحرف وجميع الأنواع الرقمية. بالنسبة لهذه الأنواع، يعد استخدام برنامج تسلسلي مخصص أسرع من استخدام المنسق الثنائي الافتراضي .NET.
لا يتم إصدار أو توثيق المُسلسِل المُحسّن علنًا. إنه مجرد قارئ/كاتب ثنائي ويستخدم بنية تخزين بسيطة ولكنها فعالة. يستخدم المُسلسل فئة BinaryWriter لكتابة تمثيل بايت للنوع، ثم يكتب تمثيل بايت للقيمة المقابلة لهذا النوع. عند قراءة وحدات البايت المتسلسلة، تقوم الفئة أولاً باستخراج بايت، وتكتشف نوع البيانات المراد قراءتها، ثم تستدعي أسلوب ReadXxx الخاص بالنوع في فئة BinaryReader.
لاحظ أن حجم الأنواع المنطقية والرقمية معروف جيدًا، ولكن ليس بالنسبة للسلاسل. في تدفق البيانات الأساسي، تكون السلسلة دائمًا مسبوقة بطول ثابت (رمز صحيح مكون من 7 بتات مكتوب في المرة الواحدة)، ويستخدم القارئ هذه الحقيقة لتحديد الحجم الصحيح للسلسلة. يتم حفظ قيمة التاريخ عن طريق كتابة العدد الإجمالي للرموز المميزة التي تشكل التاريخ فقط. ولذلك، لإجراء تسلسل للجلسة، يجب أن يكون التاريخ من النوع Int64.
يمكنك استخدام فئة BinaryFormatter لإجراء عمليات التسلسل على كائنات أكثر تعقيدًا (بالإضافة إلى كائنات مخصصة) طالما تم وضع علامة على الفئة التي تحتوي عليها على أنها قابلة للتسلسل. يتم تحديد جميع الأنواع غير الأساسية بواسطة نفس معرف النوع ويتم تخزينها في نفس دفق البيانات مثل الأنواع الأساسية. بشكل عام، يمكن أن تؤدي عمليات التسلسل إلى انخفاض الأداء بنسبة 15% إلى 25%. ومع ذلك، لاحظ أن هذا تقدير تقريبي يعتمد على افتراض استخدام الأنواع الأساسية. كلما كانت الأنواع المستخدمة أكثر تعقيدًا، زادت النفقات العامة.
من الصعب تنفيذ تخزين بيانات الجلسة بكفاءة دون الاستخدام المكثف للأنواع البدائية. لذلك، من الناحية النظرية على الأقل، يعد استخدام ثلاث فتحات جلسة لحفظ ثلاث خصائص سلسلة مختلفة لكائن ما أفضل من إجراء تسلسل للكائن بأكمله. ولكن ماذا لو كان الكائن الذي تريد إجراء تسلسل له يحتوي على 100 خاصية؟ هل تريد استخدام 100 فتحة أم فتحة واحدة فقط؟ في كثير من الحالات، يكون الأسلوب الأفضل هو تحويل نوع معقد إلى عدة أنواع أبسط. يعتمد هذا الأسلوب على محولات النوع. "محول النوع" هو مُسلسل خفيف الوزن يُرجع الخصائص الرئيسية للنوع كمجموعة من السلاسل. محولات النوع هي فئات خارجية مرتبطة بفئة أساسية باستخدام السمات. الأمر متروك لكاتب النوع لتحديد الخصائص التي سيتم حفظها وكيفية حفظها. تعد محولات النوع مفيدة أيضًا لتخزين ViewState وتمثل طريقة أكثر كفاءة لتخزين الجلسة من المنسقات الثنائية.
دورة حياة الجلسة
إحدى النقاط المهمة حول إدارة جلسة ASP.NET هي أن دورة حياة كائن حالة الجلسة تبدأ فقط عند إضافة العنصر الأول إلى قاموس الذاكرة. تعتبر جلسة ASP.NET قد بدأت فقط بعد تنفيذ مقتطف التعليمات البرمجية التالي.
Session["MySlot"] = "Some data";
يحتوي قاموس الجلسة عادةً على نوع الكائن لقراءة البيانات بشكل عكسي، يجب تحويل القيمة التي تم إرجاعها إلى نوع أكثر تحديدًا.
string data = (string) Session["MySlot"]؛
عندما تحفظ الصفحة البيانات في الجلسة، سيتم تحميل القيمة في فئة القاموس المصممة خصيصًا والموجودة في فئة HttpSessionState. يتم تحميل محتويات القاموس إلى موفر الحالة عند اكتمال الطلب الذي تتم معالجته حاليًا. إذا كانت حالة الجلسة فارغة لأنه لم يتم وضع البيانات في القاموس برمجيًا، فلن يتم إجراء تسلسل للبيانات إلى وسيط التخزين، والأهم من ذلك، لن يتم تقديمها في ASP.NET Cache أو SQL Server أو NT State Services Create فتحة لتتبع الجلسة الحالية. وذلك لأسباب تتعلق بالأداء، ولكن له تأثير مهم على كيفية التعامل مع معرفات الجلسة: سيتم إنشاء معرف جلسة جديد لكل طلب حتى يتم تخزين بعض البيانات في قاموس الجلسة.
عندما يكون من الضروري ربط حالة الجلسة بطلب قيد المعالجة، تقوم وحدة HTTP باسترداد معرف الجلسة (إذا لم يكن الطلب البادئ) وتبحث عنه في موفر الحالة الذي تم تكوينه. إذا لم يتم إرجاع أي بيانات، تقوم وحدة HTTP بإنشاء معرف جلسة جديد للطلب. يمكن اختبار ذلك بسهولة من خلال الصفحة التالية:
<%@ Page Language="C#" Trace="true" %>;
</html>;
<الجسم>؛
<form runat="server">;
<asp:button runat="server" text="انقر" />;
</نموذج>;
</الجسم>;
</html>;
عندما تنقر على الزر وتعود إلى الصفحة، سيتم إنشاء معرف جلسة جديد وسيتم تسجيل معلومات التتبع.
الشكل 3: في التطبيق الذي لا يقوم بتخزين البيانات في قاموس الجلسة، يتم إنشاء معرف جلسة جديد لكل طلب.
ماذا عن حدث Session_OnStart؟ هل سيتم رفع الحدث لكل طلب أيضاً؟ إذا قام تطبيقك بتعريف معالج Session_OnStart، فسيتم حفظ حالة الجلسة دائمًا، حتى لو كانت حالة الجلسة فارغة. ولذلك، يكون معرف الجلسة ثابتًا دائمًا لجميع الطلبات بعد الطلب الأول. استخدم معالج Session_OnStart فقط عند الضرورة القصوى.
إذا انتهت مهلة الجلسة أو تم التخلي عنها، فلن يتغير معرف الجلسة الخاص بها في المرة التالية التي يتم فيها الوصول إلى التطبيق عديم الحالة. وهو مصمم بحيث يستمر معرف الجلسة حتى نهاية جلسة المتصفح، حتى لو انتهت صلاحية حالة الجلسة. أي أنه يتم دائمًا استخدام نفس معرف الجلسة لتمثيل جلسات متعددة طالما أن مثيل المتصفح هو نفسه.
يمثل حدث Session_OnEnd نهاية الجلسة ويستخدم لتنفيذ أي كود تنظيف مطلوب لإنهاء الجلسة. ومع ذلك، لاحظ أن هذا الحدث مدعوم فقط في وضع InProc، أي فقط عندما يتم تخزين بيانات الجلسة في عملية عاملة لـ ASP.NET. لكي يتم رفع الحدث Session_OnEnd، يجب أن تكون حالة الجلسة موجودة أولاً، مما يعني أنه يجب تخزين بعض البيانات في حالة الجلسة، ويجب إكمال طلب واحد على الأقل.
في وضع InProc، يتم إضافة حالة الجلسة إلى ذاكرة التخزين المؤقت كعناصر ويتم منحها سياسة وقت انتهاء الصلاحية المتغير. انتهاء الصلاحية المتغير يعني أنه إذا لم يتم استخدام العنصر خلال فترة زمنية معينة، فسيتم حذفه. سيتم إعادة تعيين وقت انتهاء صلاحية أي طلبات تمت معالجتها خلال هذه الفترة. يتم تعيين الفاصل الزمني لعنصر حالة الجلسة على مهلة الجلسة. التقنية المستخدمة لإعادة تعيين وقت انتهاء صلاحية حالة الجلسة بسيطة للغاية وبديهية: تقوم وحدة HTTP للجلسة ببساطة بقراءة عناصر حالة الجلسة المخزنة في ذاكرة التخزين المؤقت لـ ASP.NET. إذا كانت البنية الداخلية لكائن ASP.NET Cache معروفة، فستقوم الوحدة بإجراء عمليات حسابية لإعادة تعيين وقت انتهاء صلاحية المتغير. لذا، عند انتهاء صلاحية العنصر المخزن مؤقتًا، تكون مهلة الجلسة قد انتهت.
ستتم إزالة العناصر منتهية الصلاحية تلقائيًا من ذاكرة التخزين المؤقت. تمثل وحدة جلسة الحالة أيضًا وظيفة رد اتصال الحذف كجزء من سياسة وقت انتهاء الصلاحية لهذا المشروع. ستقوم ذاكرة التخزين المؤقت تلقائيًا باستدعاء وظيفة الحذف، والتي ستقوم بعد ذلك برفع الحدث Session_OnEnd. إذا كان أحد التطبيقات ينفذ إدارة الجلسة من خلال مكون خارج العملية، فلن يتم إطلاق الحدث النهائي.
جلسات عمل بدون ملفات تعريف الارتباط
يتم تعريف كل جلسة ASP.NET نشطة باستخدام سلسلة 120 بت تتكون فقط من الأحرف المسموح بها بواسطة عنوان URL. يتم إنشاء معرف الجلسة باستخدام موفر التشفير لمولد الأرقام العشوائية (RNG). يقوم مزود الخدمة بإرجاع تسلسل مكون من 15 رقمًا تم إنشاؤه عشوائيًا (15 بايت × 8 بت = 120 بت). يتم بعد ذلك تعيين مجموعة الأرقام العشوائية إلى أحرف URL صالحة وإعادتها كسلسلة.
يتم إرسال سلسلة معرف الجلسة إلى المتصفح وإعادتها إلى تطبيق الخادم بإحدى طريقتين: استخدام ملف تعريف الارتباط (تمامًا كما هو الحال في ASP التقليدي) أو عنوان URL المعدل. افتراضيًا، ستقوم وحدة حالة الجلسة بإنشاء ملف تعريف ارتباط HTTP من جانب العميل، ولكن يمكن استخدام عنوان URL المُعدل الذي يتضمن سلسلة معرف الجلسة (خاصة للمتصفحات التي لا تدعم ملفات تعريف الارتباط). تعتمد الطريقة المستخدمة على إعدادات التكوين المخزنة في ملف web.config الخاص بالتطبيق. لتكوين إعدادات الجلسة، يمكنك استخدام قسم <sessionState> والسمة Cookieless.
<sessionState cookieless="true|false" />;
بشكل افتراضي، تكون السمة Cookieless خاطئة، مما يشير إلى استخدام ملفات تعريف الارتباط. في الواقع، ملف تعريف الارتباط هو مجرد ملف نصي يتم وضعه على القرص الصلب الخاص بالعميل بواسطة صفحة ويب. في ASP.NET، يتم تمثيل ملف تعريف الارتباط بواسطة مثيل لفئة HttpCookie. عادةً ما يحتوي ملف تعريف الارتباط على اسم ومجموعة من القيم ووقت انتهاء الصلاحية. عند تعيين السمة Cookieless على false، ستقوم وحدة حالة الجلسة فعليًا بإنشاء ملف تعريف ارتباط باسم ASP.NET_SessionId وتخزين معرف الجلسة فيه. يُظهر الكود المستعار التالي عملية إنشاء ملف تعريف الارتباط:
HttpCookie sessionCookie;
sessionCookie = new HttpCookie("ASP.NET_SessionId", sessionID);
sessionCookie.Path = "/";
وقت انتهاء صلاحية ملف تعريف الارتباط للجلسة قصير جدًا، ويتم تحديث وقت انتهاء الصلاحية بعد نجاح كل طلب. تشير سمة انتهاء الصلاحية لملف تعريف الارتباط إلى وقت انتهاء صلاحية ملف تعريف الارتباط على العميل. إذا لم يتم تعيين ملف تعريف الارتباط للجلسة بشكل صريح، فستكون خاصية انتهاء الصلاحية الافتراضية هي DateTime.MinValue، وهي أصغر وحدة زمنية يسمح بها .NET Framework.
لتعطيل ملفات تعريف الارتباط للجلسة، قم بتعيين سمة Cookieless على true في ملف التكوين كما يلي:
<configuration>;
<system.web>;
<sessionState Cookieless="true" />;
</system.web>;
</configuration>؛
في هذه المرحلة، افترض أنك تطلب الصفحة على عنوان URL التالي:
http://www.contoso.com/sample.aspx
سيكون المحتوى الفعلي المعروض في شريط عنوان المتصفح مختلفًا ويحتوي الآن على معرف الجلسة ، كما هو موضح في:
http://www.contoso.com/(5ylg0455mrvws1uz5mmaau45)/sample.aspx
عند إنشاء مثيل لوحدة HTTP لحالة جلسة العمل، تتحقق الوحدة من قيمة السمة Cookieless. إذا كان صحيحًا، فسيعيد توجيه الطلب (HTTP 302) إلى عنوان URL ظاهري معدل يحتوي على معرف الجلسة الذي يسبق اسم الصفحة مباشرة. عند معالجة الطلب مرة أخرى، سيتم تضمين معرف الجلسة في الطلب. إذا تم تقديم طلب لبدء جلسة جديدة، فإن وحدة HTTP تنشئ معرف جلسة جديد ثم تعيد توجيه الطلب. إذا تم إعادة نشر الطلب، فهذا يعني أن معرف الجلسة موجود بالفعل لأن عمليات إعادة النشر تستخدم عناوين URL نسبية.
عيب استخدام الجلسات بدون ملفات تعريف الارتباط هو فقدان حالة الجلسة إذا تم استدعاء عنوان URL مطلق. عند استخدام ملفات تعريف الارتباط، يمكنك مسح شريط العناوين، والانتقال إلى تطبيق آخر، ثم العودة إلى التطبيق السابق واسترداد نفس قيمة الجلسة. إذا قمت بذلك أثناء تعطيل ملفات تعريف الارتباط للجلسة، فسيتم فقدان بيانات الجلسة. على سبيل المثال، الكود التالي سيقاطع الجلسة:
<a runat="server" href="/code/page.aspx">;Click</a>;
إذا كنت بحاجة إلى استخدام عنوان URL مطلق، فيرجى استخدام بعض الحيل للقيام بذلك قم بتغيير معرف الجلسة المضاف إلى عنوان URL يدويًا. يمكنك استدعاء الأسلوب ApplyAppPathModifier في فئة HttpResponse.
<a رونات = "الخادم"
href=<% =Response.ApplyAppPathModifier("/code/page.aspx")%>;Click</a>;
ستستخدم طريقة ApplyAppPathModifier سلسلة تمثل عنوان URL وتعيد عنوان URL المطلق الذي يتضمن معلومات الجلسة. تعتبر هذه التقنية مفيدة بشكل خاص عندما تحتاج، على سبيل المثال، إلى إعادة التوجيه من صفحة HTTP إلى صفحة HTTPS.
تم تقديم حالة الجلسةالموجزة
في الأصل باستخدام ASP التقليدي كواجهة برمجة تطبيقات قائمة على القاموس والتي مكّنت المطورين من تخزين البيانات المخصصة أثناء الجلسة. في ASP.NET، تدعم حالة الجلسة ميزتين رئيسيتين: تخزين معرف الجلسة ونقله بدون ملفات تعريف الارتباط، وموفري الحالة حيث يتم تخزين بيانات الجلسة فعليًا. لتنفيذ هاتين الإمكانيتين الجديدتين، يستفيد ASP.NET من وحدة HTTP للتحكم في الارتباط بين حالة الجلسة وسياق الطلب الذي تتم معالجته.
في ASP التقليدي، يعني استخدام حالة الجلسة استخدام ملفات تعريف الارتباط. لم يعد هذا هو الحال في ASP.NET، حيث يمكن استخدام بنية بدون ملفات تعريف الارتباط. بفضل قوة وحدة HTTP، يمكن تحليل عنوان URL المطلوب بحيث يحتوي على معرف الجلسة ثم إعادة توجيهه. بعد ذلك، تقوم وحدة HTTP باستخراج معرف الجلسة من عنوان URL واستخدامه لاسترداد أي حالة مخزنة.
يمكن تخزين الحالة الفعلية للجلسة في ثلاثة مواقع: الذاكرة قيد المعالجة، والذاكرة خارج العملية، وجداول SQL Server. يجب إجراء تسلسل/إلغاء تسلسل البيانات قبل أن يتمكن التطبيق من استخدامها. تقوم وحدة HTTP بنسخ قيمة الجلسة من الموفر إلى ذاكرة التطبيق في بداية الطلب. بعد اكتمال الطلب، يتم إرجاع الحالة المعدلة إلى الموفر. سيكون لتواصل البيانات هذا درجات متفاوتة من التأثيرات الضارة على الأداء ، ولكنه سيعزز بشكل كبير الموثوقية والاستقرار ويجعل الدعم لبنيات حديقة الويب وأسهل تنفيذها.