چارچوب کش در جنگو: راهنمای کامل بهبود عملکرد، انواع کش و پیکربندی Memcached

این مقاله توضیح می‌دهد که چرا کش در وب‌سایت‌های پویا ضروری است، جنگو چگونه از کش برای افزایش سرعت استفاده می‌کند، چه انواعی از کش وجود دارد، و چگونه می‌توان Memcached را به‌عنوان یک سیستم کش سریع و توزیع‌شده پیکربندی کرد.

کش جنگو، Memcached، CACHESبهینه‌سازی عملکردPyMemcacheCache، PyLibMCCache

~17 دقیقه مطالعه • بروزرسانی ۲۴ اسفند ۱۴۰۴

مقدمه

وب‌سایت‌های پویا برای تولید هر صفحه نیاز به انجام عملیات سنگینی دارند: از کوئری‌های دیتابیس گرفته تا رندر قالب‌ها و اجرای منطق تجاری. این فرآیند نسبت به خواندن یک فایل ساده از دیسک بسیار پرهزینه‌تر است.

برای سایت‌های کوچک، این هزینه قابل‌قبول است؛ اما برای سایت‌های متوسط و پرترافیک، کاهش سربار پردازشی ضروری است. اینجاست که کش (Cache) وارد می‌شود.

کش چیست؟

کش یعنی ذخیره‌سازی نتیجهٔ یک عملیات سنگین تا در درخواست‌های بعدی دوباره محاسبه نشود.

نمونه شبه‌کد


اگر صفحه در کش موجود است:
    همان صفحه را برگردان
در غیر این صورت:
    صفحه را تولید کن
    آن را در کش ذخیره کن
    صفحه تولید شده را برگردان

جنگو یک سیستم کش قدرتمند ارائه می‌دهد که می‌تواند:

  • کل سایت را کش کند
  • خروجی یک view را کش کند
  • بخشی از قالب را کش کند
  • یا از API سطح پایین برای کش داده‌ها استفاده کند

جنگو همچنین با کش‌های پایین‌دستی مثل Squid، Varnish و کش مرورگر سازگار است.


راه‌اندازی کش در جنگو

برای استفاده از کش باید مشخص کنید داده‌های کش کجا ذخیره شوند. این کار با تنظیم CACHES در فایل settings.py انجام می‌شود.

نوع کش انتخابی شما تأثیر زیادی بر عملکرد دارد. یکی از سریع‌ترین گزینه‌ها Memcached است.


Memcached

Memcached یک سیستم کش مبتنی بر حافظه است که برای سرعت بالا طراحی شده و توسط سایت‌های بزرگی مانند Facebook و Wikipedia استفاده می‌شود.

مزایا

  • کاملاً در حافظه ذخیره می‌شود → سرعت بسیار بالا
  • بدون سربار دیتابیس یا فایل‌سیستم
  • قابل توزیع روی چند سرور

پیش‌نیازها

  • نصب سرور Memcached
  • نصب یکی از bindingهای پایتون: pylibmc یا pymemcache

پیکربندی ساده با TCP


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "127.0.0.1:11211",
    }
}

پیکربندی با Unix Socket


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "unix:/tmp/memcached.sock",
    }
}

کش توزیع‌شده با Memcached

Memcached می‌تواند روی چند سرور اجرا شود و به‌عنوان یک کش واحد عمل کند. برای این کار کافی است همهٔ آدرس‌ها را در LOCATION قرار دهید.

مثال: چند سرور


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": [
            "172.19.26.240:11211",
            "172.19.26.242:11211",
        ],
    }
}

مثال: پورت‌های متفاوت


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": [
            "172.19.26.240:11211",
            "172.19.26.242:11212",
            "172.19.26.244:11213",
        ],
    }
}

تنظیمات پیش‌فرض PyMemcacheCache

می‌توانید این گزینه‌ها را در OPTIONS تغییر دهید:


"OPTIONS": {
    "allow_unicode_keys": True,
    "default_noreply": False,
    "serde": pymemcache.serde.pickle_serde,
}

نکات مهم دربارهٔ Memcached

چون داده‌ها در حافظه ذخیره می‌شوند:

  • با ری‌استارت سرور، داده‌ها از بین می‌روند
  • برای ذخیره‌سازی دائمی مناسب نیست
  • فقط برای کش موقت طراحی شده است

این موضوع دربارهٔ تمام backendهای کش جنگو صدق می‌کند: هیچ‌کدام برای ذخیره‌سازی دائمی مناسب نیستند.


جمع‌بندی

چارچوب کش جنگو ابزاری قدرتمند برای افزایش سرعت و کاهش سربار پردازشی وب‌سایت‌های پویا است. چه بخواهید کل سایت را کش کنید، چه فقط یک view یا بخشی از قالب را، جنگو امکانات لازم را فراهم کرده است. Memcached نیز یکی از سریع‌ترین و قابل‌اعتمادترین گزینه‌ها برای سایت‌های پرترافیک است.

Redis در جنگو

Redis یک دیتابیس درون‌حافظه‌ای بسیار سریع است که می‌تواند به‌عنوان سیستم کش استفاده شود. برای شروع، باید یک سرور Redis اجرا کنید (محلی یا روی سرور دیگر).

پیش‌نیازها

  • نصب Redis
  • نصب binding پایتون: redis-py
  • نصب hiredis برای بهبود عملکرد (اختیاری اما توصیه‌شده)

پیکربندی ساده Redis


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
    }
}

Redis با احراز هویت


"LOCATION": "redis://username:[email protected]:6379"

Redis با چند سرور (Replication)

در این حالت:

  • نوشتن روی سرور اول (leader) انجام می‌شود
  • خواندن از replicaها به‌صورت تصادفی انجام می‌شود

"LOCATION": [
    "redis://127.0.0.1:6379",  # leader
    "redis://127.0.0.1:6378",  # replica 1
    "redis://127.0.0.1:6377",  # replica 2
]

کش دیتابیس (Database Cache)

جنگو می‌تواند داده‌های کش را در یک جدول دیتابیس ذخیره کند. این روش زمانی مناسب است که دیتابیس سریع و به‌خوبی ایندکس شده باشد.

پیکربندی


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "my_cache_table",
    }
}

ساخت جدول کش


python manage.py createcachetable

این دستور جدول مورد نیاز را ایجاد می‌کند و اگر جدول وجود داشته باشد آن را تغییر نمی‌دهد.

نکته مهم

DatabaseCache به‌صورت خودکار داده‌های منقضی‌شده را حذف نمی‌کند. حذف در هنگام add()، set() یا touch() انجام می‌شود.


کش دیتابیس با چند دیتابیس (Multiple Databases)

اگر از چند دیتابیس استفاده می‌کنید، باید یک router برای عملیات کش تعریف کنید. جنگو جدول کش را به‌عنوان مدلی به نام CacheEntry در اپلیکیشن django_cache در نظر می‌گیرد.

مثال Router


class CacheRouter:
    def db_for_read(self, model, **hints):
        if model._meta.app_label == "django_cache":
            return "cache_replica"
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == "django_cache":
            return "cache_primary"
        return None

    def allow_migrate(self, db, app_label, **hints):
        if app_label == "django_cache":
            return db == "cache_primary"
        return None

کش فایل‌محور (File-Based Cache)

در این روش هر مقدار کش به‌صورت یک فایل جداگانه ذخیره می‌شود.

پیکربندی


"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/var/tmp/django_cache",

نکات امنیتی

  • هرگز LOCATION را داخل MEDIA_ROOT یا STATIC_ROOT قرار ندهید.
  • فایل‌های کش با pickle ذخیره می‌شوند → خطر اجرای کد مخرب وجود دارد.

نکات عملکردی

اگر تعداد فایل‌ها زیاد شود، عملکرد کند می‌شود. در این صورت بهتر است از Redis یا Memcached استفاده کنید.


کش حافظهٔ محلی (Local-Memory Cache)

این کش پیش‌فرض جنگو است اگر backend دیگری تنظیم نشده باشد. این کش سریع است اما فقط در همان پروسه قابل‌استفاده است.

پیکربندی


"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "unique-snowflake",

ویژگی‌ها

  • بسیار سریع
  • thread-safe
  • بدون اشتراک بین پروسه‌ها
  • استفاده از LRU برای حذف داده‌های قدیمی
  • مناسب برای توسعه، نه تولید

Dummy Cache (برای توسعه)

Dummy Cache هیچ داده‌ای ذخیره نمی‌کند و فقط API کش را شبیه‌سازی می‌کند. این روش برای محیط توسعه یا تست بسیار مناسب است.

پیکربندی


"BACKEND": "django.core.cache.backends.dummy.DummyCache"

استفاده از Cache Backend سفارشی

اگر نیاز خاصی دارید، می‌توانید backend سفارشی بنویسید:


"BACKEND": "path.to.backend"

اما مگر اینکه دلیل بسیار قانع‌کننده‌ای داشته باشید، بهتر است از backendهای رسمی جنگو استفاده کنید.


جمع‌بندی

جنگو مجموعه‌ای کامل از سیستم‌های کش ارائه می‌دهد: Redis، دیتابیس، فایل‌محور، حافظهٔ محلی و Dummy Cache. با انتخاب backend مناسب، می‌توانید سرعت سایت را به‌طور چشمگیری افزایش دهید و سربار پردازشی را کاهش دهید.

مقدمه

جنگو یک سیستم کش بسیار انعطاف‌پذیر ارائه می‌دهد که می‌توان آن را با پارامترهای مختلف تنظیم کرد. این پارامترها در بخش CACHES در فایل settings.py تعریف می‌شوند و رفتار کش را کنترل می‌کنند.


پارامترهای Cache Arguments

TIMEOUT

مدت زمان پیش‌فرض نگهداری داده‌ها در کش (بر حسب ثانیه). مقدار پیش‌فرض: 300 ثانیه.

  • None: داده‌ها هرگز منقضی نمی‌شوند.
  • 0: داده‌ها بلافاصله منقضی می‌شوند (عملاً کش غیرفعال می‌شود).

OPTIONS

پارامترهای اضافی که به backend کش ارسال می‌شوند. این گزینه‌ها بسته به نوع backend متفاوت‌اند.

MAX_ENTRIES

حداکثر تعداد آیتم‌هایی که کش می‌تواند نگه دارد. پیش‌فرض: 300.

CULL_FREQUENCY

نسبت حذف داده‌ها هنگام رسیدن به MAX_ENTRIES. فرمول: 1 / CULL_FREQUENCY.

  • مثال: مقدار 2 یعنی حذف 50٪ داده‌ها.
  • مقدار 0 یعنی حذف کامل کش هنگام پر شدن.

KEY_PREFIX

پیشوندی که به تمام کلیدهای کش اضافه می‌شود. برای جلوگیری از تداخل بین چند سایت مفید است.

VERSION

نسخهٔ پیش‌فرض کلیدهای کش. برای invalidation گروهی بسیار کاربردی است.

KEY_FUNCTION

مسیر یک تابع که تعیین می‌کند کلید نهایی کش چگونه ساخته شود. این تابع ترکیب prefix، version و key را کنترل می‌کند.


نمونه پیکربندی File-Based Cache


CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",
        "TIMEOUT": 60,
        "OPTIONS": {"MAX_ENTRIES": 1000},
    }
}

نمونه پیکربندی PyLibMC با امکانات پیشرفته


"OPTIONS": {
    "binary": True,
    "username": "user",
    "password": "pass",
    "behaviors": {"ketama": True},
}

نمونه پیکربندی PyMemcache با Pooling


"OPTIONS": {
    "no_delay": True,
    "ignore_exc": True,
    "max_pool_size": 4,
    "use_pooling": True,
}

نمونه پیکربندی Redis با انتخاب دیتابیس و Pool سفارشی


"OPTIONS": {
    "db": "10",
    "pool_class": "redis.BlockingConnectionPool",
}

کش سراسری سایت (Per-Site Cache)

ساده‌ترین روش کش کردن کل سایت استفاده از middlewareهای کش است.

افزودن Middleware


MIDDLEWARE = [
    "django.middleware.cache.UpdateCacheMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.cache.FetchFromCacheMiddleware",
]

نکته مهم: UpdateCacheMiddleware باید اولین middleware باشد و FetchFromCacheMiddleware باید آخرین باشد.

تنظیمات لازم


CACHE_MIDDLEWARE_ALIAS = "default"
CACHE_MIDDLEWARE_SECONDS = 600
CACHE_MIDDLEWARE_KEY_PREFIX = "mysite"

این روش فقط پاسخ‌های GET و HEAD با وضعیت 200 را کش می‌کند.


کش سطح View (Per-View Cache)

برای کش کردن خروجی یک view خاص، از decorator cache_page استفاده می‌شود.

مثال ساده


from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...

استفاده از کش خاص


@cache_page(60 * 15, cache="special_cache")
def my_view(request):
    ...

استفاده از key_prefix


@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
    ...

تعریف کش در URLconf

برای جلوگیری از وابستگی view به کش، می‌توان decorator را در URLconf اعمال کرد:


urlpatterns = [
    path("foo//", cache_page(60 * 15)(my_view)),
]

جمع‌بندی

جنگو امکانات بسیار قدرتمندی برای مدیریت کش ارائه می‌دهد. با استفاده از پارامترهای پیشرفتهٔ CACHES، کش سراسری سایت و کش سطح view، می‌توانید عملکرد سایت را به‌طور چشمگیری بهبود دهید. این ابزارها به شما اجازه می‌دهند کنترل دقیقی بر نحوهٔ ذخیره‌سازی، انقضا، کلیدگذاری و مدیریت کش داشته باشید.

مقدمه

جنگو چندین لایهٔ کش ارائه می‌دهد، اما گاهی کش کامل صفحه یا کش سطح view بیش از حد بزرگ است. در چنین شرایطی، کش قطعه‌ای قالب (Template Fragment Caching) بهترین گزینه است. برای کنترل بیشتر، جنگو یک API سطح پایین کش نیز ارائه می‌دهد که امکان ذخیره‌سازی هر نوع دادهٔ پایتونی را فراهم می‌کند.


کش قطعه‌ای قالب (Template Fragment Caching)

برای استفاده از این قابلیت، ابتدا باید تگ کش را در قالب بارگذاری کنید:


{% load cache %}

استفادهٔ پایه

تگ {% cache %} محتوای داخل بلوک را برای مدت مشخصی کش می‌کند:


{% cache 500 sidebar %}
    ... محتوای سایدبار ...
{% endcache %}

آرگومان دوم (sidebar) نام قطعه است و باید یک رشتهٔ ثابت باشد.

کش بر اساس داده‌های پویا

می‌توانید نسخه‌های مختلفی از یک قطعه را بر اساس داده‌های پویا کش کنید:


{% cache 500 sidebar request.user.username %}
    ... سایدبار مخصوص کاربر ...
{% endcache %}

کش چندزبانه

اگر سایت چندزبانه است، می‌توانید کش را بر اساس زبان فعال تغییر دهید:


{% load i18n %}
{% load cache %}

{% get_current_language as LANGUAGE_CODE %}

{% cache 600 welcome LANGUAGE_CODE %}
    {% translate "Welcome to example.com" %}
{% endcache %}

استفاده از متغیر برای timeout

timeout می‌تواند یک متغیر قالب باشد:


{% cache my_timeout sidebar %}
    ...
{% endcache %}

انتخاب backend کش با using

به‌طور پیش‌فرض، تگ کش از backend با نام template_fragments استفاده می‌کند. اگر وجود نداشته باشد، از default استفاده می‌شود. می‌توانید backend دیگری را مشخص کنید:


{% cache 300 local_fragment using="localcache" %}
    ...
{% endcache %}

اگر نام backend اشتباه باشد، خطا رخ می‌دهد.


دریافت کلید کش قطعه

برای حذف یا بررسی یک قطعهٔ کش‌شده، از تابع زیر استفاده کنید:


from django.core.cache.utils import make_template_fragment_key
from django.core.cache import cache

key = make_template_fragment_key("sidebar", [username])
cache.delete(key)

این روش برای invalidation دستی بسیار کاربردی است.


API سطح پایین کش در جنگو

وقتی کش قطعه‌ای کافی نیست، API سطح پایین کش امکان ذخیره‌سازی هر نوع دادهٔ پایتونی را فراهم می‌کند. هر شیء قابل pickle شدن را می‌توان کش کرد: رشته، لیست، دیکشنری، مدل‌ها و غیره.

دسترسی به backendهای کش

برای دسترسی به یک backend خاص:


from django.core.cache import caches

cache1 = caches["myalias"]
cache2 = caches["myalias"]
cache1 is cache2  # True

کش پیش‌فرض:


from django.core.cache import cache

عملیات پایه

cache.set()


cache.set("my_key", "hello world", 30)

cache.get()


cache.get("my_key")

اگر کلید وجود نداشته باشد:


cache.get("my_key", "expired")

تشخیص None واقعی

برای تشخیص اینکه مقدار None ذخیره شده یا کلید وجود ندارد:


sentinel = object()
cache.get("my_key", sentinel) is sentinel

cache.add()

فقط اگر کلید وجود نداشته باشد مقدار ذخیره می‌شود:


cache.set("add_key", "initial")
cache.add("add_key", "new")  # نادیده گرفته می‌شود

cache.get_or_set()

اگر کلید وجود نداشته باشد، مقدار جدید ذخیره می‌شود:


cache.get_or_set("timestamp", datetime.datetime.now, 100)

جمع‌بندی

کش قطعه‌ای قالب به شما امکان می‌دهد بخش‌های سنگین صفحه را به‌صورت انتخابی کش کنید، در حالی که API سطح پایین کش کنترل کامل برای ذخیره‌سازی داده‌های دلخواه را فراهم می‌کند. ترکیب این دو ابزار می‌تواند عملکرد سایت‌های پیچیده و داده‌محور را به‌طور چشمگیری بهبود دهد.

مقدمه

API سطح پایین کش در جنگو به شما اجازه می‌دهد داده‌ها را با کنترل کامل ذخیره، بازیابی و مدیریت کنید. این API برای زمانی مناسب است که کش کامل صفحه یا کش قطعه‌ای قالب کافی نباشد و نیاز به مدیریت دقیق‌تر داده‌ها داشته باشید.


دریافت چند کلید با get_many()

برای دریافت چند مقدار با یک درخواست به کش:


cache.set("a", 1)
cache.set("b", 2)
cache.set("c", 3)

cache.get_many(["a", "b", "c"])
# {'a': 1, 'b': 2, 'c': 3}

این متد فقط کلیدهایی را برمی‌گرداند که واقعاً در کش موجود باشند.


ذخیرهٔ چند مقدار با set_many()

برای ذخیرهٔ چند کلید به‌صورت هم‌زمان:


cache.set_many({"a": 1, "b": 2, "c": 3})

در backendهایی مثل Memcached، این متد لیستی از کلیدهای ناموفق را برمی‌گرداند.


حذف کلید با delete()


cache.delete("a")
# True

اگر کلید حذف شود True برمی‌گردد.


حذف چند کلید با delete_many()


cache.delete_many(["a", "b", "c"])

حذف کامل کش با clear()

این متد تمام داده‌های کش را حذف می‌کند:


cache.clear()

در استفاده از آن باید بسیار محتاط بود.


تمدید زمان انقضا با touch()

برای تمدید زمان انقضای یک کلید:


cache.touch("a", 10)
# True

افزایش و کاهش مقدار با incr() و decr()

برای افزایش یا کاهش مقدار یک کلید عددی:


cache.set("num", 1)
cache.incr("num")      # 2
cache.incr("num", 10)  # 12
cache.decr("num")      # 11
cache.decr("num", 5)   # 6

اگر کلید وجود نداشته باشد، ValueError رخ می‌دهد. در backendهایی مثل Memcached این عملیات‌ها atomic هستند.


بستن اتصال کش با close()


cache.close()

اگر backend متد close نداشته باشد، این فراخوانی بی‌اثر است.


Prefix کلیدهای کش (KEY_PREFIX)

اگر چند سرور یا چند محیط (production و development) از یک کش مشترک استفاده کنند، ممکن است داده‌ها با هم تداخل پیدا کنند. برای جلوگیری از این مشکل، از KEY_PREFIX استفاده کنید.

هر کلید کش به‌صورت خودکار با این prefix ترکیب می‌شود.


نسخه‌بندی کلیدها (Cache Versioning)

برای invalidation گروهی بدون حذف کل کش، از نسخه‌بندی استفاده کنید.

مثال:


cache.set("my_key", "hello", version=2)
cache.get("my_key")            # None (نسخهٔ پیش‌فرض 1 است)
cache.get("my_key", version=2) # 'hello'

افزایش نسخهٔ یک کلید


cache.incr_version("my_key")
cache.get("my_key", version=3)  # 'hello'

تغییر شکل کلیدها (KEY_FUNCTION)

به‌طور پیش‌فرض کلید نهایی به شکل زیر ساخته می‌شود:


key_prefix:version:key

اگر بخواهید کلیدها را هش کنید یا ساختار دیگری داشته باشند، می‌توانید یک تابع سفارشی تعریف کنید و مسیر آن را در KEY_FUNCTION قرار دهید.


هشدارهای مربوط به کلیدهای کش (CacheKeyWarning)

Memcached اجازهٔ کلیدهای طولانی‌تر از 250 کاراکتر یا کلیدهای دارای whitespace را نمی‌دهد. برای جلوگیری از مشکلات، backendهای دیگر نیز در این شرایط هشدار می‌دهند.

بی‌صدا کردن هشدار


import warnings
from django.core.cache import CacheKeyWarning

warnings.simplefilter("ignore", CacheKeyWarning)

اعتبارسنجی سفارشی کلید

می‌توانید backend را subclass کنید و متد validate_key را بازنویسی کنید.


from django.core.cache.backends.locmem import LocMemCache

class CustomLocMemCache(LocMemCache):
    def validate_key(self, key):
        ...

جمع‌بندی

API سطح پایین کش در جنگو ابزارهای قدرتمندی برای مدیریت دقیق داده‌های کش‌شده ارائه می‌دهد. با استفاده از متدهایی مانند get_many، set_many، touch، incr/decr و نسخه‌بندی کلیدها، می‌توانید یک سیستم کش انعطاف‌پذیر، سریع و قابل‌کنترل بسازید. این امکانات برای پروژه‌های بزرگ و داده‌محور بسیار ارزشمند هستند.

پشتیبانی ناهمگام (Asynchronous Support)

جنگو در حال توسعهٔ پشتیبانی ناهمگام برای backendهای کش است، اما هنوز کش ناهمگام کامل ارائه نشده است. کلاس BaseCache نسخه‌های ناهمگام تمام متدهای پایه را ارائه می‌دهد که با پیشوند a شروع می‌شوند.

مثال


await cache.aset("num", 1)
await cache.ahas_key("num")   # True

آرگومان‌های نسخهٔ ناهمگام و همگام یکسان هستند. پشتیبانی کامل در نسخه‌های آیندهٔ جنگو ارائه خواهد شد.


کش‌های پایین‌دستی (Downstream Caching)

کش‌های پایین‌دستی سیستم‌هایی هستند که قبل از رسیدن درخواست به جنگو، پاسخ‌ها را کش می‌کنند. نمونه‌ها:

  • کش ISP در HTTP (در HTTPS ممکن نیست)
  • کش پروکسی مانند Squid
  • کش مرورگر

این نوع کش‌ها می‌توانند خطرناک باشند اگر صفحه شامل داده‌های حساس یا وابسته به کاربر باشد. کش اشتباه ممکن است اطلاعات خصوصی را برای کاربران دیگر آشکار کند.


استفاده از هدرهای Vary

هدر Vary به کش‌ها می‌گوید که خروجی صفحه به کدام هدرهای درخواست وابسته است. مثلاً اگر صفحه بر اساس زبان یا کوکی تغییر کند، باید روی آن هدرها vary شود.

استفاده از vary_on_headers()


from django.views.decorators.vary import vary_on_headers

@vary_on_headers("User-Agent")
def my_view(request):
    ...

چند هدر


@vary_on_headers("User-Agent", "Cookie")
def my_view(request):
    ...

vary بر اساس کوکی

دو نسخهٔ زیر معادل‌اند:


@vary_on_cookie
def my_view(request):
    ...

@vary_on_headers("Cookie")
def my_view(request):
    ...

استفاده از patch_vary_headers()


from django.utils.cache import patch_vary_headers

response = render(request, "template.html", context)
patch_vary_headers(response, ["Cookie"])

کنترل کش با Cache-Control

هدرهای Cache-Control تعیین می‌کنند که یک پاسخ عمومی باشد یا خصوصی، چه مدت کش شود و رفتار کش چگونه باشد.

علامت‌گذاری پاسخ به‌عنوان private


from django.views.decorators.cache import cache_control

@cache_control(private=True)
def my_view(request):
    ...

تنظیم دستی Cache-Control


from django.views.decorators.cache import patch_cache_control

patch_cache_control(response, public=True)

تنظیم max-age


@cache_control(max_age=3600)
def my_view(request):
    ...

دیگر directiveهای معتبر

  • no_transform=True
  • must_revalidate=True
  • stale_while_revalidate=ثانیه
  • no_cache=True

غیرفعال کردن کامل کش


from django.views.decorators.cache import never_cache

@never_cache
def my_view(request):
    ...

ترتیب Middleware در کش

در استفاده از caching middleware، ترتیب بسیار مهم است.

ترتیب صحیح


MIDDLEWARE = [
    "django.middleware.cache.UpdateCacheMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.cache.FetchFromCacheMiddleware",
]

چرا ترتیب مهم است؟

  • UpdateCacheMiddleware در فاز response اجرا می‌شود و باید قبل از middlewareهایی باشد که هدر Vary را تغییر می‌دهند.
  • FetchFromCacheMiddleware در فاز request اجرا می‌شود و باید بعد از middlewareهایی باشد که هدر Vary را تغییر می‌دهند.

Middlewareهایی که هدر Vary را تغییر می‌دهند

  • SessionMiddleware → اضافه کردن Cookie
  • GZipMiddleware → اضافه کردن Accept-Encoding
  • LocaleMiddleware → اضافه کردن Accept-Language

جمع‌بندی

جنگو ابزارهای قدرتمندی برای مدیریت کش سمت سرور و کش‌های پایین‌دستی ارائه می‌دهد. با استفاده از هدرهای Vary، کنترل Cache-Control، پشتیبانی ناهمگام در حال توسعه و ترتیب صحیح middleware، می‌توانید یک سیستم کش امن، سریع و بهینه برای پروژهٔ خود طراحی کنید.

نوشته و پژوهش شده توسط دکتر شاهین صیامی