أساسيات التخزين المؤقت للويب Web Caching: ترويسات HTTP واستراتيجيات التخزين المؤقت


kinan mawed

إنّ التخزين المؤقّت Caching للمحتوى بشكل ذكي هو واحد من أكثر الطرق فعاليّة لتحسين التجربة لزوّار موقعنا، إنّ التخزين المؤقّت Caching، أو تخزين المحتوى بشكل مؤقّت من الطلبات السّابقة، هو جزء من لُب استراتيجيّة توصيل المحتوى المُنفَّذة ضمن ميفاق HTTP protocol، تستطيع المُكوِّنات عبر مسار توصيل المحتوى أن تقوم بالتخزين المؤقّت للعناصر لتسريع الطلبات اللاحقة، والتي تكون خاضعة لسياسات التخزين المؤقّت المُصرَّح عنها بالنسبة للمحتوى.

web-caching-http-headers.thumb.png.759d6

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

ترويسات التخزين المؤقت Caching Headers

تعتمد سياسة التخزين المؤقّت على عاملين مختلفين، كيان التخزين المؤقّت والذي يُقرِّر بنفسه أن يقوم بالتخزين المؤقّت للمحتوى المقبول أم لا، فيستطيع أن يُقرِّر أن يُخزِّن بشكل مؤقّت أقل مما هو مسموح له ولا أكثر من ذلك.

يتم تحديد مُعظم سلوك التخزين المؤقّت عن طريق سياسة التخزين المؤقّت، والتي تُوضَع من قبل مالك المحتوى.

تتمحور هذه السياسات بشكل أساسي حول استخدام ترويسات HTTP مُحدّدة.

ومع تحديثات ميفاق HTTP protocol ظهرت بعض الترويسات المختلفة التي تُركِّز على التخزين المؤقّت مع مستويات مختلفة من التعقيد، ولكنّ الترويسات التالية هي التي لا نزال بحاجة إلى الانتباه لها:

  • Expires: إنّ الترويسة Expires واضحة جدًّا، على الرغم من أنّها محدودة المجال، فتقوم بشكل أساسي بتعيين وقت في المستقبل تنتهي فيه صلاحيّة المحتوى، وعند هذه النقطة يجب على أي طلب لهذا المحتوى أن يعود إلى الخادوم الأصل، تُستخدَم هذه الترويسة أفضل ما يُمكن كاحتياط فقط.
  • Cache-Control: وهي البديل الأكثر حداثة للترويسة Expires ومدعومة بشكل جيّد وتقوم بتطبيق تصميم أكثر مرونة، وهي مُفضّلة تقريبًا في كل الحالات على Expires، ولكن لن يضرّنا تعيين قيمة لهما معًا، سنناقش تفاصيل الخيارات التي بإمكاننا تعيينها مع Cache-Control لاحقًا.
  • Etag: تُستخدَم الترويسة Etag في التحقّق من التخزين المؤقّت، حيث يُزوّدنا الخادوم الأصل بترويسة Etag فريدة لأجل العناصر حينما يقوم بتخديم المحتوى في البداية، فعندما يُريد التخزين المؤقّت التحقق من المحتوى الذي يملكه عند انتهاء الصلاحيّة يستطيع إرسال الترويسة Etag التي يملكها من أجل المحتوى إلى الخادوم الأصل والذي إمّا أن يُخبر التخزين المؤقّت بأنّه يملك نفس المحتوى أو يقوم بإرسال المحتوى الجديد (مع ترويسة Etag جديدة).
  • Last-Modified: تُحدِّد هذه الترويسة المرّة الأخيرة التي تمّ فيها تعديل العنصر، ويُمكن استخدامها كجزء من استراتيجيّة التحقّق لضمان محتوى حديث.
  • Content-Length: بالرغم من أنّها لا تُشارِك تحديدًا في التخزين المؤقّت فإنّه من الهام تعيين قيمة لهذه الترويسة عند تعريف سياسات التخزين المؤقّت، حيث ترفض بعض البرمجيّات التخزين المؤقّت للمحتوى إن لم تكن تعلم مُسبقًا حجم هذا المحتوى الذي تحتاج لحجز مساحة له.
  • Vary: يستخدم التخزين المؤقّت بشكل نموذجي المُضيف host المطلوب والمسار إلى الموارد كمفتاح يُخزَّن بواسطته العنصر المطلوب، يُمكِن استخدام الترويسة Vary لنخبر التخزين المؤقّت أن ينتبه إلى ترويسة إضافيّة عندما يُقرّر إذا ما كان الطلب لنفس هذا العنصر، وهي أكثر ما تستخدم لإخبار التخزين الاحتياطي أن تستخدم الترويسة Accept-Encoding كمفتاح أيضًا، بحيث يعلم التخزين المؤقّت كيف يُفرِّق بين المحتوى المضغوط compressed وغير المضغوط.

نظرة جانبية على الترويسة Vary

تزوّدنا الترويسة Vary بالقدرة على تخزين إصدارات مختلفة من نفس المحتوى على حساب تمييع diluting المُدخلات في التخزين المؤقّت.

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

يبدو عنصر مثل User-Agent للوهلة الأولى طريقة جيّدة للتمييز بين متصفّحات الحواسيب ومتصفّحات الهواتف النقّالة لتخديم إصدارات مختلفة لموقعنا، ومع ذلك وبما أنّ السلاسل النصيّة لـ User-Agent ليست معياريّة فستكون النتيجة غالبًا عدّة إصدارات من نفس المحتوى في التخزينات المؤقّتة الوسيطة مع نسبة استخدام تخزين مؤقّت Cache hit ratio ضئيلة، ينبغي استخدام الترويسة Vary باعتدال، خاصّة إن لم نكن نملك القدرة على تقليل تكرار الطلبات في التخزينات المؤقّتة الوسيطة التي نتحكّم بها (والذي قد يكون ممكنًا إن استفدنا من شبكة توصيل محتوى content delivery network على سبيل المثال).

كيف تؤثر أعلام الترويسة Cache-Control على التخزين المؤقت

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

ومن بعض خيارات Cache-Control التي نستطيع استخدامها للنص على سياسة التخزين المؤقّت للمحتوى لدينا نجد:

  • no-cache: تُحدِّد هذه التعليمة أنّه يجب التحقّق من أي محتوى مُخزَّن مؤقّتًا عند كل طلب قبل تخديمه إلى العميل، وتقوم فعليًّا بتحديد المحتوى بأنّه قديم stale فورًا، ولكن تتيح له استخدام تقنيات إعادة التحقّق لتجنّب إعادة تنزيل كامل العنصر مرّة أخرى.
  • no-store: تشير هذه التعليمة إلى أنّه لا يُمكن التخزين المؤقّت للمحتوى بأي طريقة، وهي ملائمة لتعيينها إن كانت الاستجابة تمثّل بيانات حسّاسة.
  • public: تقوم بتحديد المحتوى بأنّه عام ممّا يعني أنّه يمكن التخزين المؤقّت له من قبل المتصفّح أو من قبل أي تخزينات مؤقّتة وسيطة، يتم تحديد الاستجابات بالنسبة للطلبات التي تستخدم استيثاق HTTP بأنّها خاصّة private افتراضيًّا، وتستطيع هذه الترويسة تجاوز ذلك الإعداد.
  • private: تقوم بتحديد المحتوى بأنّه خاص private، والذي يُمكن تخزينه من قبل متصفّح المستخدم ويُمنَع تخزينه بشكل مؤقّت من قبل أي أطراف وسيطة، تستخدم هذه التعليمة غالبًا للبيانات الخاصّة بالمستخدم.
  • max-age: يضبط هذا الإعداد الفترة العظمى التي يُمكن خلالها تخزين المحتوى بشكل مؤقّت قبل أن تجب إعادة التحقّق منه أو إعادة تحميله من الخادوم الأصل، وهو يستبدل الترويسة Expires من أجل المتصفّحات الحديثة وهو الأساس لتحديد حداثة جزء من المحتوى، تُحدَّد قيمة هذا الخيار بالثواني ووقت الحداثة الأعظمي المقبول هو سنة واحدة (31536000 ثانية).
  • s-maxage: وهو مشابه كثيرًا للإعداد max-age بأنّه يشير للفترة الزمنيّة التي يُمكِن خلالها التخزين المؤقّت للمحتوى، ويكمن الفرق في أنّ هذا الخيار يُطبَّق فقط على التخزينات المؤقّتة الوسيطة، يسمح جمعه مع الخيار السابق ببناء سياسة أكثر مرونة.
  • must-revalidate: يُشير هذا الخيار إلى أنّه يجب إطاعة معلومات الحداثة المنصوص عليها من خلال max-age، s-maxage، أو الترويسة Expires بشكل صارم، فلا يُمكِن تخديم المحتوى القديم تحت أي ظروف، ويمنع هذا استخدام المحتوى المُخزَّن مؤقّتًا في حالة انقطاعات الشبكة والسيناريوهات المشابهة لها.
  • proxy-revalidate: يقوم هذا الإعداد بنفس ما يقوم به الإعداد السابق ولكن ينطبق فقط على وسطاء التخزين المؤقّت البيني intermediary proxies، ويبقى متصفّح المستخدم في هذه الحالة قادرًا على تخديم المحتوى القديم في حالة انقطاعات الشبكة، ولكن لا يُمكن استخدام التخزينات المؤقّتة الوسيطة لهذا الغرض.
  • no-transform: يمنع هذا الخيار التخزينات المؤقّتة من تعديل المحتوى الذي تلقّته لأسباب تتعلّق بالأداء تحت أي ظروف، يعني هذا على سبيل المثال أنّ التخزين المؤقّت غير قادر على إرسال إصدارات مضغوطة من محتوى لم يتلقّاه بشكل مضغوط وغير مسموح له بهذا أصلًا.

نستطيع الجمع بين كل ما سبق بطرق مختلفة للحصول على سلوك متعدّد للتخزين المؤقّت، بعض القيم التبادليّة هي:

  • no-cache ،no-store، وسلوك التخزين المؤقّت الطبيعي الذي نشير إليه بغياب أحدهما
  • public و private

يحل الخيار no-store محل no-cache إن كان كلاهما موجودًا، ومن أجل الاستجابة على الطلبات التي لا تتعلق بالاستيثاق يتم تطبيق الخيار public، ومن أجل الاستجابة على الطلبات التي تحتوي استيثاق يتم تطبيق الخيار private، ويُمكِن تجاوزها بتضمين الخيار المعاكس في الترويسة Cache-Control.

تطوير سياسة تخزين مؤقت

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

  1. مشاكل شائعة

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

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

  1. توصيات عامة

على الرغم من أنّ الحالة هي التي تُملي علينا استراتيجيّة التخزين المؤقّت المُلائِمة، تستطيع التوصيات التالية إرشادنا نحو بعض القرارات المنطقيّة.

هناك بعض الخطوات التي نستطيع اتخاذها لزيادة نسبة استخدام التخزين المؤقّت Cache hit ratio قبل الانتقال لاستخدام ترويسات محدّدة، ومن بعض الأفكار نجد:

  • إنشاء أدلّة directories مُخصّصة للصور، ملفات css، والمحتوى المُشترَك: يسمح وضع المحتوى في أدلّة مُخصّصة بالرجوع بسهولة إليها من أي صفحة على موقعنا.
  • استخدام نفس الرابط URL للإشارة إلى نفس العناصر: بما أنّ التخزينات المؤقّتة تستخدم مفتاح مُكوَّن من المُضيف والمسار للمحتوى المطلوب فيجب أن نتأكّد من أن نشير إلى محتوانا بنفس الطريقة على كافّة صفحات موقعنا، وتجعل النصيحة السابقة من هذا أسهل.
  • استخدام الأشكال الشبحية sprites لصور CSS حيثما أمكن ذلك: تُقلِّل الأشكال الشبحية لصور CSS من أجل عناصر مثل الأيقونات والتصفّح navigation من عدد الجولات المطلوبة لتصيير render موقعنا وتسمح لنا بالتخزين المؤقّت لذلك الشكل الشبحي لوقت طويل.
  • استضافة الـ scripts والموارد الخارجيّة بشكل محلّي بقدر الإمكان: إن كُنّا نستخدم scripts خاصة بلغة javascript وموارد خارجيّة أخرى فيجب أن نأخذ بعين الاعتبار استضافة هذه الموارد على خواديمنا الخاصّة، نلاحظ أنّه يجب علينا الانتباه لأي تحديثات جديدة لهذه الموارد لكي نقوم بتحديث نسختنا المحليّة منها.
  • وضع بصمة خاصّة لعناصر التخزين المؤقّت: من المناسب أن نقوم بوضع بصمة خاصّة بالنسبة للمحتوى الثابت مثل ملفّات CSS وJavascript، ويعني هذا إضافة مُعرِّف فريد unique identifier إلى اسم الملف (عادةً تلبيد hash للملف) بحيث إن تمّ تعديل المورد يُمكِن طلب الاسم الجديد للمورد مما يجعل الطلبات تجتاز التخزين المؤقّت بشكل صحيح، توجد العديد من الأدوات التي تساعد في إنشاء بصمات وتعديل المراجع إليها في ملفّات HTML.

ومن ناحية اختيار الترويسات الصحيحة للعناصر المختلفة، فيمكن اعتبار ما يلي كمرجع عام:

  • السماح لكافّة التخزينات المؤقتة بتخزين الممتلكات العامّة general assets: ينبغي أن يتم التخزين المؤقّت للمحتوى الثابت وغير المتعلّق بالمستخدم في كافّة النقاط على سلسلة التوصيل، ممّا يسمح للتخزينات المؤقّتة الوسيطة بالاستجابة بالمحتوى للعديد من المستخدمين.
  • السماح للمتصفّحات بالتخزين المؤقّت للممتلكات الخاصّة بالمستخدم: من المقبول والمفيد بالنسبة للمحتوى الخاص بالمستخدم أن نسمح بالتخزين المؤقّت داخل متصفّح المستخدم، وبينما يكون من غير الملائم فعل هذا على التخزينات المؤقّتة الوسيطة يسمح التخزين المؤقّت في المتصفّح بالاستعادة الآنيّة للمحتوى من أجل المستخدمين خلال الزيارات اللاحقة.
  • عمل استثناءات للمحتوى الأساسي الحساس بالنسبة للوقت: إن كنّا نملك محتوى حساس بالنسبة للوقت نقوم بعمل استثناء للقواعد السابقة بحيث لا يتم تخديم المحتوى القديم في الحالات الحرجة، على سبيل المثال إن كان موقعنا يمتلك عربة تسوّق shopping cart فينبغي أن تعكس العناصر الموجودة فيها فورًا، واعتمادًا على طبيعة المحتوى يُمكِن تعيين الخيار no-cache أو no-store في الترويسة Cache-Control لتحقيق هذا.
  • توفير خيارات للتحقّق دومًا: تسمح لنا خيارات التحقّق بتحديث المحتوى القديم بدون أن يتوجّب علينا تنزيل كامل الموارد مرّة أخرى، يسمح تعيين الترويسات Etag وLast-Modified للتخزينات المؤقّتة بأن تتحقّق من محتواها وتعيد تخديمه إن لم يتم تعديله على الخادوم الأصل، مما يؤدي إلى إنقاص الحمل load.
  • تعيين أوقات حداثة freshness times طويلة للمحتوى الداعم: من أجل الاستفادة من التخزين المؤقّت بشكل فعّال فإنّ العناصر المطلوبة كمحتوى داعم لملء الطلب ينبغي عادة أن تملك وقت حداثة طويل، وهو مناسب بشكل عام من أجل لعناصر مثل الصور وملفّات CSS والتي يتم سحبها لتصيير صفحة HTML المطلوبة من قبل المستخدم، يسمح تعيين وقت حداثة طويل مع جمعه بوضع بصمة بأن تقوم التخزينات المؤقّتة بتخزين هذه الموارد لفترات طويلة، فإن تمّ تغيير الممتلكات فستقوم البصمة المُعدّلة بإبطال العنصر المُخزَّن مؤقّتًا وستقوم بتحفيز تنزيل المحتوى الجديد، وحتى ذلك الحين يُمكِن أن يتم تخزين العناصر الداعمة بشكل مؤقّت لوقت طويل في المستقبل.
  • تعيين أوقات حداثة freshness times قصيرة للمحتوى الأب: يجب أن يملك عنصر الاحتواء وقت حداثة قصير نسبيًّا أو حتى لا يتم تخزينه بشكل مؤقّت نهائيًّا وذلك من أجل تطبيق المخطّط السّابق، وهو بشكل نموذجي صفحة HTML التي تستدعي العناصر المساعدة الأخرى، فيتم تنزيل ملفّات HTML بشكل متكرّر ممّا يسمح لها بالاستجابة للتغيرات بسرعة، ومن ثمّ بعدها يُمكِن التخزين المؤقّت للمحتوى الداعم بشدّة.

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

  • عناصر مُخزَّنة مؤقّتًا بشدّة
  • عناصر مُخزّنة مؤقّتًا مع وقت حداثة قصير والقدرة على إعادة التحقّق
  • عناصر لا ينبغي تخزينها بشكل مؤقّت إطلاقًا

والهدف هو نقل المحتوى إلى الفئة الأولى إن أمكن مع الحفاظ على مستوى مقبول من الدقة.

الخاتمة

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

ترجمة -وبتصرّف- لـ Web Caching Basics: Terminology, HTTP Headers, and Caching Strategies لصاحبه Justin Ellingwood.





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن