Distribuované nasazení

Obsluhujte jeden dokument z několika uzlů

Když několik uzlů obsluhuje stejné dokumenty, uzel, který dokument vykresluje, není nutně uzel, který odpovídá na požadavky prohlížeče na stránku. Toto vydání rozděluje tuto úlohu na dvě explicitní části — write side, která publikuje artefakty dokumentu do sdíleného úložiště, a read side, která je obsluhuje z libovolného uzlu.

Pokud vaše aplikace v současnosti volá UseDoconutWebFarm(...), tato stránka je jeho náhradou. Ani UseDoconutWebFarm, ani WebFarmOptions v tomto vydání neexistují.

Vaše topologie se nemění. Nastavte sdílený kořen na stejnou UNC cestu, kterou používáte dnes, a každý uzel bude nadále fungovat stejně jako nyní. Změní se jen to, že publikování je operace, kterou voláte, místo vedlejšího efektu vykreslování.

Co nahradilo middleware web-farm

Dříve jedno middleware dělalo vše: obsluhovalo předrenderované obrázky ze sdílené složky a pollovalo, zatímco jiný uzel je stále zapisoval. Čekání bylo slepý rozpočet — „blokovat až tři minuty v naději, že se stránka objeví“.

Nyní je stejná úloha dvěma voláními, která můžete vidět a rozumně pochopit:

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

Sdílená konfigurace, kterou oba díly potřebují

Publikátor zapisuje pomocí FileShareConfig a čtecí handler obsluhuje pomocí jednoho. Pokud se RootPath nebo UsePerTokenSubfolders mezi nimi liší, druhý uzel vrátí 404. Vytvořte konfiguraci na jednom místě a použijte ji z obou stran — referenční aplikace udržuje jediného pomocníka SharedStore.Config(...) z tohoto důvodu:

csharp
internal static class SharedStore
{
    public static FileShareConfig Config(string rootPath, bool asyncPublish) => new()
    {
        Location              = CloudLocation.FileShare,
        RootPath              = rootPath,               // např. \\fileserver\doconut
        UsePerTokenSubfolders = true,
        ReadReadyPollMs       = asyncPublish ? 250 : 0,
    };
}

FileShareConfig žije v Doconut.Clouds, spolu s AzureConfig, AmazonConfig, GoogleCloudConfig, DropBoxConfig, RedisConfig, CDNConfig a FTPConfig. V předchozí knihovně byly v Doconut.Configs.Cloud; názvy tříd se nezměnily.

VlastnostÚčel
LocationCloudLocation.FileShare pro UNC/místní sdílení
RootPathSdílená složka, ke které má přístup každý uzel
UsePerTokenSubfoldersUchovávejte artefakty každého dokumentu ve své vlastní složce {token}
ReadReadyPollMsJak dlouho čtecí strana čeká na stránku, která je stále zapisována; 0 zakazuje pollování

Zápisová část — publikovat dokument

Vložte DistributedDocumentPublisher a publikujte místo otevírání lokální relace. Jedno volání vykreslí a zapíše kompletní sadu artefaktů — stránky, informace o dokumentu, vyhledávací index a metadata:

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(
    pathToFile, shareConfig, config, documentOptions, publishOptions, ct);

return Content(result.Token);

DistributedDocumentPublishResult nese Token, ArtifactPath, TotalPages a Format. Token je odeslán do prohlížeče přesně jako běžný token vieweru.

Toto jediné volání nahrazuje předchozí dvojici — otevření dokumentu do lokální relace a následné nahrání obrázků na sdílení pomocí ExportToPng(sharedPath). ExportToPng a ExportToCloud zde nemají ekvivalent.

Čtecí strana — poskytovat ze sdíleného úložiště

Namontujte obslužnou rutinu FileShare na stejnou větev, kterou klient již volá:

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 je v tomto vydání obecný: poskytovatel je typový argument místo hodnoty Location, která se přepíná uvnitř middleware. Obslužné rutiny jsou pojmenovány <Provider>CloudHandler — FileShareCloudHandler, AzureCloudHandler, AmazonS3CloudHandler, GoogleCloudHandler, DropBoxCloudHandler, RedisCloudHandler, FTPCloudHandler a CDNCloudHandler.

Obslužná rutina CDN je pouze pro čtení — poskytuje to, co je již na CDN; nenahrává.

Priorita první stránky a HTTP 202

FirstPagePriority = true umožňuje, aby byla stránka 1 okamžitě dostupná. Pozdější stránky odpovídají HTTP 202 Accepted, dokud nejsou zapsány, místo blokování požadavku.

To přesouvá čekání na klienta, kde je omezené a viditelné. Povolit opakování na widgetu:

javascript
const objViewer = $('#div_ctlDoc').docViewer({
    BasePath:   '/',
    ResPath:    'doconut-res/images',
    retryOn409: true      // opakování 202 (a starší 409) připravenostních odpovědí
});

Klient, který ne provádí opakování, zobrazuje poškozené dlaždice pro stránky, které se stále vykreslují. Rozpočet pro opakování a jeho výchozí hodnoty (retryInitialDelayMs, retryBackoffFactor, retryMaxDelayMs, retryMaxAttempts, retryMaxTotalMs) jsou zdokumentovány v ViewerConfig.

Tento pár — FirstPagePriority plus čtecí poll — nahrazuje předchozí nastavení PageWaitTimeSeconds a StartWaitFromPage. Žádné z nich již neexistuje.

Volitelné: publikovat na pozadí

Ve výchozím nastavení PublishAsync vykresluje před návratem. Pro návrat okamžitě po vytvoření tokenu a umožnění hostovanému pracovníkovi vykreslit do stejné složky, zaregistrujte frontu s opt‑in:

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

Poté zařaďte do fronty místo inline publikování a nastavte Async = true v možnostech publikování. Fronta je omezená: když je přetížena, EnqueueAsync vyhodí DistributedPublishQueueFullException. Odpovězte na to pomocí 503 a hlavičky Retry-After, aby klienti odložili požadavek místo zablokování:

csharp
try
{
    var token = await _publishQueue.EnqueueAsync(
        pathToFile, 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.");
}

Bez této registrace není nic registrováno a publikování zůstává synchronní — výchozí cesta je bajt‑po‑bajtu identická.

Když je povoleno publikování na pozadí, nastavte čtecí straně nenulové ReadReadyPollMs, aby krátce čekala na stránku, kterou pracovník ještě nezapsal.

Bezpečnost napříč uzly

Tokeny vázané na relaci předpokládají, že obsluhující uzel viděl úvodní požadavek. Ve farmě tomu tak není, takže kontrola na úrovni relace by odmítla legitimní požadavky. Proto distribuovaná referenční aplikace nastavuje UnsafeMode = true.

UnsafeMode = true není bezpečnostní model — jde o odstranění modelu, který již neplatí. Nahraďte jej podepsanými přístupovými lístky, které může každý uzel ověřit nezávisle:

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

Nikdy nepřenášejte UnsafeMode = true zpět do nasazení s jedním uzlem a nikdy jej nepoužívejte jako zkratku pro selhávající kontrolu relace na jednom uzlu — správná oprava je AddSession() / UseSession(). Viz Sessions & Security.

Uzavření publikovaného dokumentu

CloseDocument(token) na injektovaném Viewer uvolňuje lokální relaci. Pro odstranění publikovaných artefaktů dokumentu ze sdíleného prostoru poskytovatel vystavuje:

text
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)

Předávejte stejný FileShareConfig, se kterým byl dokument publikován.

Mapování nastavení po nastavení

PředchozíNyní
UseDoconutWebFarm(new WebFarmOptions { … })PublishAsync(...) na zápisové straně plus UseDoconutCloud<FileShareCloudHandler>(...) na čtecí straně
WebFarmOptions.PathFileShareConfig.RootPath
PageWaitTimeSeconds / StartWaitFromPageDistributedDocumentPublishOptions.FirstPagePriority plus FileShareConfig.ReadReadyPollMs
viewer.OpenDocument(...) then viewer.ExportToPng(share)jeden PublishAsync(...) volání
viewer.ExportToCloud(cloudConfig)PublishAsync(...) s konfigurací poskytovatele
UseDoconutCloud(new CloudOptions { Location = … })UseDoconutCloud<THandler>(options => …, pathPrefix)
DocOptions.IsWebfarm / WebfarmPathStále přítomno, napsáno jako IsWebFarm / WebFarmPath — není potřeba na cestě publikování
Doconut.Configs.CloudDoconut.Clouds

Kontrolní seznam

  1. Vytvořte FileShareConfig v jednom pomocném modulu a zavolejte jej z obou částí.
  2. Nasmerujte RootPath na sdílený prostor, který již používáte.
  3. Publikujte pomocí PublishAsync(...); odstraňte všechny volání ExportToPng / ExportToCloud.
  4. Podávejte pomocí UseDoconutCloud<FileShareCloudHandler>(...) na větvi, kterou klient volá.
  5. Zapněte FirstPagePriority a retryOn409 společně, nebo je nechte oba vypnuté.
  6. Nahraďte kontrolu na úrovni relace podepsanými přístupovými lístky — nezasílejte UnsafeMode = true samostatně.
  7. Ověřte otevřením dokumentu na jednom uzlu a požádáním o pozdější stránku z jiného.

Byla tato stránka užitečná?