هدر پاسخ Vary به عنوان «زشتی بخش HTTP که هنوز آن را بهبود ندادهایم» توصیف شده است. همین پست آن را مکانیزمی «وحشتناک و پینهدوزیشده» با «تعاملپذیری بسیار افتضاح» در میان واسطهها توصیف میکند. معمولاً اینجاست که مهندسان عاقل با دستان بالا رفته به آرامی عقبنشینی میکنند.
این تعریف دقیقاْ تأییدی بر هدر Vary نیست، اما زشت بودن به معنای بیفایده بودن نیست.
یک URL میتواند بیش از یک پاسخ صحیح داشته باشد. برای مثال، یک سرور ممکن است فرمتهای تصویری متفاوتی را به مرورگرهای مختلف ارائه دهد. اگر یک کش هدر Vary را نادیده بگیرد، خطر ارائه بایتهای اشتباه به یک درخواست وجود دارد. اما اگر هر مقدار هدر خام را به عنوان یک مقدار متمایز در نظر بگیرد، تعداد انگشتشماری از درخواستهای مشابه میتوانند به هزاران ورودی کش که به سختی قابل استفاده مجدد هستند، تبدیل شوند. هدر Vary به کش میگوید که کدام فیلدهای درخواست ممکن است روی پاسخ تأثیر بگذارند، اما به کش نمیگوید که چه تفاوتهایی واقعاً اهمیت دارند.
پشتیبانی از Vary اکنون در بخش قوانین کش (Cache Rules) در تمام پلنها در دسترس است. مبدا (Origin) همچنان هدرهای درخواستی را که ممکن است روی پاسخ تأثیر بگذارند نام میبرد، اما شما تصمیم میگیرید که شرکت کلودفلر (Cloudflare) چگونه با هر یک از آنها برخورد کند. شما میتوانید هدرهای توافقشده شناختهشده را نرمالسازی کنید، مقادیر دقیق را در صورت اهمیت آن تفاوتهای کوچک عبور دهید، یا زمانی که تغییرات غیرقابل پیشبینی است، کش را دور بزنید. مبدا اعلام میکند چه چیزی ممکن است تغییر کند و شما تصمیم میگیرید که چه میزان از تغییرات برای کش واقعاً معنادار است.
نحوه عملکرد Vary
هدر Vary یک هدر پاسخ استاندارد HTTP است که به کشهای واسطه (مانند کلودفلر (Cloudflare)) میگوید کدام فیلدهای درخواست ممکن است روی پاسخ ارسالشده توسط مبدا تأثیر بگذارند. سایتها از Vary برای ارائه زبانهای مختلف، فرمتهای تصویر، طرحهای فشردهسازی یا محتوای منطقهای از یک URL استفاده میکنند.
یک URL را در نظر بگیرید که دو نمایش معتبر تولید میکند. یک مرورگر یک وبسایت را درخواست میکند:
مبدا کدهای HTML را برمیگرداند و فیلد Accept را به عنوان فیلدی که ممکن است روی پاسخ تأثیر بگذارد، شناسایی میکند:
یک کلاینت API میتواند همان URL را با ترجیحی متفاوت درخواست کند:
این بار، پاسخ صحیح فرمت JSON است. هدر Vary: Accept به کش میگوید که URL به تنهایی برای انتخاب بین پاسخها کافی نیست. مقدار Accept درخواست نیز باید در نظر گرفته شود.
بدون هدر Vary، هر پاسخی که ابتدا وارد کش شود ممکن است به هر دو کلاینت ارائه شود. اگر HTML برنده شود، کلاینت API نشانهگذاری (Markup) را دریافت میکند و تجزیهکننده JSON آن با خطا مواجه میشود. اگر JSON برنده شود، مرورگری که انتظار یک صفحه وب را دارد، پاسخ API را دریافت میکند.
هدر Vary از ارائه پاسخ اشتباه توسط کش به کلاینت درخواستکننده جلوگیری میکند. اما سوال دشوارتری را مطرح میکند: چه زمانی دو درخواست حاوی مقادیر هدر متفاوتی هستند، آیا آنها واقعاً نیاز به پاسخهای متفاوتی دارند؟
چه زمانی کشکردن صحیح بیفایده میشود
هدر Vary میتواند به کش بگوید کدام فیلدهای درخواست ممکن است روی پاسخ تأثیر بگذارند. این هدر به کش نمیگوید که پاسخ نمایانگر چیست. برای مثال، مبدایی را در نظر بگیرید که محتوا را فقط به زبانهای انگلیسی، فرانسوی و آلمانی ارائه میدهد. ممکن است یک کلاینت ارسال کند:
در حالی که ممکن است کلاینت دیگری درخواست کند:
در اینجا هر دو درخواست زبان انگلیسی را ترجیح میدهند. پاسخ مبدا ممکن است هر دو درخواست را دقیقاْ به یک پاسخ انگلیسی یکسان نگاشت کند. اما کشی که مقادیر خام را مقایسه میکند نمیتواند با اطمینان فرض کند که آنها معادل هستند. آنها ترتیبها و برچسبهای زبانی متفاوتی دارند (که مبدا تفاوتی بین آنها قائل نمیشود). بنابراین کش ممکن است آنها را به عنوان انواع جداگانه ذخیره کند، حتی زمانی که بدنه پاسخ آنها حاوی بایتهای یکسانی باشد.
این مشکل اساسی هدر Vary است. برنامهها اغلب مجموعه کوچک و محدودی از نمایشها را از مجموعه عظیمی از مقادیر ممکن درخواست تولید میکنند. مبدا میفهمد که هزاران ترجیح زبانی در سه زبان پشتیبانیشده ادغام میشوند، در حالی که کش معمولاً چنین چیزی را نمیداند.
این مشکل زمانی تشدید میشود که یک پاسخ در چندین فیلد تغییر کند. ده مقدار ممکن در یک فیلد، ده نوع (Variant) ایجاد میکنند. ده مقدار در سه فیلد میتواند ۱۰۰۰ ترکیب ایجاد کند. هدرهای واقعی میتوانند کاردینالیتی (Cardinality) بسیار بیشتری داشته باشند: مقادیر User-Agent بسیار زیاد هستند، کوکیها میتوانند برای بازدیدکنندگان فردی منحصر به فرد باشند، و هدرهای ترجیحی میتوانند در ترتیب، قالبگذاری (فواصل و تبها اهمیت دارند!) و مقادیر کیفیت متفاوت باشند.
نتیجه کشی است که میتواند کاملاً درست باشد اما تقریباً به طور دائم سرد بماند (یک ورودی هرگز دوباره استفاده نمیشود). پاسخهای یکسان میتوانند در سراسر ورودیهایی پخش شوند که ترافیک بسیار کمی برای گرم ماندن و ماندن در کش دریافت میکنند. آنها میتوانند ظرفیت را مصرف کنند، یکدیگر را بیرون برسانند (Evict)، نسبتهای بازدید کش را کاهش دهند و درخواستهای بیشتری را به سرورهای مبدا ارسال کنند. تخلیه (Eviction) میتواند ورودیهای سرد را حذف کند، اما نمیتواند آنها را فقط به این دلیل که پاسخها یکسان هستند، ادغام کند.
یک تحلیل روی بیش از ۱۲۰۰۰۰۰۰۰ پاسخ از تقریباْ ۵۰۰۰۰ سایت محبوب نشان داد که تقریباً ۳۰۰۰ سایت روی چهار فیلد یا بیشتر تغییرات دارند. برخی روی ۱۰، ۲۳ یا حتی ۴۷ فیلد تغییر ایجاد میکردند. ما میخواهیم مطمئن شویم مشتریان ابزارهای لازم را برای استفاده از Vary در مواقع مناسب در اختیار دارند، اما نه به قدری زیاد که یک کش بیفایده ایجاد کنند.
برخی تغییرات با کاردینالیتی بالا عمدی هستند. شبکه توزیع محتوا (CDN) یا پروکسیهای معکوس ممکن است مقادیری مانند یک منطقه جغرافیایی را برای پارتیشنبندی قابل پیشبینی محتوا تزریق کنند. این کار زمانی جواب میدهد که مقادیر ممکن کنترل شوند و هر مؤلفه روی معنای آنها توافق داشته باشد. بدون آن محدودیتها، کش به انواعی تکه تکه میشود که ممکن است هرگز دوباره از آنها استفاده نکند.
این همان مسئله طراحی بود که برای پشتیبانی از Vary باید حل میکردیم. ما باید تغییرات کافی را حفظ میکردیم تا پاسخ مناسب ارائه شود، بدون اینکه اجازه دهیم تفاوتهای تصادفی بین درخواستها کارایی کش را از بین ببرند.
چگونگی کنترل Vary در قوانین کش (Cache Rules)
مشتریان کلودفلر (Cloudflare) از قبل راههای متعددی برای مدیریت محتوای توافقشده مشابه با Vary داشتند. آنها میتوانستند کش را دور بزنند و اجازه دهند مبدا آنها با آن مقابله کند، منطق مذاکره مبدا را در یک کلید کش سفارشی یا قانون دیگر بازتولید کنند، از یک Worker استفاده کنند، یا از ویژگیهایی مانند Vary برای تصاویر استفاده کنند.
این گزینهها همچنان مفید هستند، اما یا از کش کردن صرفنظر میکنند، یا منطق برنامه را تکرار میکنند، یا نیاز به نوشتن کد اضافی دارند، یا به یک مورد استفاده محدودتر میپردازند. استفاده از Vary در قوانین کش (Vary in Cache Rules) ممکن است با تقسیم پشتیبانی به دو تصمیم، شکاف بین این ویژگیهای موجود را پر کند:
- مبدا از Vary برای شناسایی هدرهای درخواستی که ممکن است روی پاسخ تأثیر بگذارند، استفاده میکند.
- قانون کش (Cache Rule) تعیین میکند که کلودفلر (Cloudflare) چگونه مقدار هر هدر را مدیریت میکند.
یک قانون کش (Cache Rule) هر پاسخی را مجبور به تغییر (vary) نمیکند. اگر مبدا هدر Vary را برنگرداند، کلودفلر پاسخ را به طور عادی کش میکند، اگرچه این قانون ممکن است همچنان Accept و Accept-Language را قبل از ارسال درخواست به مبدا بازنویسی کند.
هنگامی که مبدا Vary را برمیگرداند، کلودفلر از اکشن پیکربندیشده برای هر هدر که نام میبرد استفاده میکند. هدرهای بدون تنظیمات فردی از اکشن پیشفرض قانون استفاده میکنند. سه اکشن موجود عبارتند از:
ما اکشن normalize (نرمالسازی) را به عنوان پیشفرض پیشنهاد میکنیم. برای هدرهای فردی با مقادیر شخصی یا بیکران، از bypass (دور زدن) استفاده کنید. از passthrough (عبور مستقیم) زمانی استفاده کنید که مقدار دقیق پاسخ را تغییر میدهد.
برای مثال، اکشن passthrough تفاوتهای حروف بزرگ و کوچک، فضای خالی، ترتیب و مقادیر تکراری را حفظ میکند، حتی زمانی که مبدا با آنها به عنوان مقادیر معادل رفتار میکند. با هدر Vary: X-View و اکشن passthrough، این سه مقدار کلیدهای کش جداگانهای تولید میکنند:
X-View: compact,full
X-View: Compact,full
X-View: compact, full
تغییرات تصادفی کافی میتواند یک پاسخ قابل استفاده مجدد را به چندین نوع یکباره در کش شما تبدیل کند.
صرف نظر از اکشنهای پیکربندیشده، هدر Vary: * همیشه کش را دور میزند. این بدان معناست که هر جنبهای از درخواست، حتی اطلاعات خارج از پیام HTTP (مانند آدرس IP کلاینت)، ممکن است روی پاسخی که مبدا انتخاب میکند تأثیر بگذارد. بنابراین کلودفلر نمیتواند پاسخ را برای یک درخواست بعدی بدون تماس با مبدا دوباره استفاده کند.
چگونگی حرکت یک پاسخ از طریق کش
بیایید یکی از درخواستهای /catalog را از بالا از طریق کلودفلر (Cloudflare) دنبال کنیم.
در اولین درخواست، کلودفلر هیچ داده ذخیرهشدهای برای Vary برای آن منبع ندارد، بنابراین جستجوی کش با عدم تطابق (Miss) مواجه میشود. قانون کش منطبق میتواند فیلدهای پیکربندیشده را قبل از اینکه کلودفلر با مبدا تماس بگیرد، نرمالسازی کند.
این اتفاق میتواند قبل از آن رخ دهد که کلودفلر بداند آیا پاسخ نهایی حاوی Vary خواهد بود یا خیر. قانون کش نرمالسازی مجاز را تعریف میکند؛ پاسخ بعداً تعیین میکند که آیا آن فیلدها بخشی از نوع کششده میشوند یا خیر.
این ترتیب اهمیت دارد. اگر کلودفلر چندین مقدار خام را تحت یک کلید کش نرمالشده گروهبندی کند، اما مبدا همچنان آن مقادیر خام را دریافت کند، مبدا میتواند پاسخهای متفاوتی تولید کند که کش بعداً آنها را قابل تعویض در نظر بگیرد. ارسال مقدار نرمالشده، انتخاب مبدا را با تطابق کش هماهنگ نگه میدارد.
مبدا با این موارد پاسخ میدهد:
Vary: Accept, Accept-Language
کلودفلر نامهای آن هدر را ثبت میکند و پاسخ را به عنوان یک نوع کششده ذخیره میکند. مقادیر هدر، که مطابق با قانون کش پردازش شدهاند، این نوع را از سایرین برای همان منبع متمایز میکنند.
هنگامی که درخواست دیگری برای /catalog میرسد، کلودفلر با کلید کش پایه منبع شروع میکند: به طور کلی URL به علاوه هر فیلد کلید پیکربندیشده دیگر. سپس فیلدهای ذخیرهشده Vary را میخواند و قانون کش را روی آن هدرها در درخواست جدید اعمال میکند تا نوع کششده منطبق را شناسایی کند.
فرض کنید آنها به این صورت نرمالسازی میشوند:
Accept: text/html
Accept-Language: en,fr
کلودفلر از آن مقادیر برای جستجوی مستقیم نوع کششده منطبق استفاده میکند. این کار درخواست را با هر نوع ذخیرهشده به صورت تک تک مقایسه نمیکند.
اگر یک نوع منطبق وجود داشته باشد و تازه باشد، درخواست یک بازدید کش (Cache Hit) است. اگر نه، کلودفلر درخواست را به مبدا ارسال میکند و ممکن است پاسخ حاصل را به عنوان نوع دیگری ذخیره کند.
پاسخ مبدا چرخه را کامل میکند. برای هر هدر نامبرده شده در Vary، کلودفلر از اکشن پیکربندیشده برای آن هدر، یا اکشن پیشفرض قانون اگر هدر به صورت جداگانه فهرست نشده باشد، استفاده میکند:
- اگر حاوی Vary نباشد، کلودفلر آن را به طور عادی کش میکند.
- اگر هر هدر نامبرده شده به حالت نرمالسازی (normalize) یا عبور مستقیم (passthrough) حل شود، کلودفلر میتواند پاسخ را به عنوان یک نوع کششده ذخیره کند.
- اگر هر فیلد نامبرده شده از اکشن bypass استفاده کند، کلودفلر پاسخ را ذخیره نمیکند.
- اگر پاسخ حاوی Vary: * باشد، کلودفلر آن را ذخیره نمیکند.
این امر مسئولیت مهمی را بر عهده مبدا میگذارد. هر پاسخ قابل کش که میتواند بر اساس فیلدهای درخواست متفاوت باشد، باید هدر مناسب Vary را به طور مداوم برگرداند، از جمله خطاها و پاسخهای جایگزین. اگر یک پاسخ آن را حذف کند، کلودفلر میتواند آن پاسخ را بدون واریانس لازم برای ایوله نگه داشتن آن کش کند.
کلیدهای کش در نمودار مفهومی هستند. درخواست بعدی یک پاسخ کششده تازه را فرض میکند.
هر پاکسازی (Purge) که یک منبع کششده را هدف قرار دهد، تمام انواع Vary آن را پوشش میدهد. الزامات موجود برای پاکسازی کلیدهای کش سفارشی همچنان اعمال میشوند.
تغییر پیکربندی Vary به طور خودکار محتوای موجود را پاک نمیکند. خطمشی جدید ممکن است کلیدهای کش متفاوتی تولید کند: درخواستها میتوانند تحت کلیدهای جدید با عدم تطابق مواجه شده و دوباره پر شوند، در حالی که ورودیهای قدیمی تا زمانی که منقضی شوند یا پاک شوند باقی میمانند.
نرمالسازی درخواستهای معادل را کنار هم نگه میدارد
درخواستهای بالا را که درخواست انگلیسی و فرانسوی داشتند به خاطر دارید؟
Accept-Language: en-US, fr;q=0.8
Accept-Language: fr;q=0.8, en-GB
هر دو درخواست انگلیسی را ترجیح میدهند، اما اکشن passthrough با آنها به عنوان انواعی متفاوت رفتار میکند. اگر قانون کش اجازه en، fr و de را بدهد، اکشن normalize هر دو را به en,fr کاهش میدهد و به آنها اجازه میدهد یک پاسخ کششده را به اشتراک بگذارند.
برای انجام این کار، کلودفلر مقادیر موجود در Accept، Accept-Language و Accept-Encoding را به حروف کوچک تبدیل میکند، سپس آنها را بر اساس مقدار کیفیت، بالاترین مورد ابتدا و با ترتیب الفبایی برای شکستن تساویها مرتب میکند. بنابراین ترتیب کلاینت روی کلید کش تأثیر نمیگذارد. پس از مرتبسازی، کلودفلر پارامترها را از ورودیهای دارای مقدار کیفیت غیرصفر حذف میکند. همچنین میتواند هنگام کوتاه کردن برچسبهای زبانی یا فیلتر کردن به فرمتها و زبانهای پیکربندیشده، مقدار q=0 («غیرقابل قبول») را از دست بدهد. برای مثال، en-US;q=0 میتواند به en تبدیل شود. اگر مبدا نیاز به دیدن این استثناها دارد، از passthrough برای Accept یا Accept-Language استفاده کنید.
همچنین میتوانید قانون را به گونهای پیکربندی کنید که فقط انواع رسانه یا زبانهای مشخصشده را در Accept و Accept-Language نگه دارد. برچسبهای زبانی منطقهای مانند en-US به زبان پایه خود یعنی en کاهش مییابند، مگر اینکه برچسب کامل پیکربندی شده باشد. این به شما امکان میدهد نرمالسازی را با فرمتها و زبانهایی که مبدا شما در واقع ارائه میدهد، هماهنگ کنید.
برای هماهنگ نگه داشتن انتخاب مبدا با تطابق کش، کلودفلر مقادیر نرمالشده Accept و Accept-Language را به مبدا ارسال میکند. همچنین زمانی که قابلیت Respect Strong ETags فعال است، مقادیر نرمالشده Accept-Encoding را ارسال میکند. سایر هدرها فقط برای تطابق کش نرمالسازی میشوند.
پیکربندی Vary در قوانین کش
در داشبورد کلودفلر (Cloudflare)، به بخش Caching > Cache Rules بروید، یک قانون ایجاد یا ویرایش کنید، پاسخ را واجد شرایط کش کنید و تنظیمات Vary را اضافه کنید. رفتار پیشفرض را تنظیم کنید، سپس هدرهایی را که انتظار میرود مبدا شما نام ببرد، اضافه کنید.
همان پیکربندی از طریق Rulesets API در فاز http_request_cache_settings در دسترس است. تنظیمات پیشفرض یک اکشن جایگزین برای هدرهایی که مبدا شما در Vary نام برده و شما به صورت جداگانه پیکربندی نکردهاید، انتخاب میکند.
این مثال Accept و Accept-Language را به مجموعه پیکربندیشدهای از فرمتها و زبانها نرمالسازی میکند. اکشن نرمالسازی پیشفرض همچنین برای سایر هدرهای نامبرده شده در Vary اعمال میشود:
این یک بدنه درخواست کامل برای یک درخواست PUT به نقطه ورود فاز http_request_cache_settings است. یک درخواست PUT هر قانون را در آن نقطه ورود جایگزین میکند. اگر از قبل قوانین کش دارید، آنها را در آرایه قوانین قرار دهید یا از عملیات ایجاد یا بهروزرسانی تکقانون مناسب استفاده کنید.
اگر مبدا یک نمایش برای هر جفت نوع رسانه و زبان ارائه دهد، شش ترکیب محتوا وجود دارد. این کار کش را به شش کلید محدود نمیکند. ترتیب ترجیح، هدرهای گمشده و مقادیری که به حالت خالی نرمالسازی میشوند میتوانند موارد بیشتری ایجاد کنند. مجموعه پشتیبانیشده را کوچک نگه دارید و مرزهای قانون را به طور واضح تعریف کنید. پس از راهاندازی، همان URL را با مقادیر هدر مختلف که باید به همان نوع کششده نرمالسازی شوند، تست کنید. درخواستهای تست را از همان کلاینت ارسال کنید، تأیید کنید که فرمت و زبان مورد انتظار را برمیگردانند، و CF-Cache-Status را بررسی کنید. به محض پر شدن کش به دنبال بازدیدها باشید و پاسخهای عدم تطابق مداوم یا پاسخهای دور زدن غیرمنتظره را بررسی کنید.
برای محدودیتها، مثالهای اضافی و نحوه تنظیم این مورد در Terraform، به مستندات Vary مراجعه کنید.
چرا از کلید کش سفارشی استفاده نکنیم؟
در این مرحله، یک سوال آشکار این است: «چرا Accept و Accept-Language را به یک کلید کش سفارشی اضافه نکنیم؟»
این کار زمانی جواب میدهد که آن فیلدها همیشه بخشی از هویت منبع باشند. اما یک کلید کش سفارشی ابعاد پیکربندیشده را به هر پاسخی که تحت پوشش قانون است اضافه میکند، چه مبدا از آنها استفاده کرده باشد چه نکرده باشد.
هدر Vary مبتنی بر پاسخ است، اما پاسخهای قابل کش تحت همان کلید پایه به مجموعه ثابتی از فیلدهای Vary نیاز دارند.
زمانی که یک ویژگی درخواست همیشه منبع را تعریف میکند، از یک کلید کش سفارشی استفاده کنید. زمانی که مبدا همان مجموعه فیلدهای درخواست را در پاسخهای قابل کش اعلام میکند، از Vary استفاده کنید. از قرار دادن همان هدر در هر دو خودداری کنید مگر اینکه تکرار عمدی بوده و تست شده باشد.
همین امروز از Vary در قوانین کش استفاده کنید!
هدر Vary به حل یک مشکل آشکار کمک میکند: یک URL میتواند بیشاز یک پاسخ صحیح داشته باشد. اما این هدر یک مشکل دشوارتر را به کش تحویل میدهد، کدام تفاوتهای درخواست واقعاً اهمیت دارند؟ مبدا میداند چه پاسخهایی را میتواند ارائه دهد. کش باید بداند کدام درخواستها میتوانند از هر پاسخ دوباره استفاده کنند.
استفاده از Vary در قوانین کش این دو دیدگاه را به هم متصل میکند. مبدا فیلدهای درخواستی را که ممکن است روی پاسخ تأثیر بگذارند شناسایی میکند. شما تصمیم میگیرید که مقادیر را نرمالسازی (normalize) کنید، از عبور مستقیم (passthrough) برای تفاوتهای دقیق استفاده کنید، یا پاسخ را خارج از کش نگه دارید.
هدر Vary هرگز آنقدر زشت نبوده که بیفایده باشد. اما پیکربندی دستی فرمتها و زبانهای پشتیبانیشده ممکن است مناسب همه نباشد.
منبع: blog.cloudflare.com
