ذاكرة التخزين المؤقت فيHTTP

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

رائع، يتم تزويد كل متصفح بتطبيق لذاكرة التخزين المؤقت لـ HTTP. وكل ما يتعين علينا فعله هو التأكد من أن كل استجابة للخادم توفر توجيهات رأس HTTP المناسبة لتوجيه المتصفح في الوقت وللمدة التي يمكن تخزين الاستجابة فيها بواسطة المتصفح.

طلب HTTP

عندما يعرض الخادم استجابة، فإنه يرسل أيضًا مجموعة من رؤوس HTTP تصف نوع المحتوى والطول وتوجيهات التخزين المؤقت ورمز التحقق المميز وغير ذلك الكثير. على سبيل المثال، في المبادلة الواردة أعلاه، يعرض الخادم استجابة 1024 بايت، ويوجه العميل إلى التخزين المؤقت حتى 120 ثانية، ويقدم رمز التحقق المميز (“x234dff”) الذي يمكن استخدامه بعد انتهاء صلاحية الاستجابة لمعرفة ما إذا كان المورد قد تم تعديله أم لا.

التحقق من الاستجابات المخزنة مؤقتًا باستخدام ETags

TL;DR

  • يتم نقل رمز التحقق المميز بواسطة الخادم عبر رأس ETag HTTP
  • 'ويمكِّن رمز التحقق المميز من إجراء فحص لتحديث الموارد: ولا يتم إجراء نقل للبيانات في حال عدم تغير المورد.'

لنفترض أنه قد مرت 120 ثانية منذ الجلب الأول وأن المتصفح قد بدأ طلبًا جديدًا للمورد نفسه. أولاً، يتحقق المتصفح من ذاكرة التخزين المؤقت المحلية ويبحث عن الاستجابة السابقة، إلا أنه لا يمكنه استخدامها للأسف، لأن الاستجابة تكون منتهية الصلاحية. في هذه المرحلة يمكن ببساطة إرسال طلب جديد وجلب الاستجابة الكاملة الجديدة، إلا أن ذلك ليس فعالاً لأنه إذا لم يكن المورد قد تغير عندئذٍ، فليس هناك أي سبب لتنزيل وحدات بايت نفسها المتوفرة في ذاكرة التخزين المؤقت.

وتكمن المشكلة في أن رموز التحقق المميزة، كما هو موضح في الرأس ETag، مصممة لحل ما يلي: ينشئ الخادم ويعرض رمزًا عشوائيًا مميزًا يكون عادة علامة تجزئة أو بصمة أخرى لمحتويات الملف. لا يحتاج العميل إلى معرفة كيفية إنشاء البصمة، ولكن يحتاج فقط إلى إرسالها إلى الخادم في الطلب التالي: وإذا ظلت البصمة نفسها، فهذا يعني أنه لم يتم تغيير المورد ويمكن تخطي التنزيل.

نموذج Cache-Control على HTTP

في المثال الوارد أعلاه، يوفر العميل تلقائيًا رمز ETag المميز داخل رأس طلب HTTP وهو “If-None-Match”، ويتحقق الخادم من الرمز المميز مقارنة بالمورد الحالي، وفي حال عدم تغيره، يتم عرض استجابة “304 Not Modified” لإخبار المتصفح بأن الاستجابة المتوفرة في ذاكرة التخزين المؤقت لم تتغير ويمكن التجديد لمدة 120 ثانية أخرى. لاحظ أنه لا يتعين علينا تنزيل الاستجابة مرة أخرى؛ وهذا يوفر الوقت ومعدل نقل البيانات.

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

Cache-Control

TL;DR

  • يمكن لكل مورد تحديد سياسة التخزين المؤقت عبر رأس Cache-Control HTTP
  • تتحكم توجيهات Cache-Control في من يمكنه التخزين المؤقت للاستجابة، وبأية شروط، ولأية مدة

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

نموذج Cache-Control على HTTP

no-cache و no-store

تشير “no-cache” إلى أن الاستجابة المعروضة لا يمكن استخدامها لتلبية طلب تابع لعنوان URL نفسه بدون التحقق أولاً من الخادم إذا تغيرت الاستجابة. ونتيجة لذلك، إذا كان هناك رمز تحقق مميز (ETag) وصالح، فسيتحمل no-cache جولة للتحقق من الاستجابة المخزنة، ولكن يمكن الحد من التنزيل إذا لم يتم تغيير المورد.

وعلى العكس، يعد “no-store” أكثر بساطة، ذلك أنه يمنع المتصفح وجميع الذاكرة المخزنة الوسيطة من تخزين أية نسخة من الاستجابة المعروضة - مثل النسخة التي تتضمن بيانات شخصية أو مصرفية. وفي كل مرة يطلب المستخدم هذا الأصل، يتم إرسال طلب إلى الخادم ويتم تنزيل استجابة كاملة في كل مرة.

الفرق بين public وprivate

إذا تم وضع علامة على الاستجابة أنها public، فيمكن تخزينها مؤقتًا، حتى إذا كانت تتضمن مصادقة HTTP مقترنة بها، وحتى عندما لا تكون حالة الاستجابة قابلة للتخزين المؤقت بشكل عادي. وفي معظم الأحيان، لا يكون public لازمًا، نظرًا لأن معلومات التخزين المؤقت الصريحة (مثل max-age) تشير إلى أن الاستجابة قابلة للتخزين المؤقت على أية حال.

وعلى العكس، يمكن تخزين استجابة “private” مؤقتًا بواسطة المتصفح ولكن الهدف منها عادة يكون المستخدم الواحد ومن ثم لا يُسمح بالتخزين المؤقت بواسطة أية ذاكرة تخزين مؤقت وسيطة - على سبيل المثال يمكن تخزين معلومات المستخدم الخاصة التي تتضمنها صفحة HTML بواسطة متصفح المستخدم، وليس بواسطة CDN.

max-age

يحدد التوجيه الحد الأقصى للوقت بالثانية الذي يمكن إعادة استخدام الاستجابة المجلوبة فيه من وقت الطلب - على سبيل المثال، يشير “max-age=60” إلى أن الاستجابة قد يتم تخزينها مؤقتًا وإعادة استخدامها خلال الـ 60 ثانية التالية.

تحديد سياسة Cache-Control المثالية

تخزين شجرة القرار مؤقتًا

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

توجيهات Cache-Control توضيح
max-age=86400 يمكن التخزين المؤقت للاستجابة بواسطة المتصفح وأية ذاكرة تخزين مؤقت وسيطة (أي يكون `public`) حتى يوم واحد (60 ثانية × 60 دقيقة × 24 ساعة)
private, max-age=600 يمكن التخزين المؤقت للاستجابة بواسطة متصفح العميل حتى 10 دقائق فقط (60 ثانية × 10 دقائق)
no-store لا يُسمح بالتخزين المؤقت للاستجابة ويجب جلبها بالكامل على كل طلب.

وفقًا لأرشيف HTTP، من بين أهم 300 ألف موقع (وفقًا لترتيب Alexa)، نصف الاستجابات التي يتم تنزيلها تقريبًا يمكن تخزينه مؤقتًا بواسطة المتصفح، وهذا يمثل توفيرًا كبيرًا لمشاهدات الصفحات والزيارات المتكررة. بالطبع، هذا لا يعني أن تطبيقك تحديدًا سيتضمن 50% من الموارد التي يمكن تخزينها مؤقتًا: لأن بعض المواقع قد تخزن 90% أو أكثر من مواردها، بينما قد تتضمن المواقع الأخرى الكثير من البيانات الخاصة أو الحساسة للوقت التي لا يمكن تخزينها مؤقتًا على الإطلاق.

راجع صفحاتك لتحديد الموارد التي يمكن تخزينها مؤقتًا والتأكد من أنها تعرض رؤوسًا مناسبة من Cache-Control وETag.

إلغاء الاستجابات المخزنة مؤقتًا وتحديثها

TL;DR

  • يتم استخدام الاستجابات المخزنة محليًا حتى انتهاء صلاحية المورد
  • يمكننا تضمين بصمة محتوى ملف في عنوان URL من إجبار العميل على التحديث إلى نسخة جديدة من الاستجابة
  • يحتاج كل تطبيق إلى تحديد الترتيب الهرمي لذاكرة التخزين المؤقت الخاصة به لتحقيق مستوى الأداء المثالي

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

لنفترض على سبيل المثال أننا أخبرنا الزائرين بتخزين ورقة أنماط CSS لمدة تصل إلى 24 ساعة (max-age=86400)، ولكن نفَّذ المصمم تحديثًا نريد إتاحته لجميع المستخدمين. كيف يمكننا إشعار جميع الزائرين بما يعتبر الآن نسخة منتهية الصلاحية من CSS لتحديث ذاكرة التخزين المؤقت لديهم؟ يعد هذا السؤال محيرًا، ولا يمكننا ذلك على الأقل بدون تغيير عنوان URL للمورد.

وبعد أن يتم تخزين الاستجابة بواسطة المتصفح، سيتم استخدام النسخة المخزنة مؤقتًا حتى لا تصبح جديدة، كما تم التحديد بواسطة max-age أو expires، أو حتى يتم التخلص منها في ذاكرة التخزين المؤقت لأي سبب آخر، على سبيل المثال، عندما يمحو المستخدم ذاكرة التخزين المؤقت في المتصفح. ونتيجة لذلك، قد ينتهي الأمر باستعمال مستخدمين مختلفين لنسخ مختلفة من الملف عند إنشاء الصفحة؛ حيث يستعمل المستخدمون الذين جلبوا المورد للتو النسخة الجديدة، بينما يستعمل المستخدمون الذين جلبوا نسخة سابقة (ولكنها لا تزال صالحة) نسخة أقدم من الاستجابة.

إذًا، كيف يمكننا الحصول على الميزة في كل الحالتين: حالة التخزين المؤقت من جانب العميل والتحديثات السريعة؟ الأمر بسيط، حيث يمكننا تغيير عنوان URL للمورد وإجبار المستخدم على تنزيل الاستجابة الجديدة متى تم تغيير المحتوى. ويتم هذا عادة من خلال تضمين بصمة الملف أو رقم نسخة في اسم الملف، على سبيل المثال style.x234dff.css.

الترتيب الهرمي لذاكرة التخزين المؤقت

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

  • يتم وضع علامة على HTML أنه “no-cache”، أي أن المتصفح سيعيد التحقق دائمًا من المستند عند كل طلب وسيجلب آخر نسخة إذا تم تغيير المحتويات. وكذلك، داخل ترميز HTML نضمِّن بصمات في عناوين URL لأصول CSS وجافا سكريبت: عند تغير محتويات هذه الملفات، سيتغير HTML للصفحة كذلك وسيتم تنزيل نسخة جديدة من استجابة HTML.
  • يُسمح بتخزين CSS مؤقتًا بواسطة المتصفحات وذاكرة التخزين المؤقت الوسيطة (مثل CDN)، ويتم تعيينه على انتهاء الصلاحية خلال عام واحد. لاحظ أنه يمكننا استخدام “far future expires” لعام واحد بشكل آمن نظرًا لأننا نضمِّن اسم الملف في بصمة الملف: إذا تم تحديث CSS، فسيتغير عنوان URL أيضًا.
  • يتم تعيين جافا سكريبت أيضًا على انتهاء الصلاحية خلال عام واحد، ولكن يتم وصفه باعتباره خاصًا، ربما بسبب احتوائه على بعض بيانات المستخدم الخاصة التي يجب ألا يتم تخزينها مؤقتًا في CDN.
  • يتم تخزين نسخة مؤقتة من الصورة بدون إصدار أو بصمة فريدة ويتم تعيينها على انتهاء الصلاحية خلال يوم واحد.

يتيح لنا الجمع بين ETag وCache-Control وعناوين URL الفريدة تقديم الميزات في الجانبين: مدة أطول قبل انتهاء الصلاحية، وإمكانية التحكم في مكان التخزين المؤقت للاستجابة والتحديثات عند الطلب.

قائمة التحقق من التخزين المؤقت

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

في ما يلي بعض النصائح والتقنيات التي يجب وضعها في الاعتبار عند استخدام إستراتيجية التخزين المؤقت:

  1. استخدم عناوين URL متناسقة: إذا كنت تعرض المحتوى نفسه على عناوين URL مختلفة، فسيتم جلب المحتوى وتخزينه عدة مرات. نصيحة: لاحظ أن عناوين URL حساسة لحالة الأحرف!
  2. تأكد من أن الخادم يوفر رمز تحقق مميزًا (ETag): تقلل رموز التحقق المميزة من الحاجة إلى نقل وحدات بايت نفسها عند عدم تغير المورد على الخادم.
  3. حدد الموارد التي يمكن تخزينها مؤقتًا من خلال وسيطات: تعد الموارد التي تتضمن استجابات متماثلة لجميع المستخدمين عناصر مرشحة إلى حد كبير للتخزين المؤقت بواسطة CDN ومتوسطات أخرى.
  4. حدد زمن التخزين المؤقت المثالي لكل مورد: قد تتضمن الموارد المختلفة متطلبات تحديث مختلفة. راجع وحدد max-age المناسب لكل مورد.
  5. حدد الترتيب الهرمي الأفضل لذاكرة التخزين المؤقت لموقعك: يتيح لك الجمع بين عناوين URL للمورد مع بصمات المحتوى والزمن القصير أو no-cache مع مستندات HTML إمكانية التحكم في سرعة اختيار التحديثات بواسطة العميل.
  6. الحد من التغيير السريع: يتم تحديث بعض الموارد بمعدل أسرع من الموارد الأخرى. وإذا كان هناك جزء معين من المورد (مثل وظيفة جافا سكريبت أو مجموعة أنماط CSS) يتم تحديثه كثيرًا، فيمكنك تقديم هذه الشفرة في ملف منفصل. ويتيح ذلك لبقية المحتوى (مثل شفرة المكتبة التي لا تتغير كثيرًا)، أن يتم جلبه من ذاكرة التخزين المؤقت وتقليل مقدار المحتوى المطلوب تنزيله متى تم جلب تحديث.