عنوان:

‫بهینه‌سازی کارایی رندرینگ سمت سرور در Blazor 11x با مولفه CacheView


نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۵/۲۶ ۱۱:۵۰
آدرس: www.dntips.ir
چکیده: رندرینگ سمت سرور ثابت (Static Server-Side Rendering یا SSR) در چارچوب کاری Blazor به توسعه‌دهندگان اجازه می‌دهد تا صفحات وب را با سرعت بارگذاری اولیه بالا و بهینه‌سازی موتورهای جستجو (SEO) تولید کنند. با این حال، رندر مجدد زیردرخت‌های کامپوننتی سنگین در هر درخواست HTTP، می‌تواند فشار پردازشی (CPU Overhead) و نرخ تخصیص حافظه (Memory Allocation) را به شدت افزایش دهد. مایکروسافت در به‌روزرسانی‌های جدید اکوسیستم دات‌نت (از پیش‌نمایش‌های NET 10 / 11.)، قابلیت جدیدی تحت عنوان کامپوننت CacheView معرفی کرده است. این کامپوننت با ذخیره‌سازی خروجی HTML تولیدشده از بخش‌های سنگین صفحه، در درخواست‌های بعدی از نمونه‌سازی (Instantiation) و اجرای مجدد چرخه حیات کامپوننت‌ها جلوگیری می‌کند. این مقاله به بررسی معماری CacheView، استراتژی‌های انقضا، ابعاد تفکیک کش (Vary-By Dimensions)، یکپارچگی با لایه کش توزیع‌شده HybridCache و مکانیزم‌های حفظ امنیت داده از طریق اتریبیوت‌های رفتاری کش (CacheBehavior) می‌پردازد.

۱. مقدمه
در معماری Blazor SSR، بخش عمده‌ای از منابع سرور صرف تبدیل گراف کامپوننت‌ها به ساختار DOM و در نهایت تولید رشته‌های HTML می‌شود. بخش‌هایی از صفحات - نظیر کاتالوگ محصولات، جداول آماری، سربرگ‌ها و پاورقی‌ها - ماهیتی ایستا یا نیمه‌ایستا دارند و داده‌های آن‌ها در فواصل زمانی کوتاه تغییر نمی‌کند. اجرای مکرر پرس‌وجوهای پایگاه‌داده و پردازش مجدد منطق رندرینگ برای این اجزا، کارایی سرور را در مقیاس بالا (High-Throughput Scenarios) کاهش می‌دهد.
تا پیش از این، توسعه‌دهندگان ناچار به پیاده‌سازی کش دستی در لایه سرویس (Service Layer Caching) از طریق IMemoryCache یا IDistributedCache بودند؛ رویکردی که اگرچه فراخوانی‌های پایگاه داده را حذف می‌کرد، اما هزینه رندرینگ و تولید HTML کامپوننت در سمت سرور همچنان پابرجا می‌ماند. کامپوننت این خلاء را با کش کردن مستقیم خروجی رندر شده (Rendered HTML Fragment) پر می‌کند.

۲. ساختار و نحوه عملکردCacheView
کامپوننت کش‌ویوو، زیردرخت (Subtree) مدنظر را در بر می‌گیرد. در نخستین درخواست (Cache Miss)، کامپوننت‌های فرزند طبق روال معمول رندر شده و رشته نهایی HTML آن‌ها در کش ذخیره می‌شود. در درخواست‌های بعدی (Cache Hit):
  • کامپوننت‌های فرزند اصلاً نمونه‌سازی (Instantiate) نمی‌شوند.
  • رویدادهای چرخه حیات (مانند OnInitializedAsync یا OnParametersSetAsync) فراخوانی نمی‌گردند.
  • خروجی HTML از پیش ضبط‌شده مستقیماً بازپخش (Replay) می‌شود.

نمونه کد ساده:
@using Microsoft.AspNetCore.Components

<CacheView ExpiresAfter="TimeSpan.FromMinutes(10)"
           VaryByRoute="productId"
           VaryByQuery="page,pageSize"
           VaryByCulture="true">
    <ExpensiveProductSummary ProductId="productId" />
</CacheView>

۳. سیاست‌های انقضا و ابعاد تفکیک کش (Invalidation & Vary-By)
۳.۱. سیاست‌های انقضا (Expiration Policies)
CacheView از مدل‌های استاندارد انقضا پشتیبانی می‌کند:
  • انقضای نسبی مطلقه (ExpiresAfter): تعیین طول عمر مشخص بر حسب بازه زمانی (مانند TimeSpan.FromMinutes(10)).
  • انقضای زمانی معین (ExpiresOn): تعیین زمان دقیق انقضا با DateTimeOffset.
  • انقضای لغزان (ExpiresSliding): تمدید اعتبار کش در صورت ارسال درخواست‌های مکرر در بازه تعیین‌شده.

۳.۲. ابعاد تفکیک کش (Vary-By Dimensions)
برای جلوگیری از تداخل داده‌های کاربران یا نمایش اطلاعات نامربوط، ابعاد متعددی برای تفکیک کلید کش تعبیه شده است:
  • VaryByRoute: تفکیک بر اساس پارامترهای مسیر URL (مثلاً شناسه رکورد یا اسلاگ).
  • VaryByQuery: تفکیک بر اساس پارامترهای Query String (مانند فیلترها و شماره صفحه).
  • VaryByCulture: تفکیک بر اساس Culture فعلی برنامه برای پشتیبانی از سیستم‌های چندزبانه.
  • VaryByUser: تفکیک بر اساس کاربر احراز هویت شده (ClaimsPrincipal).
  • VaryByHeader / VaryByCookie: تفکیک بر اساس مقادیر هدر یا کوکی‌های مشخص.
  • VaryBy: پذیرش یک رشته سفارشی دلخواه برای سناریوهای خاص بیزینس.
  • CacheKey: ایجاد تمایز میان چندین مرز CacheView تحت یک کامپوننت والد یکسان.

نکته امنیتی و فنی: کش کردن به صورت خودکار برای متدهای غیر از GET (مانند POST)، در زمان تنظیم Enabled="false"، و در حین فرآیند رندر جریانی (Streaming SSR) متوقف می‌شود تا از ذخیره‌سازی داده‌های موقت یا ناقص جلوگیری گردد.

۴. لایه ذخیره‌سازی و یکپارچگی باHybridCache
به طور پیش‌فرض، CacheView از یک مخزن حافظه رم درون‌برنامه‌ای (In-Memory) با سقف حجم مشخص (به طور پیش‌فرض 100 مگابایت قابل تنظیم از طریق RazorComponentsServiceOptions.CacheViewSizeLimit) استفاده می‌کند. در سامانه‌های توزیع‌شده و چندسروری (Load-Balanced Clusters)، ذخیره‌سازی صرفاً در حافظه محلی منجر به عدم هماهنگی داده‌ها می‌شود. CacheView به طور بومی با انتزاع قدرتمند HybridCache هماهنگ است. با ثبت HybridCache در کانتینر تزریق وابستگی (DI)، کامپوننت CacheView به شکل خودکار به یک سیستم کش دو لایه‌ای (Two-Tier) مجهز می‌شود؛ به طوری که لایه اول L1 (حافظه محلی درون پروسس) و لایه دوم L2 (ردیس، اس‌کیوال یا سایر پایگاه‌های داده توزیع‌شده) را بدون تغییر کد ویو پوشش می‌دهد.
// Program.cs
builder.Services.AddHybridCache();

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

۵. امنیت داده‌های پویا و مکانیزم سوراخ‌کاری کش (Cache Punching)
بزرگترین چالش در کش کردن بخش‌های رابط کاربری، نشت اطلاعات حساس یا از کار افتادن رفتارهای پویا در کامپوننت‌های وابسته به نشست کاربر (Per-Request State) است. برای رفع این چالش، ویژگی‌های متادیتایی جدیدی معرفی شده‌اند:
۵.۱. اتریبیوت‌های رفتاری
[CacheBehavior(CacheBehavior.Rerender)] (مکانیزم سوراخ‌کاری / Hole-punching):
  • کامپوننت حاوی این ویژگی، حتی اگر درون یک CacheView با وضعیت Hit قرار داشته باشد، در هر درخواست مجدداً رندر می‌شود و خروجی جدید آن در ساختار کش تزریق می‌گردد.
  • نمونه‌های پیش‌فرض دات‌نت:AntiforgeryToken (جهت حفظ امنیت توکن‌های ضد جعل) و HeadOutlet (تغییر پویای متاتگ‌های سربرگ).

[CacheBehavior(CacheBehavior.Throw)]:
  • در صورتی که کامپوننت درون CacheView استفاده شود، سرور خطای سیستمی پرتاب می‌کند تا از انتشار اشتباه داده جلوگیری شود.

[CacheCondition(CacheVaryBy.…)]:
  • مکمل ویژگی Throw است و مشخص می‌کند در چه صورتی کش کردن این کامپوننت مجاز است.

۵.۲. رفتار کامپوننت‌های داخلی چارچوب کاری
نام کامپوننتانوتیشن (Annotation)رفتار و شرایط لازم
AntiforgeryToken / HeadOutletRerenderهمیشه در هر درخواست مجدداً رندر می‌شوند (بدون کش).
AuthorizeViewThrow + CacheCondition(CacheVaryBy.User)قرارگیری درون کش غیرمجاز است، مگر اینکه VaryByUser="true" باشد.
QuickGridThrow + CacheCondition(CacheVaryBy.Query)وابسته به پارامترهای صفحه‌بندی/مرتب‌سازی؛ نیازمند تعریف VaryByQuery.
VirtualizeThrowبه طور مطلق درون CacheView پشتیبانی نمی‌شود.
در صورت نقض این شروط، دات‌نت یک استثنای صریح همراه با راهکار رفع آن بازمی‌گرداند:
System.InvalidOperationException: Component 'QuickGrid`1[...]' cannot be used inside a CacheView 
because its output depends on per-request state ([CacheBehavior(CacheBehavior.Throw)], 
[CacheCondition(CacheVaryBy.Query)]) that cannot be safely cached and replayed. To fix this, 
configure the CacheView to vary by Query, or move the component outside the CacheView.

۶. نتیجه‌گیری
معرفی کامپوننت کش‌ویوو، یک گام تکاملی بزرگ در بهینه‌سازی رندرینگ سمت سرور چارچوب کاری Blazor به شمار می‌رود. این قابلیت با ارائه سازوکاری ساده اما در عین حال انعطاف‌پذیر و ایمن، هزینه مصرف پردازنده و تخصیص حافظه را در سرور به حداقل می‌رساند. یکپارچگی شفاف با HybridCache و حفاظت پیش‌گیرانه در برابر نشت داده به کمک [CacheBehavior]، ابزاری مطمئن و با کارایی بالا در اختیار توسعه‌دهندگان ارشد دات‌نت برای ساخت وب‌سایت‌های پرترافیک و بهینه‌سازی هزینه‌های زیرساخت ابری قرار می‌دهد.