نظرة سريعة على التخزين المؤقت في ريلز

من موسوعة حسوب
< Rails
مراجعة 08:07، 13 مارس 2019 بواسطة جميل-بيلوني (نقاش | مساهمات) (إنشاء الصفحة. هذه الصفحة من مساهمات "دعاء فرح")
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)
اذهب إلى التنقل اذهب إلى البحث

هذا الدليل عبارة عن مقدمة لتسريع تطبيق Rails مع التخزين المؤقت.

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

غالبًا ما يكون التخزين المؤقت الطريقة الأكثر فاعلية لتعزيز أداء التطبيق. من خلال التخزين المؤقت، يمكن لمواقع الويب التي تعمل على خادم واحد مع قاعدة بيانات واحدة الحفاظ على تحميل الآلاف من المستخدمين المتزامنين.

يوفر Rails مجموعة من ميزات التخزين المؤقت من خارج الصندوق. سوف يعلمك هذا الدليل نطاق وغرض كل واحد منهم. إتقان هذه التقنيات وتطبيقات Rails الخاصة بك يمكن أن تخدم الملايين من وجهات النظر دون أوقات استجابة باهظة أو فواتير الخادم.

بعد قراءة هذا الدليل، ستعرف:

  • التجزئة و دمية Russian للتخزين المؤقت.
  • كيفية إدارة التبعيات للتخزين المؤقت.
  • مخازن ذاكرة التخزين المؤقت البديلة.
  • دعم GET  الشرطي.

التخزين المؤقت الأساسي

هذه مقدمة إلى ثلاثة أنواع من تقنيات التخزين المؤقت: الصفحة page، والإجراء action، والتخزين المؤقت للتجزئة fragment.

بشكل افتراضي يوفر Rails ذاكرة التخزين المؤقت في الجزء. لاستخدام التخزين المؤقت للصفحة والعمل، ستحتاج إلى إضافة actionpack-page_caching و actionpack-action_caching إلى Gemfile الخاص بك.

بشكل افتراضي، يُمكن التخزين المؤقت فقط في بيئة الإنتاج الخاصة بك. للتلاعب بالتخزين المؤقت محليًا، ستحتاج إلى تمكين التخزين المؤقت في البيئة المحلية عن طريق تعيين config.action_controller.perform_caching على true في الملف config/environments/*.rb:

config.action_controller.perform_caching = true

ملاحظة: سيؤثر تغيير قيمة config.action_controller.perform_caching فقط على التخزين المؤقت الذي يوفره مكون "عنصر وحدة التحكم في الإجراء". على سبيل المثال، لن يؤثر ذلك على التخزين المؤقت ذي المستوى المنخفض، الذي نتناوله أدناه.

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

التخزين المؤقت للصفحة هو آلية Rails تسمح لطلب صفحة كُونت بواسطة خادم الويب (أي Apache أو NGINX) دون الحاجة إلى المرور عبر مكدس Rails بأكمله. في حين أن هذا هو بسرعة فائقة لا يمكن تطبيقه على كل حالة (مثل الصفحات التي تحتاج إلى المصادقة). أيضا، لأن خادم الويب يخدم ملفًا مباشرةً من نظام الملفات، ستحتاج إلى تنفيذ انتهاء صلاحية ذاكرة التخزين المؤقت.

تمت إزالة التخزين المؤقت للصفحة من Rails 4. راجع actionpack-page_caching gem.

التخزين المؤقت للإجراء

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

ملاحظة: تمت إزالة التخزين المؤقت للعمل من Rails 4. راجع actionpack-action_caching gem. انظر نظرة عامة على انتهاء صلاحية ذاكرة التخزين المؤقت المستندة إلى مفتاح DHH للطريقة المفضلة حديثًا.

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

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

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

على سبيل المثال، إذا كنت تريد تخزين مؤقت لكل منتج في الصفحة، يمكنك استخدام هذا الرمز:

<% @products.each do |product| %>

 <% cache product do %>

   <%= render product %>

 <% end %>

<% end %>

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

views/products/1-201505056193031061005000/bea67108094918eeba42cd4a6e786901

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

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

ملاحظة: ستحذف مخازن ذاكرة التخزين المؤقت مثل Memcached ملفات التخزين المؤقت القديمة تلقائيًا.

إذا كنت تريد تخزين جزء مؤقتًا تحت شروط معينة ، فيمكنك استخدام cache_if أو cache_unless:

<% cache_if admin?, product do %>

 <%= render product %>

<% end %>

التخزين المؤقت للمجموعة

يمكن للمساعد render أيضاً بتخزين قوالب فردية تقديمها لمجموعة. يمكن حتى واحد حتى المثال السابق مع كل من خلال قراءة جميع قوالب ذاكرة التخزين المؤقت دفعة واحدة بدلا من واحدة تلو الأخرى. و ذلك بتمرير النسخة cached: true عند عرض المجموعة:

<%= render partial: 'products/product', collection: @products, cached: true %>

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

التخزين المؤقت Russian Doll

قد ترغب في دمج أجزاء مخزنة مؤقتًا داخل أجزاء أخرى مخزنة مؤقتًا. وهذا ما يسمى التخزين المؤقت Russian doll.

ميزة التخزين المؤقت Russian doll هي أنه في حالة تحديث منتج واحد، يمكن إعادة استخدام جميع الأجزاء الداخلية الأخرى عند إعادة تجديد الجزء الخارجي.

كما هو موضح في القسم السابق، ستنتهي صلاحية الملف المخزن مؤقتًا إذا كانت قيمة التغييرات updated_at للسجل الذي يعتمد عليه الملف المخزن مؤقتًا بشكل مباشر. ومع ذلك، لن تنتهي صلاحية أي ذاكرة تخزين مؤقت مُضمن جزء منها.

على سبيل المثال، خذ الواجهة التالية:

<% cache product do %>

 <%= render product.games %>

<% end %>

وهو ما يؤدي بدوره إلى عرض هذه الواجهة:

<% cache game do %>

 <%= render game %>

<% end %>

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

class Product < ApplicationRecord

 has_many :games

end

class Game < ApplicationRecord

 belongs_to :product, touch: true

End

مع وضع touch على true، فإن أي إجراء يغير updated_at لسجل اللعبة سيغيره أيضًا للمنتج المقترن، وبذلك تنتهي صلاحية ذاكرة التخزين المؤقت.

التخزين المؤقت الجزئي المشترك

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

render(partial: 'hotels/hotel', collection: @hotels, cached: true)

سيُحمل ملف اسمه hotels / hotel.erb.

خيار آخر هو تضمين اسم الملف الكامل للجزء للتقديم.

render(partial: 'hotels/hotel.html.erb', collection: @hotels, cached: true)

سيُحمل ملف باسم hotels / hotel.html.erb في أي نوع من أنواع ملفات mime، على سبيل المثال، يمكنك تضمين هذا الجزء في ملف JavaScript.

<nowiki>https://guides.rubyonrails.org/action_cable_overview.html</nowiki>

إدارة التبعيات

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

التبعيات الضمنية

يمكن اشتقاق معظم تبعيات القالب من الإستدعاءات render في القالب نفسه. فيما يلي بعض الأمثلة على الإستدعاءات التي تجعل ActionView :: Digestor تعرف كيفية فك ترميز:

render partial: "comments/comment", collection: commentable.comments

render "comments/comments"

render 'comments/comments'

render('comments/comments')

render "header" translates to render("comments/header")

render(@topic)         translates to render("topics/topic")

render(topics)         translates to render("topics/topic")

render(message.topics) translates to render("topics/topic")

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

render @project.documents.where(published: true)

إلى

render partial: "documents/document", collection: @project.documents.where(published: true)

تبعيات صريحة

في بعض الأحيان سيكون لديك تبعيات قالب لا يمكن اشتقاقه على الإطلاق. هذا هو الحال عادة عندما يحدث التقديم في المساعدين. إليك مثال على ذلك:

<%= render_sortable_todolists @project.todolists %>

ستحتاج إلى استخدام تنسيق تعليق خاص لاستدعاء هؤلاء:

<%# Template Dependency: todolists/todolist %>

<%= render_sortable_todolists @project.todolists %>

في بعض الحالات، مثل إعداد توارث جدول فردي، قد يكون لديك مجموعة من التبعيات الصريحة. بدلاً من كتابة كل قالب، يمكنك استخدام حرف بدل لمطابقة أي قالب في الدليل:

<%# Template Dependency: events/* %>

<%= render_categorizable_events @person.events %>

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

<%# Template Collection: notification %>

<% my_helper_that_calls_cache(some_arg, notification) do %>

 <%= notification.name %>

<% end %>

التبعيات الخارجية

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

<%# Helper Dependency Updated: Jul 28, 2015 at 7pm %>

<%= some_helper_method(person) %>

التخزين المؤقت منخفض المستوى

تحتاج في بعض الأحيان إلى تخزين قيمة استعلام أو نتيجة معينة بدلاً من تخزين أجزاء من ذاكرة التخزين المؤقت. تعمل آلية التخزين المؤقت في Rails بشكل رائع لتخزين أي نوع من المعلومات.

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

خذ بعين الاعتبار المثال التالي. يحتوي التطبيق على نموذج منتج مع نسخة تابع تبحث عن سعر المنتج على موقع ويب منافس. ستكون البيانات التي تُرجع بواسطة هذه الطريقة مثالية للتخزين المؤقت على مستوى منخفض:

class Product < ApplicationRecord

 def competing_price

   Rails.cache.fetch("#{cache_key}/competing_price", expires_in: 12.hours) do

     Competitor::API.find_price(id)

   end

 end

End

ملاحظة: لاحظ أننا في هذا المثال استخدمنا تابع cache_key_with_version، لذا فإن مفتاح ذاكرة التخزين المؤقت الناتج سيكون شيئًا مثل المنتجات / 233-20140225082222765838000 / competing_price.

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

التخزين المؤقت SQL

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

فمثلا:

class ProductsController < ApplicationController

 def index

   # Run a find query

   @products = Product.all

   ...

   # Run the same query again

   @products = Product.all

 end

End

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

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

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

توفر Rails متاجرًا مختلفة للبيانات المخزنة مؤقتًا (بخلاف التخزين المؤقت لـ SQL و الصفحة).

إعدادات التكوين

يمكنك إعداد مخزن ذاكرة التخزين المؤقت الافتراضي للتطبيق من خلال تعيين خيار التكوين config.cache_store. يمكن تمرير معاملات أخرى كوسائط إلى مُنشئ مخزن التخزين المؤقت:

config.cache_store = :memory_store, { size: 64.megabytes }

ملاحظة: بدلاً من ذلك، يمكنك الاتصال بـ ActionController :: Base.cache_store خارج كتلة التكوين.

يمكنك الوصول إلى ذاكرة التخزين المؤقت عن طريق استدعاء Rails.cache.

ActiveSupport::Cache::Store

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

التوابع الرئيسية للإستدعاء هي ?read ،write ،delete ،exist و fetch.

التابع fetch يأخذ كتلة ويرجع قيمة موجودة من ذاكرة التخزين المؤقت أو تقييم الكتلة وكتابة النتيجة إلى ذاكرة التخزين المؤقت في حالة عدم وجود قيمة.

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

  • :namespace - يمكن استخدام هذا الخيار لإنشاء مساحة اسم داخل مخزن ذاكرة التخزين المؤقت. من المفيد بشكل خاص إذا كان التطبيق الخاص بك يشارك ذاكرة تخزين مؤقت مع تطبيقات أخرى.
  • :compress - مفعل افتراضيًا. يضغط إدخالات ذاكرة التخزين المؤقت بحيث يمكن تخزين المزيد من البيانات في نفس مساحة الذاكرة، مما يؤدي إلى عدد أقل من عمليات الإخلاء في الذاكرة المؤقتة ومعدلات ضرب أعلى.
  • :compress_threshold - افتراضياً 1 كيلو بايت. يضغط إدخالات ذاكرة التخزين المؤقت الأكبر من هذا الحد، محدد بالبايت، مضغوطة.
  • :expires_in - يحدد هذا الخيار وقت انتهاء الصلاحية بالثواني لإدخال ذاكرة التخزين المؤقت عندما يزال تلقائيًا من ذاكرة التخزين المؤقت.
  • :race_condition_ttl - يستخدم هذا الخيار مع الخيار :expires_in. وسوف تمنع ظروف السباق عندما تنتهي صلاحية إدخالات ذاكرة التخزين المؤقت من خلال منع عمليات متعددة من إعادة إنشاء نفس الإدخال في نفس الوقت (المعروف أيضًا باسم تأثير كومة الكلب). يحدد هذا الخيار عدد الثواني التي يمكن إعادة استخدامها في إدخال منتهي الصلاحية أثناء إعادة إنشاء قيمة جديدة. من الممارسات الجيدة تعيين هذه القيمة إذا كنت تستخدم الخيار: expires_in.

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

يمكنك إنشاء مخزن مؤقت مخصص خاص بك ببساطة عن طريق توسيع ActiveSupport :: Cache :: Store وتنفيذ التوابع المناسبة. بهذه الطريقة، يمكنك تبديل أي عدد من تقنيات التخزين المؤقت في تطبيق Rails.

لاستخدام مخزن مؤقت مخصص، ما عليك سوى تعيين مخزن ذاكرة التخزين المؤقت على نسخة جديدة للفئة المخصصة.

config.cache_store = MyCacheStore.new

ActiveSupport::Cache::MemoryStore

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

config.cache_store = :memory_store, { size: 64.megabytes }

إذا كنت تقوم بتشغيل عدة عمليات الخادم Ruby on Rails (وهي الحالة إذا كنت تستخدم Phusion Passenger أو وضع clustered)، فلن تتمكن نُسخ عملية الخادم Rails من مشاركة بيانات ذاكرة التخزين المؤقت مع بعضها البعض. مخزن التخزين المؤقت هذا غير مناسب لعمليات نشر التطبيقات الكبيرة. ومع ذلك، يمكن أن تعمل بشكل جيد للمواقع الصغيرة منخفضة الحركة مع اثنين فقط من عمليات الخادم، بالإضافة إلى بيئات التطوير والاختبار.

تتكون مشاريع Rails جديدة لاستخدام هذا التطبيق في بيئة التطوير بشكل افتراضي.

نظرًا لأن العمليات لن تشارك بيانات ذاكرة التخزين المؤقت عند استخدام :memory_store، لن يكون من الممكن قراءة ذاكرة التخزين المؤقت أو كتابتها أو انتهاء صلاحيتها يدويًا عبر وحدة التحكم Rails.

ActiveSupport::Cache::FileStore

يستخدم مخزن التخزين المؤقت هذا نظام الملفات لتخزين الإدخالات. يجب تحديد المسار إلى الدليل حيث تُخزن ملفات المخزن عند تهيئة ذاكرة التخزين المؤقت.

config.cache_store = :file_store, "/path/to/cache/directory"

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

عندما تنمو ذاكرة التخزين المؤقت حتى يمتلئ القرص، يُوصى بمسح الإدخالات القديمة بشكل دوري.

هذا هو تطبيق التخزين المؤقت الافتراضي (في "# {root} / tmp / cache /") إذا لم يتوفر config.cache_store صريح.

ActiveSupport::Cache::MemCacheStore

يستخدم مخزن التخزين المؤقت هذا خادم ذاكرة Danga's memcached لتوفير ذاكرة تخزين مركزية لتطبيقك. يستخدم Rails جوهرة dalli المجمعة بشكل افتراضي. هذا هو مخزن ذاكرة التخزين المؤقت الأكثر شيوعًا حاليًا لمواقع الإنتاج. يمكن استخدامه لتوفير مجموعة ذاكرة تخزين مؤقت مشتركة واحدة بأداء عالٍ جدًا وتكرار.

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

تقبل التوابع write وfetch في ذاكرة التخزين المؤقت هذين الخيارين الإضافيين اللذين يستفيدان من الميزات الخاصة بـ memcached. يمكنك تحديد : raw لإرسال قيمة مباشرة إلى الخادم بدون وجود تسلسل. يجب أن تكون القيمة عبارة عن سلسلة أو رقم. يمكنك استخدام العمليات المباشرة memcached مثل increment و decrement فقط على القيم الخام. يمكنك أيضًا تحديد

:unless_exist إذا كنت لا تريد memcached للكتابة فوق إدخال موجود.

config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"

ActiveSupport::Cache::RedisCacheStore

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

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

بالنسبة لخادم Redis الخاص بالذاكرة المؤقتة فقط ، قم بتعيين maxmemory-policy على أحد المتغيرات الخاصة بكل المفاتيح. يدعم Redis 4+ الإخلاء الأقل استخدامًا (allkeys-lfu) ، وهو اختيار افتراضي ممتاز. يجب على Redis 3 وأقدم استخدام الإخلاء الأقل استخدامًا (allkeys-lru).

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

بشكل افتراضي، لن يحاول مخزن ذاكرة التخزين المؤقت إعادة الاتصال بـ Redis إذا فشل الاتصال أثناء الطلب. إذا واجهت قطع اتصال متكررة فقد ترغب في تمكين محاولات إعادة الاتصال.

ذاكرة التخزين المؤقت يقرأ ويكتب لا يثير استثناءات. هم فقط يرجعون التشعب nil، ويتصرفون كما لو لم يكن هناك شيء في ذاكرة التخزين المؤقت. لقياس ما إذا كانت ذاكرة التخزين المؤقت تصل إلى الاستثناءات، يمكنك تقديم error_handler للإبلاغ عن خدمة تجميع الاستثناءات. يجب أن يقبل ثلاث وسائط للكلمات الرئيسية: method، تابع تخزين ذاكرة التخزين المؤقت التي كانت تسمى في الأصل؛ returning، القيمة التي أُرجعت للمستخدم، عادة nil؛ و exception، الاستثناء الذي أُنقذ.

للبدء، أضف جوهرة redis إلى Gemfile:

gem 'redis'

يمكنك تمكين الدعم لمكتبة الاتصال أسرع hiredis بواسطة الإضافة، إضافة غلاف روبي إلى Gemfile:

gem 'hiredis'

سيتطلب مخزن التخزين المؤقت Redis تلقائيًا استخدام hiredis واستخدامه إذا كان متاحًا. هناك حاجة إلى مزيد من التكوين.

وأخيرًا، أضف التكوين في الملف config / environments / * .bb:

<nowiki>config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }</nowiki>

أكثر تعقيدًا، قد يظهر متجر Redis cache store كالتالي:

cache_servers = %w(<nowiki>redis://cache-01:6379/0</nowiki> <nowiki>redis://cache-02:6379/0</nowiki>)

config.cache_store = :redis_cache_store, { url: cache_servers,

 connect_timeout: 30,  # Defaults to 20 seconds

 read_timeout:    0.2, # Defaults to 1 second

 write_timeout:   0.2, # Defaults to 1 second

 error_handler: -> (method:, returning:, exception:) {

   # Report errors to Sentry as warnings

   Raven.capture_exception exception, level: 'warning',

     tags: { method: method, returning: returning }

 }

}

ActiveSupport::Cache::NullStore

من المفترض استخدام تطبيق مخزن ذاكرة التخزين المؤقت هذا فقط في بيئات التطوير أو الاختبار ولا يخزن أي شيء أبدًا. قد يكون هذا مفيدًا جدًا في التطوير عندما يكون لديك رمز يتفاعل مباشرة مع Rails.cache ولكن قد يتداخل التخزين المؤقت مع إمكانية رؤية نتائج تغييرات التعليمات البرمجية. باستخدام هذا المخزن المؤقت، ستؤدي جميع عمليات fetch و read إلى فقدانها.

config.cache_store = :null_store

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

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

يمكنك استخدام Hash و Arrays من القيم كمفاتيح ذاكرة التخزين المؤقت.

<nowiki>#</nowiki> This is a legal cache key

Rails.cache.read(site: "mysite", owners: [owner_1, owner_2])

لن تكون المفاتيح التي تستخدمها على Rails.cache هي نفسها المفاتيح المستخدمة فعليًا مع محرك التخزين. يمكن تعديلها باستخدام مساحة اسمية أو تعديلها لتلائم القيود الخلفية للتكنولوجيا. هذا يعني، على سبيل المثال، أنه لا يمكنك حفظ القيم باستخدام Rails.cache ثم حاول سحبها باستخدام جوهرة dalli. ومع ذلك، لا داعي للقلق أيضًا بشأن تجاوز حد حجم memcached أو انتهاك قواعد تركيب الجملة.

دعم GET الشرطي

تعتبر GETs المشروطة إحدى ميزات مواصفات HTTP التي توفر طريقة لخوادم الويب لإخبار المستعرضات بأن الاستجابة لطلب GET لم يتغير منذ آخر طلب ويمكن سحبها بأمان من ذاكرة التخزين المؤقت للمتصفح.

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

يتحمل الخادم (الخاص بنا) مسؤولية البحث عن آخر طابع زمني معدّل و رأس if-none-match وتحديد ما إذا كان سيرسل الاستجابة الكاملة أم لا. مع دعم get الشرطية في Rails هذه مهمة سهلة جدًا:

class ProductsController < ApplicationController

 def show

   @product = Product.find(params[:id])

   # If the request is stale according to the given timestamp and etag value

   # (i.e. it needs to be processed again) then execute this block

   if stale?(last_modified: @product.updated_at.utc, etag: @product.cache_key_with_version)

     respond_to do |wants|

       # ... normal response processing

     end

   end

   # If the request is fresh (i.e. it's not modified) then you don't need to do

   # anything. The default render checks for this using the parameters

   # used in the previous call to stale? and will automatically send a

   # :not_modified. So that's it, you're done.

 end

End

بدلاً من تجزئة الخيارات، يمكنك أيضًا ببساطة المرور في النموذج. سيستخدم Rails التابعين updated_at و cache_key_with_version لإعداد last_modified و etag:

class ProductsController < ApplicationController

 def show

   @product = Product.find(params[:id])

   if stale?(@product)

     respond_to do |wants|

       # ... normal response processing

     end

   end

 end

End

إذا لم تكن لديك أية معالجة خاصة للرد وكنت تستخدم آلية التقديم الافتراضية (بمعنى أنك لا تستخدم response_to أو call لتقديم نفسك)، فحينئذٍ يكون لديك مساعد سهل في وضع fresh_when:

class ProductsController < ApplicationController

 # This will automatically send back a :not_modified if the request is fresh,

 # and will render the default template (product.*) if it's stale.

 def show

   @product = Product.find(params[:id])

   fresh_when last_modified: @product.published_at.utc, etag: @product

 end

End

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

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

باستخدام هذا المساعد ، تم تعيين رأس الصفحة  last_modified على Time.new (2011، 1، 1) .utc وتنتهي صلاحية رأس الصفحة إلى 100 عام.

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

class HomeController < ApplicationController

 def index

   http_cache_forever(public: true) do

     render

   end

 end

End

ETags القوية مقابل الضعيفة

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

ETAGS الضعيفة لديها W / الرائدة في تمييزها من ETags القوية.

W/"618bbc92e2d35ea1945008b42799b0e7"  Weak ETag

"618bbc92e2d35ea1945008b42799b0e7"  Strong ETag

بعكس ETag الضعيف، يشير ETag القوي إلى أن الاستجابة يجب أن تكون متماثلة تمامًا والبايت يطابق البايت. مفيد عند تنفيذ طلبات النطاق داخل ملف فيديو أو ملف PDF كبير. بعض شبكات CDN تدعم فقط الأتصالات القوية، مثل Akamai. إذا كنت في حاجة ماسة إلى إنشاء ETag قوي، فيمكن القيام بذلك على النحو التالي.

class ProductsController < ApplicationController

 def show

   @product = Product.find(params[:id])

   fresh_when last_modified: @product.published_at.utc, strong_etag: @product

 end

End

يمكنك أيضًا تعيين ETag القوي مباشرة على الاستجابة.

response.strong_etag = response.body # => "618bbc92e2d35ea1945008b42799b0e7"

التخزين المؤقت في التطوير

من الشائع أن ترغب في اختبار استراتيجية التخزين المؤقت للتطبيق الخاص بك في وضع التطوير. يوفر Rails مهمة dev:cache  لتبديل التخزين المؤقت بسهولة على فتح/ قفل.

$ bin/rails dev:cache

Development mode is now being cached.

$ bin/rails dev:cache

Development mode is no longer being cached.

المراجع

  • مقالة DHH حول انتهاء الصلاحية المستندة إلى المفتاح.
  • Ryan Bates' Railscast على ملخص التخزين المؤقت.

مصادر