استقرارهای توزیعشده
سرو یک سند از چندین گره
زمانی که چندین گره همان اسناد را سرو میکنند، گرهای که یک سند را رندر میکند لزوماً گرهای نیست که درخواستهای صفحه مرورگر را پاسخ میدهد. این نسخه این کار را به دو نیمه واضح تقسیم میکند — یک write side که آثار یک سند را در ذخیرهسازی مشترک منتشر میکند، و یک read side که آنها را از هر گرهای سرو میکند.
اگر برنامهٔ شما در حال حاضر UseDoconutWebFarm(...) را فراخوانی میکند، این صفحه جایگزین شماست. نه UseDoconutWebFarm و نه WebFarmOptions در این نسخه وجود ندارند.
توپولوژی شما تغییر نمیکند. ریشهٔ مشترک را به همان مسیر UNC که امروز استفاده میکنید اشاره کنید و هر گره همانطور که اکنون کار میکند ادامه میدهد. چیزی که تغییر میکند این است که انتشار یک عملیات است که شما فراخوانی میکنید، نه اثر جانبی رندر کردن.
چه چیزی جایگزین میدلور وبفارم شد
قبلاً یک میدلور همه کارها را انجام میداد: تصاویر پیشرندر شده را از یک پوشهٔ مشترک سرو میکرد و در حالی که گرهٔ دیگری هنوز آنها را مینوشت پول میکرد. انتظار یک بودجهٔ کور بود — «تا سه دقیقه مسدود بمانید به امید ظاهر شدن صفحه».
اکنون همان کار به دو فراخوانی تبدیل شده است که میتوانید ببینید و دربارهٔ آن استدلال کنید:
node A OpenDocument request
-> PublishAsync(...) writes pages, document info,
search index and metadata
into RootPath/{token}/
-> returns the token
node B ?token=...&page=7
-> UseDoconutCloud<FileShareCloudHandler>
-> reads RootPath/{token}/ and serves the pageپیکربندی مشترک که هر دو نیمه به آن نیاز دارند
ناشر با یک FileShareConfig مینویسد و هندلر خواندن با یکی سرو میکند. اگر RootPath یا UsePerTokenSubfolders بین آنها متفاوت باشند، گرهٔ دیگر 404 برمیگرداند. پیکربندی را دقیقاً در یک مکان بسازید و از هر دو طرف فراخوانی کنید — برنامهٔ مرجع یک کمککنندهٔ واحد SharedStore.Config(...) برای این دلیل نگه میدارد:
internal static class SharedStore
{
public static FileShareConfig Config(string rootPath, bool asyncPublish) => new()
{
Location = CloudLocation.FileShare,
RootPath = rootPath, // مثلاً \\fileserver\doconut
UsePerTokenSubfolders = true,
ReadReadyPollMs = asyncPublish ? 250 : 0,
};
}FileShareConfig در Doconut.Clouds قرار دارد، همراه با AzureConfig، AmazonConfig، GoogleCloudConfig، DropBoxConfig، RedisConfig، CDNConfig و FTPConfig. در کتابخانهٔ قبلی اینها در Doconut.Configs.Cloud بودند؛ نام کلاسها تغییر نکردهاند.
| ویژگی | هدف |
|---|---|
Location | CloudLocation.FileShare برای یک اشتراک UNC/محلی |
RootPath | پوشهٔ مشترکی که هر گره میتواند به آن دسترسی داشته باشد |
UsePerTokenSubfolders | نگه داشتن آثار هر سند در پوشهٔ خود {token} |
ReadReadyPollMs | مدت زمانی که read side برای صفحهای که هنوز نوشته میشود صبر میکند؛ 0 نظرسنجی را غیرفعال میکند |
سمت نوشتن — انتشار سند
DistributedDocumentPublisher را تزریق کنید و به جای باز کردن یک جلسهٔ محلی منتشر کنید. یک فراخوانی تمام مجموعهٔ آثار را رندر و مینویسد — صفحات، اطلاعات سند، فهرست جستجو و متادیتا:
var shareConfig = SharedStore.Config(rootPath, asyncPublish: false);
var documentOptions = new DocOptions { TimeOut = 60 };
var config = new PdfConfig { AllowSearch = true };
var publishOptions = new DistributedDocumentPublishOptions
{
CleanUpStagingDirectory = true,
FirstPagePriority = true,
};
DistributedDocumentPublishResult result = await _publisher.PublishAsync(
path, shareConfig, config, documentOptions, publishOptions, ct);
return Content(result.Token);DistributedDocumentPublishResult شامل Token، ArtifactPath، TotalPages و Format است. توکن دقیقاً همانطور که یک توکن نمایشگر عادی عمل میکند به مرورگر میرود.
این فراخوانی واحد جایگزین جفت قبلی میشود — باز کردن سند در یک جلسهٔ محلی و سپس ریختن تصاویر بر روی اشتراک با ExportToPng(sharedPath). ExportToPng و ExportToCloud در اینجا معادل ندارند.
سمت خواندن — سرو از اشتراک
دستگیره FileShare را بر روی همان شاخهای که کلاینت قبلاً فراخوانی میکند، سوار کنید:
app.UseDoconutResources();
app.MapWhen(
ctx => ctx.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
branch => branch.UseDoconutCloud<FileShareCloudHandler>(options =>
{
options.Location = CloudLocation.FileShare;
options.CloudUploadConfig = SharedStore.Config(rootPath, asyncPublish: false);
}, "/"));UseDoconutCloud در این نسخه عمومی است: ارائهدهنده بهجای مقدار Location که داخل میدلور سوئیچ میشود، آرگومان نوع است. دستگیرهها با نامهای <Provider>CloudHandler — FileShareCloudHandler، AzureCloudHandler، AmazonS3CloudHandler، GoogleCloudHandler، DropBoxCloudHandler، RedisCloudHandler، FTPCloudHandler و CDNCloudHandler نامگذاری شدهاند.
دستگیره CDN فقط‑خواندنی است — آنچه در CDN موجود است را سرو میکند؛ آپلود نمیکند.
اولویت صفحهٔ اول و HTTP 202
FirstPagePriority = true صفحهٔ ۱ را بلافاصله قابل سرو میکند. صفحات بعدی تا زمان نوشتن، پاسخ HTTP 202 Accepted میدهند، بهجای مسدود کردن درخواست.
این انتظار را به سمت کلاینت منتقل میکند، جایی که محدود و قابل مشاهده است. بازآزمایی را روی ویجت فعال کنید:
const objViewer = $('#div_ctlDoc').docViewer({
BasePath: '/',
ResPath: 'doconut-res/images',
retryOn409: true // تلاشهای مجدد 202 (و 409 قدیمی) پاسخهای آمادگی
});کلاینتی که بازآزمایی نمیکند، برای صفحاتی که هنوز در حال رندر هستند، کاشیهای خراب نشان میدهد. بودجهٔ بازآزمایی و مقادیر پیشفرض آن (retryInitialDelayMs، retryBackoffFactor، retryMaxDelayMs، retryMaxAttempts، retryMaxTotalMs) در پیکربندینمایشگر مستند شدهاند.
این جفت — FirstPagePriority بهاضافه نظرسنجی سمتخواندن — جایگزین تنظیمات قبلی PageWaitTimeSeconds و StartWaitFromPage میشود. هیچیک دیگر وجود ندارند.
اختیاری: انتشار در پسزمینه
بهطور پیشفرض PublishAsync پیش از بازگشت رندر میشود. برای بازگشت بهمحض صدور توکن و اجازه دادن به یک کارگر میزبانیشده برای رندر در همان پوشه، صف انتخابی را ثبت کنید:
builder.Services.AddDoconut(options => { /* … */ });
builder.Services.AddDoconutDistributedAsyncPublish();سپس بهجای انتشار درونخطی، صفبندی کنید و Async = true را در گزینههای انتشار تنظیم کنید. صف محدود است: وقتی اشباع میشود، EnqueueAsync خطای DistributedPublishQueueFullException را پرتاب میکند. با یک 503 و هدر Retry-After به آن پاسخ دهید تا کلاینتها بهجای گیر کردن، عقبنشینی کنند:
try
{
var token = await _publishQueue.EnqueueAsync(
path, shareConfig, config, documentOptions, publishOptions, ct);
return Content(token);
}
catch (DistributedPublishQueueFullException)
{
Response.Headers.RetryAfter = "5";
Response.StatusCode = 503;
return Content("The publish queue is full. Please retry.");
}بدون این ثبت، هیچچیزی ثبت نمیشود و انتشار بهصورت همزمان باقی میماند — مسیر پیشفرض دقیقاً بایت به بایت یکسان است.
زمانی که انتشار پسزمینه فعال است، به سمت خواندن یک ReadReadyPollMs غیر صفر بدهید تا برای صفحهای که کارگر هنوز ننوشته است، کمی صبر کند.
امنیت در میان گرهها
توکنهای وابسته به نشست فرض میکنند گرهٔ سرویسدهنده درخواست اولیه را دیده است. در یک مزرعه اینطور نیست، بنابراین بررسی بر پایه نشست درخواستهای معتبر را رد میکند. به همین دلیل برنامهٔ مرجع توزیعی UnsafeMode = true را تنظیم میکند.
UnsafeMode = true مدل امنیتی نیست — بلکه حذف مدلی است که دیگر اعمال نمیشود. آن را با بلیتهای دسترسی امضاشده جایگزین کنید، که هر گره میتواند بهصورت مستقل آن را تأیید کند:
builder.Services.AddDoconutDistributedDocumentSecurity(options => { /* … */ });هرگز UnsafeMode = true را به یک استقرار تکگرهای بازنگردانید، و هرگز آن را بهعنوان یک میانبر برای بررسی نشست ناموفق در یک گره در نظر نگیرید — در آنجا اصلاح صحیح AddSession() / UseSession() است. ببینید جلسات و امنیت.
بستن یک سند منتشر شده
CloseDocument(token) روی Viewer تزریقشده یک نشست محلی را آزاد میکند. برای حذف آثار منتشر شدهٔ یک سند از اشتراک، ناشر موارد زیر را در دسترس میگذارد:
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)همان FileShareConfig که سند با آن منتشر شده بود را پاس دهید.
نگاشت تنظیم‑به‑تنظیم
| قبلی | اکنون |
|---|---|
UseDoconutWebFarm(new WebFarmOptions { … }) | PublishAsync(...) در سمت نوشتن بهعلاوه UseDoconutCloud<FileShareCloudHandler>(...) در سمت خواندن |
WebFarmOptions.Path | FileShareConfig.RootPath |
PageWaitTimeSeconds / StartWaitFromPage | DistributedDocumentPublishOptions.FirstPagePriority بهاضافه FileShareConfig.ReadReadyPollMs |
viewer.OpenDocument(...) then viewer.ExportToPng(share) | یک PublishAsync(...) فراخوانی |
viewer.ExportToCloud(cloudConfig) | PublishAsync(...) با پیکربندی ارائهدهنده |
UseDoconutCloud(new CloudOptions { Location = … }) | UseDoconutCloud<THandler>(options => …, pathPrefix) |
DocOptions.IsWebfarm / WebfarmPath | هنوز موجود، به صورت IsWebFarm / WebFarmPath نوشته میشود — در مسیر انتشار نیازی نیست |
Doconut.Configs.Cloud | Doconut.Clouds |
فهرست بررسی
FileShareConfigرا در یک کمکی بسازید و از هر دو نیمه فراخوانی کنید.RootPathرا به اشتراکی که قبلاً استفاده میکنید اشاره دهید.- با
PublishAsync(...)منتشر کنید؛ هر فراخوانیExportToPng/ExportToCloudرا حذف کنید. - با
UseDoconutCloud<FileShareCloudHandler>(...)در شاخهای که مشتری فراخوانی میکند سرو کنید. FirstPagePriorityوretryOn409را همزمان روشن کنید، یا هر دو را خاموش بگذارید.- بررسی per‑session را با بلیتهای دسترسی امضاشده جایگزین کنید —
UnsafeMode = trueرا به تنهایی ارسال نکنید. - با باز کردن یک سند در یک گره و درخواست صفحهٔ دیرهنگام از گرهٔ دیگر، صحت را تأیید کنید.
آیا این صفحه مفید بود؟