Oruji.org
Oruji.orgPersian Tutorials
ورود
ویرایش: 1396/11/13 19:20

آموزش Caching در جنگو (Django)

هر بار که یک کاربر یک صفحه را درخواست می کند، وب سرور تمام محاسبات را ایجاد می کند – از کوئری های پایگاه داده جهت render کردن template برای business logic – برای ساختن صفحه ای که بازدید کنندگان سایت می بینند. این حرکت از نظر بار اضافی بسیار پر خرج تر و سنگین تر از خواندن فایل از filesystem می باشد.

برای اغلب برنامه های وب، این بار اضافی یک درگیری بزرگی به حساب نمی آید. اغلب برنامه های وب washingtonpost.com یا slashdot.org نیستند؛ آن ها وب سایت هایی یا اندازهای کوچک و متوسط و با ترافیکی به همین شکل می باشند. ولی برای سایت های با ترافیک بالا، حذف بارهای اضافی تا حد ممکن یک ضرورت به حساب می آید.

در آنجا بود که cashing بوجود آمد.

cache کردن چیزی، ذخیره ی نتیجه ی یک محاسبه ی پر خرج به طوری که مجبور نباشید محاسبه را در بار بعدی انجام دهید می باشد. در زیر تعدادی شبه کد وجود دارد که نحوه ی این عمل را برای یک صفحه ی وب به طور پویا تولید شده توضیح می دهد:

given a URL, try finding that page in the cache if the page is in the cache: return the cached page else: generate the page save the generated page in the cache (for next time) return the generated page

فریم ورک یا چارچوب جنگو یک سیستم قدرتمند cache را ارائه می کند که اجازه می دهد صفحات پویا را به طوری که اجباری برای مورد محاسبه قرار دادن برای هر درخواست نداشته باشید ذخیره کنید. برای راحتی، جنگو (Django) سطح های متفاوتی از cache به صورت دانه دانه را ارائه می دهد: می توان خروجی view های خاص را cache کرد، می توان تنها قسمت هایی که برای تولید مشکل می باشند را cache کرد، یا می توان تمام سایت را cache کرد.

همچنین جنگو با cache ها "upstream" به خوبی کار می کند، مانند Squid (http://www.squid-cache) و cache های بر پایه ی مرورگر. این ها انواعی از cache هایی هستند که به طور مستقیم کنترل نمی شوند ولی می توان تذکراتی (از طریق HTTP headers) درباره ی قسمت هایی از سایت که باید cache شده باشد و نحوه ی آن تهیه کرد.

نصب کردن Cache

سیستم cache نیازمند نصب کردن اندکی از مقدادیر می باشد. به عبارت دیگر، باید جایی که داده ی cache شده ی شما باید وجود داشته باشد را به آن بگویید – خواه در یک پایگاه داده، در filesystem یا مستقیما در حافظه. این یک تصمیم مهم می باشد که بر روی اجرای cache شما تاثیر می گذارد؛ بله، برخی از انواع cache ها از انواع دیگر سریع تر می باشند.

اولویت cache شما در تنظیم CACHE_BACKEND درون فایل تنظیمات می باشد. در زیر توضیحی از تمام مقادیر قابل دسترس برای CACHE_BACKEND وجود دارد.

Memcached

تاکنون سریع ترین، موثرترین نوع cache در دسترس برای جنگو، Memcached یک فریم ورک cache بر پایه ی حافظه می باشد که در ابتدا برای کنترل بارگذاری های بالا در LiveJournal.com و به دنبال آن (در دست ترجمه ...). این نوع cache توسط سایت هایی از قبیل Facebook و Wikipedia جهت کاهش دسترسی پایگاه داده و به طور چشمگیری افزایش کارایی سایت استفاده شده است.

Memcached به صورت آزاد و مجانی در http://danga.com/memchashed/ در دسترس می باشد. این cache به صورت daemon اجرا شده و مقدار مشخص از RAM را اختصاص داده است. تمام کاری که این نوع cache انجام می دهد، تهیه ی یک رابط سریع برای اضافه کردن، بازیابی و حذف داده دلخواه در cache می باشد. تمام داده به طور مستقیم در حافظه ذخیره شده است، بنابراین هیچ بار اضافه ای برای پایگاه داده یا استفاده filesystem وجود ندارد.

بعد از نصب خود Memchached، نیاز به نصب اتصالات پایتون Memcached خواهیم داشت، که به طور مستقیم همراه جنگو نمی باشند. دو نسخه از این قابل دسترس می باشند. یکی از ماژول های زیر را انتخاب و نصب کنید:

جهت استفاده Memcached با جنگو، CACHE_BACKEND را با مقدار memcached://ip:port/ تنظیم کنید، جایی که ip آدرس IP Memcached daemon و port، پورت Memcached ای می باشد که در حال اجرا است.

در مثال زیر، Memcached در localhost (127.0.0.1) پورت 11211 در حال اجرا می باشد:

CACHE_BACKEND = 'memcached://127.0.0.1:11211/'

یکی از ویژگی های بسیار خوب Memcached، توانایی آن برای به اشتراک گذاشتن cache در سرتاسر چندین سرور می باشد. بدین معنی که شما می توانید Memcached daemon ها را در چندین ماشین اجرا کرده و برنامه با گروهی از ماشین به صورت یک cache تنها رفتار خواهد کرد، بدون نیاز به مقادیر cache تکراری در هر ماشین. جهت بهره بردن از این خصوصیت، تمام آدرس های سرورها را در CACHE_BACKEND که با علامت (;) از هم جدا شده اند قرار دهید.

در مثال زیر، cache در سرتاسر نمونه های Memcachedd در حال اجرا در آدرس IP های 172.19.26.240 و 172.19.26.242 هر دو در پورت 11211 به اشتراک گذاشته شده اند.

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/'

در مثال زیر، cache در سرتاسر نمونه های Memcached در حال اجرا در آدرس IP های 172.19.26.240 (پورت 11211) و 172.19.26.242 (پورت 11212) و 172.19.26.244 (پورت 11213) به اشتراک گذاشته شده است.

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'

نکته آخر درباره ی Memcached این است که، cache بر پایه ی حافظه دارای یک اشکال نیز می باشد: به این دلیل که داده cache شده درون حافظه ذخیره می شود، در صورتی که سرور شما crash کند داده مورد نظر از بین خواهد رفت. واضح است که، حافظه برای ذخیره سازی داده به طور دائمی در نظر گرفته نشده است، بنابراین به cache کردن بر پایه حافظه برای تنها ذخیره داده اعتماد نکنید. بدون هیچ شکی، باطن هیچکدام از سیستم های cache جنگو برای ذخیره سازی دائمی در نظر گرفته نشده اند – آن ها به طور کلی راهکارهایی برای cache کردن داده می باشند، نه ذخیره سازی – ولی به این موضوع در اینجا اشاره کردیم، چرا که cache کردن بر پایه حافظه به طور خاص موقتی می باشد.

Cache کردن پایگاه داده

جهت استفاده از یک جدول پایگاه داده برای cache، ابتدا یک جدول cache درون پایگاه داده خود توسط اجرای دستور زیر ایجاد کنید:

python manage.py createcachetable [cache_table_name]

... جایی که [cache_table_name] نام جدول پایگاه داده ای می باشد که ساخته خواهد شد. (این نام می تواند هر چیزی که می خواهید باشد، تا زمانی که یک نام جدول معتبر باشد و درون پایگاه داده شما وجود نداشته باشد.) این دستور یک جدول تنها در پایگاه داده شما ایجاد می کند که در قالب بندی مناسبی که سیستم cache پایگاه داده انتظار دارد می باشد.

هنگامی که شما جدول پایگاه داده را ایجاد کردید، تنظیم CACHE_BACKEND را با "db://tablename" تنظیم کنید، جایی که tablename نام جدول پایگاه داده می باشد. در مثال زیر، نام جدول cache نام my_cache_table می باشد:

CACHE_BACKEND = 'db://my_cache_table'

cache پایگاه داده از پایگاه داده همسان به صورتی که درون فایل تنظیمات تعیین شده است استفاده می کند. شما نمی توانید از پایگاه داده متفاوتی برای جدول cache خود استفاده کنید.

cache پایگاه داده در صورتی که دارای یک پایگاه داده ی سریع باشید بسیار عالی کار خواهد کرد.

Cache کردن Filesystem

جهت ذخیره ی آیتم های cache شده در یک filesystem، از "file://" در CACHE_BACKEND استفاده کنید. به عنوان مثال، جهت ذخیره داده ی cache شده در /var/tmp/django_cache از تنظیم زیر استفاده کنید:

CACHE_BACKEND = 'file:///var/tmp/django_cache'

توجه داشته باشید که سه علامت (/) در شروع مثال فوق وجود دارد. دوتای اول برای file://، و سومی، اولین حرف مسیر دایرکتوری /var/tmp/django_cache می باشد. در صورتی که در سیستم عامل ویندوز هستید، حرف درایو را بعد از file:// مانند زیر قرار دهید:

file://c:/foo/bar

مسیر دایرکتوری باید کامل باشد – بدین بدان معنی است که، باید از ریشه filesystem شروع شود. گذاشتن یا نذاشتن علامت (/) در پایان تنظیم اهمیتی ندارد.

اطمینان حاصل کنید دایرکتوری اشاره شده توسط این تنظیم وجود داشته و قابل نوشتن و خواندن توسط کاربر سیستمی که وب سرور درون آن اجرا می شود باشد. در ادامه مثال فوق، در صورتی که سرور شما به صورت کاربر apache اجرا می شود، اطمینان حاصل کنید که دایرکتوری /var/tmp/django_cache وجود داشته و قابل نوشتن و خواندن توسط کاربر apache باشد.

هر مقدار cache ای به صورت یک فایل جدا ذخیره شده خواهد بود که محتویات داده cach ذخیره شده در یک قالب بندی سریال شده ("pickled") توسط ماژول pickle پایتون هستند. هر نام فایل کلید cache رها شده برای استفاده امن filesystem می باشد.

Cache کردن حافظه ی داخلی

اگر مزایای سرعت cache در حافظه را بدون قابلیت اجرای Memcached می خواهید، cache حافظه داخلی را ملاحظه کنید. این cache چند پردازشی و thread-safe می باشد. برای استفاده از آن، تنظیم CACHE_BACKEND را با "locmem:///" تنظیم کنید. برای مثال:

CACHE_BACKEND = 'locmem:///'

توجه داشته باشید که هر پردازش دارای نمونه cache خصوصی خود می باشد، که بدین معنی است که cache به صورت cross‑process ممکن خواهد بود. همچنین واضح است که حافظه ی داخلی cache منحصرا حافظه ی کار آمد به حساب نمی آید، بنابراین شاید برای محیط های تولید انتخاب مناسبی نباشد. این نوع cache برای توسعه عالی می باشد.

Cache کردن ساختگی (برای توسعه)

در پایان، جنگو یک cache با نام "dummy" ارائه کرده است که در واقع cache نیست – این تنها بدون انجام چیزی رابط cache را اجرا می کند.

این نوع cache در صورتی که شما دارای یک سایت تولید باشید که از cache سنگینی را در مکان های گوناگون استفاده کند مفید است. ولی مکان ها یک محیط توسعه/آزمون جایی که نمی خواهید cache انجام شود و نمی خواهید لزوما کد شما برای مورد خاص اخیر تغییر کند. جهت فعال کردن dummy cache، تنظیم CACHE_BACKEND را مانند زیر تنظیم کنید:

CACHE_BACKEND = 'dummy:///'

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

هنگامی که جنگو پشتیبانی از تعداد از cache ها بدون هیچ تنظیمی را ارائه می کند. ممکن است بخواهید از یک cache سفارشی شده استفاده کنید. برای استفاده از یک cache خارجی با جنگو، از مسیر import پایتون به صورت قسمت طرح (قسمتی قبل از تعریف علامت کالن ":") از URL، CACHE_BACKEND مانند زیر استفاده کنید:

CACHE_BACKEND = 'path.to.backend://'

در صورتی که cache مخصوص خود را می سازید، می توانید از cache استاندارد به صورت پیاده سازی مرجع استفاده کنید. درون دایرکتوری django/core/cache/backends/ از منبع جنگو کد را خواهید یافت.

نکته: بدون هیچ دلیل قانع کننده ای، مانند به عنوان مثال پشتیبانی نکردن یک میزبانی از آیتمی، شما باید به cache های درون جنگو (Django) وجود دارند استفاده کنید. آن ها بخوبی مورد آزمون قرار گرفته و استفاده از آن ها ساده می باشد.

آرگومان های CACHE_BACKEND

هر نوع cache ای ممکن است آرگومان هایی دریافت کند. آن ها به شکل query-string در تنظیم CACHE_BACKEND داده شده می باشند. آرگومان های معتبر از این قرار می باشند:

در مثال زیر، timeout مقدار 60 می باشد:

CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=60"

در مثال زیر، timeout مقدار 30 بوده و max_entires مقدار 400 می باشد:

CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400"

آرگومان های نا معتبر بدون هیچ خطایی رد می شوند، به طوری که مقادیر نا معتبر آرگومان های شناخته شده رد می شوند.

Cache در هر سایت

هنگامی که cache راه اندازی شده است، ساده ترین روش جهت استفاده از cache، cache کردن کل سایت می باشد. نیاز به اضافه کردن 'django.middleware.cache.UpdateCacheMiddleware' و 'django.middleware.cache.FetchFromCacheMiddleware' به تنظیم مورد نظر یعنی MIDDLEWARE_CLASSES خواهید داشت، مانند مثال زیر:

MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', )

سپس، نیازمندی های زیر را به فایل تنظیمات جنگو خود اضافه کنید:

middleware مربوط به cache، هر صفحه ای که دارای پارامتر GET یا POST نباشد را cache می کند. به طور اختیاری، در صورتی که تنظیم CACHE_MIDDLEWARE_ANONYMOUS_ONLY مقدار True را داشته باشد، تنها درخواست های anonymous (نه آن هایی که توسط یک کاربر وارد شده ساخته شده باشند) cache شده خواهند بود. این یک روش ساده و موثر از از کار انداختن عمل cache برای صفحات هر کاربر خاص (شمال رابط مدیر جنگو) می باشند. دقت داشته باشید، اگر از CACHE_MIDDLEWARE_ANONYMOUS_ONLY استفاده می کنید، باید اطمینان حاصل کنید AuthenticationMiddleware فعال کرده اید.

علاوه بر این، cache middleware به طور خودکار تعدادی header در هر HttpResponse قرار می دهد:

برای اطلاعات بیشتر در مورد middleware به مبحث middleware مراجعه کنید.

در صورتی که یک view زمان انقضای (به عنوان مثال دارای یک بخش max-age در هدر Cache-Control خود باشد) خود را قرار دهد، سپس صفحه تا زمان انقضا cache شده خواهد بود، به جای CACHE_MIDDLEWARE_SECONDS. استفاده از decorator ها در django.views.decorators.cache می توان به سادگی یک زمان انقضای view (با استفاده از decorator، cache_control) قرار داد یا cache برای یک view را غیر فعال کرد (با استفاده از decorator، never_cache). برای اطلاعات بیشتر در مورد این decorator ها به بخش "استفاده از header های دیگر" در ادامه ی همین آموزش از کتاب مراجعه کنید.

Cache در ازای هر View

یک روش cache در مقیاس کوچک تر برای استفاده از فریم ورک یا چارچوب cache به شکل cache کردن خروجی view های منحصر به فرد می باشد. django.views.decorators.cache یک decorator با نام cache_page تعریف می کند که به طور خودکار پاسخ view را برای شما cache می کند. این روش برای استفاده ساده می باشد:

from django.views.decorators.cache import cache_page def my_view(request): # ... my_view = cache_page(my_view, 60 * 15)

همچنین می توانید از دستور زبان پایتون 2.4 به بالا استفاده کنید:

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

cache_page یک آرگومان تنها دریافت می کند: timeout مربوط به cache، بر حسب ثاینه. در مثال بالا، نتیجه view مورد نظر یعنی my_view() برای 15 دقیقه cache شده خواهد بود. (توجه داشته باشید که جهت خوانایی بیشتر به صورت 60 * 15 نوشته شده است. 60 * 15 به صورت 900 ارزیابی خواهد شد – این بدان معنی است که، 15 دقیقه توسط ضرب 60 ثانیه در هر دقیقه بدست می آید.)

cache به ازای هر view، مانند cache به ازای هر سایت، (در دست ترجمه ...). در صورتی که چندین URL به یک view همسان اشاره کنند، هر URL به صورت جداگانه cache خواهد شد. در ادامه مثال my_view، در صورتی که URLconf شما مانند زیر باشد:

urlpatterns = ('', (r'^foo/(\d{1,2})/$', my_view), )

سپس درخواست های به /foo/1/ و /foo/23/ به طور جداگانه cache خواهند شد، به صورتی که ممکن است انتظار داشته باشید. ولی هنگامی که یک URL خاص (مانند /foo/23/) درخواست شده باشد، درخواست های بعدی به آن URL از cache استفاده خواهند کرد.

تعیین به ازای هر Cache View در URLconf

مثال های بخش قبلی دارای کد مسقیم زده شده در view ای که مورد cache قرار می گرفت بودند، زیرا cache_page تابع my_view را در محل تغییر می دهد. این رویکرد view شما را به سیستم cache جفت می کند، که به دلایلی ایده آل نمی باشد. به عنوان مثال، ممکن است بخواهید از توابع view در جایی دیگر، سایت بدون cache استفاده کنید، یا ممکن است view ها را به افرادی توزیع کنید که ممکن است بخواهند از آن ها بدون cache شدن استفاده کنند. راهکار برای این مشکلات، تعیین cache به ازای هر view به جای قرار گرفتن در خود توابع view درون URLconf می باشد.

انجام این کار ساده می باشد: به سادگی عبارت cache_page که درون تابع view قرار گرفته است را درون URLconf اشاره کننده به این تابع view قرار دهید. در زیر URLconf قبلی را مشاهده می کنید:

urlpatterns = ('', (r'^foo/(\d{1,2})/$', my_view), )

در زیر کدی همسان وجود دارد، با این تفاوت که cache_page درون URLconf قرار گرفته است:

from django.views.decorators.cache import cache_page urlpatterns = ('', (r'^foo/(\d{1,2})/$', cache_page(my_view, 60 * 15)), )

در صورتی که از این رویکرد استفاده می کنید، قرار داده cache_page را درون URLconf فراموش نکنید.

Template Fragment Caching

در صورتی که خواستار کنترل بیشتر می باشید، همچنین می توانید قطعه های template را با استفاده تگ template ای با نام cache، cache کنید. جهت دادن دسترسی templateبه این تگ، {% load cache %} را در بالای template خود قرار دهید.

تگ {% cache %} محتویات بلاک برای مقدار زمان داده شده را cache می کند. این تگ حداقل دو آرگومان دریافت می کند: cache timeout بر حسب ثانیه، و نام برای دادن قطعه cache. برای مثال:

{% load cache %} {% cache 500 sidebar %} .. sidebar .. {% endcache %}

گاهی اوقات ممکن است بخواهید چندین کپی از یک قطعه را بسته به برخی داده های پویا که داخل قطعه ظاهر می شوند cache کنید. برای مثال، ممکن است یک کپی جدای cache شده از نوار کناری استفاده شده در مثال قبلی برای هر کاربر از سایت خود را بخواهید. توسط ارسال آرگومان های اضافه به تگ {% cache %} برای تشخیص قطعه cache به طور منحصر به فرد این کار را انجام دهید:

{% load cache %} {% cache 500 sidebar request.user.username %} .. sidebar for logged in user .. {% endcache %}

تعیین بیشتر از یک آرگومان برای تشخیص قطعه کاملا خوب می باشد. به سادگی برخی آرگومان ها را به {% cache %} همان طور که نیاز دارید ارسال کنید.

cache timeout می تواند یک متغیر template باشد، تا زمانی که متغیر template یک مقدار integer باشد. برای مثال، در صورتی که متغیر my_timeout مقدار 600 برایش قرار گرفته باشد، سپس دو مثال زیر با هم برابر هستند:

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

این خصوصیت برای اجتناب از تکرار در template ها مفید می باشد. می توان timeoutرا درون یک متغیر قرار داد، در یک جا، و تنها از آن مقدار دوباره استفاده کرد.

API سطح پایین Cache

گاهی اوقات، cache کردن تمام صفحه ی render شده فایده ی خیلی زیادی برای شما ندارد، در واقع بیش از حد نا مناسب می باشد.

ممکن است، برای مثال، سایت شما حاوی یک view باشد که بسته به چندین کوئری پر خرج نتیجه دهد، نتایج از آن تغییر در فواصل مختلف. در این مورد، استفاده از cache تمام صفحه ایده آل نمی باشد که به ازای هر سایت یا هر view استراتژی های ارائه شده cache، زیرا شما نمی خواهید تمام نتیجه (از آنجایی که برخی از داده ها اغلب تغییر می کنند) را cache کنید، ولی همچنان می خواهید نتایجی که به ندرت تغییر می کنند را cache کنید.

برای موارد شبیه به این، جنگو یک cache API سطح پایین ساده را ارائه می کند. می توان از این API جهت ذخیره ی شیء هایی در cache با هر سطحی که می خواهید استفاده کرد. می توان هر شیء پایتونی که می تواند به طور امن pickled باشد را cache کرد: رشته ها، دیکشنری ها، لیست شیء های مدل، و غیره ... (اغلب شیء های رایج پایتون می توانند pickled باشند؛ برای اطلاعات بیشتر درباره pickling به مستندات پایتون مراجعه کنید.)

ماژول cache، django.core.cache دارای یک شیء cache می باشد که به طور خودکار از تنظیم CACHE_BACKEND ساخته شده است:

>>> from django.core.cache import cache

رابط اصلی set(key, value, timeout_seconds) و get(key):

>>> cache.set('my_key', 'hello, world!', 30) >>> cache.get('my_key') 'hello, world!'

آرگومان timeout_seconds اختیاری می باشد و به آرگومان timeout در تنظیم CACHE_BACKEND بر می گردد.

در صورتی که شیء در cache مورد نظر یعنی cache.get() وجود نداشته باشد None بر می گرداند:

# Wait 30 seconds for 'my_key' to expire... >>> cache.get('my_key') None

توصیه می شود مقدار واقعی None را در cache ذخیره نکنید، چرا که قادر به تشخیص مقدار ذخیره کرده ی None خود و دیگر مقادیر None نخواهید بود.

cache.get() می تواند یک آرگومان default دریافت کند. این آرگومان مقدار برگشت داده شده، در صورت عدم وجود شیء در cache را تعیین می کند:

>>> cache.get('my_key', 'has expired') 'has expired'

جهت اضافه کردن یک کلید تنها در صورتی که وجود نداشته باشد، از متد add() استفاده کنید. این متد پارامترهایی همانند set() دریافت می کند، ولی این متد در صورتی که کلید تعیین شده حاضر باشد (وجود داشته باشد) تلاشی برای به روز رسانی نخواهد کرد:

>>> cache.set('add_key', 'Initial value') >>> cache.add('add_key', 'New value') >>> cache.get('add_key') 'Initial value'

در صورتی که نیاز به دانستن این موضوع دارید که آیا add() یک مقدار در cache ذخیره کرده است، می توانید مقدار برگشتی را بررسی کنید. این مقدار در صورتی که مقدار ذخیره شده باشد True و در غیر این صورت مقدار False بر می گرداند.

همچنین یک رابط بانام get_many() وجود دارد که تنها یک بار (در دست ترجمه ...). get_many() یک دیکشنری با تمام کلیدهایی که خواسته اید و در واقع در cache وجود داشته باشد بر می گرداند.

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

در پایان، می توان به طور واضح با delete() کلیدها را حذف کرد. این یک روش ساده از حذف cache برای یک شیء خاص می باشد:

>>> cache.delete('a')

همچنین می توان با استفاده از متدهای incr() و decr() یک کلید موجود را به ترتیب افزایش و کاهش داد. به طور پیشفرض، مقدار cache موجود توسط مقدار 1، افزایش یا کاهش داده خواهد شد. مقادیر دیگر افزایش/کاهش می توانند توسط تهیه ی یک آرگومان برای فراخوانی افزایش/کاهش تعیین شده باشند. در صورتی که تلاش کنید یک کلید cache ای که وجود ندارد را افزایش یا کاهش دهید یک خطا ایجاد خواهد شد.:

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

Cache های بالا دست

تا کنون این آموزش از کتاب، بر روی cache داده های خودتان تمرکز داشته است. ولی نوع دیگری از cache، مربوط به توسعه ی وب می باشد که توسط cache های "بالا دست" انجام می شود. این ها سیستم هایی هستند که صفحات را برای کاربران حتی قبل از رسیدن درخواست به وب سایت شما، cache می کنند.

در اینجا مثال از cache های بالا دست وجود دارد:

cache بالا دست یک افزایش بهره وری خوب می باشد، ولی یک خطر در آن وجود دارد: بسیاری از محتویات صفحات وب از لحاظ authentication و میزبانی از متغیرهای دیگر متفاوت می باشند، و سیستم های cache به طور کورکورانه صفحات مستقر در URL ها را می توانند به طور نادرس نشان دهند یا داده های حساس را به بازدیدکنندگان بعدی از آن صفحات نشان دهند.

به عنوان مثال، تصور کنید یک سیستم وب پست الکترونیکی را اداره می کنید، و محتویات صفحه ی "inbox" واضح است که بسته به کاربر وارد شده می باشد. در صورتی که یک ISP کورکورانه سایت شما را cache کند، سپس اولین کاربری که از طریق آن ISP وارد شود صفحه ی inbox، cache شده برای بازدید کنندگان بعدی از آن سایت نمایش داده خواهد شد که این اصلا جالب نیست.

خوشبختانه، HTTP یک راهکار برای این مشکل ارائه می کند. تعدادی از HTTP header ها برای راهنمایی کردن cache های بالا دست جهت متمایز کردن محتویات cache بسته به متغیرهای تعیین شده وجود دارند. و برای گفتن مکانیسم های cache که نباید صفحات خاصی را cache کنند. به برخی از این header ها در بخش های بعدی خواهیم پرداخت.

Using Vary Headers

(در دست ترجمه ...). برای مثال، در صورتی که محتویات یک صفحه ی به زبان مورد ترجیح کاربر وابستگی داشته باشد، صفحه "vary on language" گفته می شود.

به طور پیشفرض، سیستم cache جنگو کلیدهای cache خود را با استفاده از مسیر درخواست شده (مانند "/stories/2005/jun/23/bank_robbed/") ایجاد می کنید. این یعنی هر درخواست به آن URL از یک نسخه cache همسان استفاده خواهد کرد، بدون در نظر گرفتن تفاوت های user-agent از قبیل کوکی ها یا تنظیمات زبان. هر چند، اگر این صفحه محتویات متفاوتی بر اساس آن تفاوت در header های درخواست تولید کند – از قبیل یک کوکی، یا یک زبان، یا یک user-agent – شما نیاز خواهید داشت جهت گفتن مکانیسم های cache که خروجی صفحه به آن چیزها بستگی دارد، از Vary header استفاده کنید.

برای انجام این کار در جنگو، از decorator برای view با نام vary_on_headers مانند زیر استفاده کنید:

from django.views.decorators.vary import vary_on_headers # Python 2.3 syntax. def my_view(request): # ... my_view = vary_on_headers(my_view, 'User-Agent') # Python 2.4 decorator syntax. @vary_on_headers('User-Agent') def my_view(request): # ...

در این مورد، یک مکانیسم cache (مانند cache middleware خود جنگو) یک نسخه ی جدا از صفحه را برای هر user-agent منحصر به فرد cache خواهد کرد.

مزیت استفاده از vary_on_headers به جای دستی قرار دادن Vary header (با استفاده از چیزی شبیه به response['Vary'] = 'user-agent') این است که decorator به Vary header اضافه می کند (که ممکن وجود داشته باشد)، به جای (در دست ترجمه ...) و به طور بالقوه هر چیزی که در آن جا وجود داشته باشد را override می کند.

می توان چندین header را به vary_on_headers() ارسال کرد:

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

این به cache های بالا دست تغییر کردن هر دو را می گوید، که یعنی ترکیب user-agent و cookie مقدار cache خود را بدست خواهند آورد. برای مثال، یک درخواست با user-agent ای مانند Mozilla و مقدار کوکی foo=bar از یک درخواست با user-agent ای با نام Mozilla و مقدار کوکی foo=ham متفاوت در نظر گرفته خواهند شد.

به این دلیل که vary در کوکی بسیار رایج می باشد، یک decorator با نام vary_on_cookie وجود دارد. این دو view برابر می باشند:

@vary_on_cookie def my_view(request): # ... @vary_on_headers('Cookie') def my_view(request): # ...

header هایی که به vary_on_header ارسال می شوند به حروف بزرگ و کوچک حساس نیستند؛ "User‑gent" هیچ فرقی با "user-agent" نخواهد داشت.

همچنین می توان از یک تابع کمکی با نام django.utils.cache.patch_var_headers به طور مستقیم استفاده کرد. این تابع Vary header را قرار داده یا اضافه می کند. برای مثال:

from django.utils.cache import patch_vary_headers def my_view(request): # ... response = render_to_response('template_name', context) patch_vary_headers(response, ['Cookie']) return response

patch_vary_headers یک نمون ی HttpResponse به صورت اولین آرگومان و یک لیست/تاپل از نام های header حساس به حروف بزرگ و کوچک به عنوان آرگومان دوم دریافت می کند.

کنترل Cache: با استفاده از Header ها

مشکلات دیگر cache حریم شخصی داده و سوال از جایی که داده باید در یک آبشاری از cache ها در آن ذخیره شده باشد.

یک کاربر معمولا با دو نوع از cache ها رو به رو می باشد: cache مرورگر خود کاربر (cache خصوصی) و ارائه دهنده ی cache کاربر (یک cache عمومی). cache عمومی توسط چندین کاربر و استفاده می شود و توسط برخی دیگر کنترل می شود. (در دست ترجمه ...). بنابراین برنامه های وب نیاز به یک روش برای گفتن cache ها دارند که کدام داده خصوصی و بوده و کدام عمومی می باشد.

راهکار، نشان دادن cache صفحه باید "خصوصی" باشد. برای انجام این کار در جنگو، از decorator مورد نظر برای view با نام cache_control استفاه کنید:

from django.views.decorators.cache import cache_control @cache_control(private=True) def my_view(request): # ...

این decorator مراقب فرستادن HTTP header مناسب در پشت صحنه می باشد.

چند روش دیگر برای کنترل پارامترهای cache وجود دارد. برای مثال، HTTP به برنامه ها اجازه ی انجام کارهای زیر را می دهد:

در جنگو، از decorator، cache_control برای تعیین این پارامترهای cache استفاده کنید. در این مثال، cache_control جهت دوباره معتبر ساختن cache در هر دسترسی و جهت ذخیره ی نسخه های cache برای حداکثر 3600 ثانیه به cache ها می گوید:

from django.views.decorators.cache import cache_control @cache_control(must_revalidate=True, max_age=3600) def my_view(request): # ...

هر رهنمود HTTP کنترل cache معتبری در cache_control() معتبر می باشد. در زیر لیست کامل وجود دارد:

(توجه داشته باشید که caching middleware پیش از این cache هدر max-age را با مقدار تنظیم CACHE_MIDDLEWARE_SETTINGS قرار داده شده است. در صورتی که از یک max_age سفارشی در یک decorator، cache_control استفاده می کنید، decorator اولیت خواهد گرفت، و مقادیر header به درستی ادغام خواهند شد.)

در صورتی که می خواهید جهت غیر فعال کردن الگوریتم cache کردن از هدرها استفاده کنید، django.view.decorators.cache.never_cache یک decorator برای view می باشد که جهت اطمینان از پاسخی که توسط مرورگر یا دیگر cache ها cache نخواهد شد هدرها را اضافه می کند. مثال:

from django.views.decorators.cache import never_cache @never_cache def myview(request): # ...

بهینه سازی های دیگر

جنگو چند قسمت دیگر از middleware را که می تواند کارایی app های شما را بهینه کند ارائه می کند.

ترتیب MIDDLEWARE_CLASSES

در صورتی که از cashing middleware استفاده می کنید، قرار دادن هر نیمه در جای راست داخل تنظیم MIDDLEWARE_CLASSES اهمیت دارد. چرا که cache middleware نیاز دارد بداند هدرها توسط کدام ذخیره سازی vary cache می شود. middleware همواره هنگامی که بتواند چیزی را به پاسخ هدر Vary اضافه می کند.

UpdateCacheMiddleware فاز پاسخ را اجرا می کند، جایی که middleware به طور برعکس می باشد، بنابراین یک آیتم در بالای لیست آخرین فاز پاسخ را اجرا می کند. در نتیجه، نیاز می باشد اطمینان حاصل کنید که UpdateCacheMiddleware قبل از هر middleware دیگر ظاهر می شود که ممکن است چیزی را به هدر Vary اضافه کند. ماژول های middleware این کار را انجام می دهند:

FetchFromCacheMiddleware، از سوی دیگر فاز درخواست را اجرا می کند، جایی که middleware به صورت اول به آخر بکار برده شده است، بنابراین یک آیتم در بالای لیست اولی فاز درخواست را اجرا می کند. همچنین FetchFromCacheMiddleware لازم است بعد از به روز رسانی های هدر Vary، middleware دیگر اجرا شود، بنابراین FetchFromCacheMiddleware باید بعد از هر آیتمی باشد که این کار را انجام می دهد.