การปรับใช้แบบกระจาย

ให้บริการเอกสารหนึ่งจากหลายโหนด

เมื่อหลายโหนดให้บริการเอกสารเดียวกัน โหนดที่ทำการเรนเดอร์เอกสารไม่ได้จำเป็นต้องเป็นโหนดที่ตอบสนองคำขอหน้าเว็บของเบราว์เซอร์ การปล่อยเวอร์ชันนี้จะแบ่งงานนั้นออกเป็นสองส่วนที่ชัดเจน — ด้านเขียน ที่เผยแพร่ศิลปวัตถุของเอกสารไปยังที่เก็บข้อมูลร่วม, และ ด้านอ่าน ที่ให้บริการจากโหนดใดก็ได้

หากแอปพลิเคชันของคุณในขณะนี้เรียก UseDoconutWebFarm(...), หน้านี้เป็นการแทนที่ของคุณ ไม่มี UseDoconutWebFarm หรือ WebFarmOptions อยู่ในเวอร์ชันนี้

โทโพโลยีของคุณไม่เปลี่ยนแปลง ตั้งค่ารากที่ใช้ร่วมกันให้ชี้ไปที่เส้นทาง UNC เดียวกันที่คุณใช้ในวันนี้และทุกโหนดจะทำงานต่อไปเช่นเดิม สิ่งที่เปลี่ยนแปลงคือการเผยแพร่เป็นการดำเนินการที่คุณเรียกใช้ แทนที่จะเป็นผลข้างเคียงของการเรนเดอร์

สิ่งที่แทนที่มิดเดิลแวร์เว็บฟาร์ม

ก่อนหน้านี้มี middleware ตัวเดียวทำทุกอย่าง: มันให้บริการภาพที่เรนเดอร์ล่วงหน้าออกจากโฟลเดอร์ที่ใช้ร่วมและ ทำการโพล ขณะที่โหนดอื่นยังคงเขียนอยู่ การรอคอยเป็นการประมาณที่ไม่มีข้อมูล — “บล็อกสูงสุดสามนาทีโดยคาดว่าหน้าจะปรากฏ”

ตอนนี้งานเดียวกันกลายเป็นสองการเรียกที่คุณสามารถมองเห็นและทำความเข้าใจได้:

text
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(...) เพียงหนึ่งตัวเพื่อเหตุผลนี้:

csharp
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; ชื่อคลาสไม่ได้เปลี่ยนแปลง

Propertyวัตถุประสงค์
LocationCloudLocation.FileShare สำหรับการแชร์ UNC/โลคัล
RootPathโฟลเดอร์ที่ใช้ร่วมที่ทุกโหนดสามารถเข้าถึงได้
UsePerTokenSubfoldersเก็บศิลปวัตถุของแต่ละเอกสารในโฟลเดอร์ {token} ของตนเอง
ReadReadyPollMsระยะเวลาที่ด้านอ่านรอหน้าที่ยังกำลังเขียนอยู่; 0 ปิดการโพล

ด้านเขียน — เผยแพร่เอกสาร

ฉีด DistributedDocumentPublisher และเผยแพร่แทนการเปิดเซสชันโลคัล การเรียกครั้งเดียวจะเรนเดอร์และเขียนชุดศิลปวัตถุทั้งหมด — หน้า, ข้อมูลเอกสาร, ดัชนีการค้นหา, และเมตาดาต้า:

csharp
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 บนสาขาเดียวกับที่ไคลเอนต์เรียกอยู่แล้ว:

csharp
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 เป็น read-only — มันให้บริการสิ่งที่มีอยู่แล้วบน CDN; ไม่ทำการอัปโหลด.

ความสำคัญของหน้าที่หนึ่งและ HTTP 202

FirstPagePriority = true ทำให้หน้า 1 สามารถให้บริการได้ทันที หน้าอื่น ๆ จะตอบกลับด้วย HTTP 202 Accepted จนกว่าจะถูกเขียนเสร็จ, แทนที่จะบล็อกคำขอ.

สิ่งนี้ย้ายการรอคอยไปยังไคลเอนต์, ซึ่งทำให้สามารถจำกัดและมองเห็นได้ เปิดการลองใหม่บนวิดเจ็ต:

javascript
const objViewer = $('#div_ctlDoc').docViewer({
    BasePath:   '/',
    ResPath:    'doconut-res/images',
    retryOn409: true      // การลองใหม่ 202 (และ legacy 409) การตอบสนองพร้อมใช้งาน
});

ไคลเอนต์ที่ ไม่ ทำการลองใหม่จะแสดงไทล์ที่เสียหายสำหรับหน้าที่ยังกำลังเรนเดอร์อยู่. งบประมาณการลองใหม่และค่าเริ่มต้นของมัน (retryInitialDelayMs, retryBackoffFactor, retryMaxDelayMs, retryMaxAttempts, retryMaxTotalMs) ถูกอธิบายไว้ใน การตั้งค่า Viewer.

คู่นี้ — FirstPagePriority บวกกับการโพลด้านการอ่าน — คือสิ่งที่แทนที่การตั้งค่า PageWaitTimeSeconds และ StartWaitFromPage ก่อนหน้า. ทั้งสองไม่มีอยู่แล้ว.

ตัวเลือก: เผยแพร่ในพื้นหลัง

โดยค่าเริ่มต้น PublishAsync จะเรนเดอร์ก่อนที่จะคืนค่า. เพื่อให้คืนค่าทันทีเมื่อโทเคนถูกสร้างและให้ worker ที่โฮสต์เรนเดอร์ลงในโฟลเดอร์เดียวกัน, ลงทะเบียนคิว opt-in:

csharp
builder.Services.AddDoconut(options => { /* … */ });
builder.Services.AddDoconutDistributedAsyncPublish();

จากนั้นใส่คิวแทนการเผยแพร่แบบอินไลน์, และตั้งค่า Async = true ในตัวเลือกการเผยแพร่. คิวมีขอบเขต: เมื่อเต็ม, EnqueueAsync จะโยน DistributedPublishQueueFullException. ตอบกลับด้วย 503 และหัวข้อ Retry-After เพื่อให้ไคลเอนต์ถอยหลังแทนที่จะค้าง:

csharp
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 ที่ไม่เป็นศูนย์เพื่อให้รอสั้น ๆ สำหรับหน้าที่ worker ยังไม่ได้เขียน.

ความปลอดภัยข้ามโหนด

โทเคนที่ผูกกับเซสชันสมมติว่าโหนดที่ให้บริการเห็นคำขอเริ่มต้น. ในฟาร์มอาจไม่เห็น, ดังนั้นการตรวจสอบต่อเซสชันจะปฏิเสธคำขอที่ถูกต้อง. นั่นคือเหตุผลที่แอปพลิเคชันอ้างอิงแบบกระจายตั้งค่า UnsafeMode = true.

UnsafeMode = true ไม่ใช่โมเดลความปลอดภัย — มันคือการลบโมเดลที่ไม่ใช้แล้ว แทนที่ด้วยตั๋วการเข้าถึงที่ลงนาม, ซึ่งแต่ละโหนดสามารถตรวจสอบได้อย่างอิสระ:

csharp
builder.Services.AddDoconutDistributedDocumentSecurity(options => { /* … */ });

ห้ามนำ UnsafeMode = true กลับไปยังการปรับใช้แบบโหนดเดียว, และห้ามถือว่าเป็นทางลัดสำหรับการตรวจสอบเซสชันที่ล้มเหลวบนโหนดหนึ่ง — ที่นั่นการแก้ไขที่ถูกต้องคือ AddSession() / UseSession(). ดูที่ เซสชันและความปลอดภัย.

ปิดเอกสารที่เผยแพร่

CloseDocument(token) บน Viewer ที่ฉีดจะทำลายเซสชันในเครื่อง. เพื่อเอา ส่วนประกอบ ที่เผยแพร่ของเอกสารออกจาก แชร์, ผู้เผยแพร่ให้:

text
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)

ส่งผ่าน FileShareConfig เดียวกันที่ใช้เผยแพร่เอกสาร.

การแมปการตั้งค่าแบบต่อเนื่อง

ก่อนหน้าปัจจุบัน
UseDoconutWebFarm(new WebFarmOptions { … })PublishAsync(...) ด้านการเขียนบวกกับ UseDoconutCloud<FileShareCloudHandler>(...) ด้านการอ่าน
WebFarmOptions.PathFileShareConfig.RootPath
PageWaitTimeSeconds / StartWaitFromPageDistributedDocumentPublishOptions.FirstPagePriority บวกกับ FileShareConfig.ReadReadyPollMs
viewer.OpenDocument(...) แล้ว viewer.ExportToPng(share)หนึ่งการเรียก PublishAsync(...)
viewer.ExportToCloud(cloudConfig)PublishAsync(...) พร้อมกับการกำหนดค่าของผู้ให้บริการ
UseDoconutCloud(new CloudOptions { Location = … })UseDoconutCloud<THandler>(options => …, pathPrefix)
DocOptions.IsWebfarm / WebfarmPathยังคงอยู่, เขียนเป็น IsWebFarm / WebFarmPath — ไม่จำเป็นบนเส้นทางการเผยแพร่
Doconut.Configs.CloudDoconut.Clouds

รายการตรวจสอบ

  1. สร้าง FileShareConfig ในตัวช่วยเดียวและเรียกใช้จากทั้งสองส่วน.
  2. ชี้ RootPath ไปที่ แชร์ ที่คุณใช้อยู่แล้ว.
  3. เผยแพร่ด้วย PublishAsync(...); ลบการเรียก ExportToPng / ExportToCloud ใด ๆ.
  4. ให้บริการด้วย UseDoconutCloud<FileShareCloudHandler>(...) บนสาขาที่ลูกค้าเรียก.
  5. เปิด FirstPagePriority และ retryOn409 พร้อมกัน, หรือปิดทั้งสอง.
  6. แทนที่การตรวจสอบต่อเซสชันด้วยตั๋วการเข้าถึงที่ลงนาม — อย่าส่ง UnsafeMode = true เพียงอย่างเดียว.
  7. ตรวจสอบโดยการเปิดเอกสารบนโหนดหนึ่งและขอหน้าที่ล่าช้าจากอีกโหนดหนึ่ง.

หน้านี้เป็นประโยชน์หรือไม่?