Distribuerade distributioner

Servera ett dokument från flera noder

När flera noder serverar samma dokument är noden som renderar ett dokument inte nödvändigtvis den nod som svarar på webbläsarens sidförfrågningar. Denna version delar upp den uppgiften i två tydliga halvor — en skrivsida som publicerar ett dokuments artefakter till delad lagring, och en läsida som serverar dem från vilken nod som helst.

Om din applikation för närvarande anropar UseDoconutWebFarm(...), är den här sidan ditt ersättningsalternativ. Varken UseDoconutWebFarm eller WebFarmOptions finns i denna version.

Din topologi förändras inte. Peka den delade roten på samma UNC‑sökväg som du använder idag så fortsätter varje nod att fungera som den gör nu. Det som förändras är att publicering är en operation du anropar, istället för en bieffekt av rendering.

Vad ersatte web-farm-mellanprogramvaran

Tidigare gjorde ett mellanprogram allt: det serverade förrenderade bilder från en delad mapp och pollade medan en annan nod fortfarande skrev dem. Väntan var en blind budget — "blockera upp till tre minuter i hopp om att sidan visas".

Nu är samma uppgift två anrop som du kan se och resonera kring:

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

Den delade konfigurationen som båda halvorna behöver

Publiceraren skriver med en FileShareConfig och läshanteraren serverar med en. Om RootPath eller UsePerTokenSubfolders skiljer sig åt mellan dem, svarar den andra noden med 404. Bygg konfigurationen på exakt ett ställe och anropa den från båda sidor — referensapplikationen behåller en enda SharedStore.Config(...)‑hjälpare av denna anledning:

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

FileShareConfig finns i Doconut.Clouds, tillsammans med AzureConfig, AmazonConfig, GoogleCloudConfig, DropBoxConfig, RedisConfig, CDNConfig och FTPConfig. I det tidigare biblioteket var dessa i Doconut.Configs.Cloud; klassnamnen förändrades inte.

EgenskapSyfte
LocationCloudLocation.FileShare för en UNC/lokal delning
RootPathDen delade mappen som varje nod kan nå
UsePerTokenSubfoldersBehåll varje dokuments artefakter i sin egen {token}‑mapp
ReadReadyPollMsHur länge lässidan väntar på en sida som fortfarande skrivs; 0 inaktiverar polling

Skrivsida — publicera dokumentet

Injicera DistributedDocumentPublisher och publicera istället för att öppna en lokal session. Ett anrop renderar och skriver hela artefaktuppsättningen — sidor, dokumentinformation, sökindex och 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(
    path, shareConfig, config, documentOptions, publishOptions, ct);

return Content(result.Token);

DistributedDocumentPublishResult bär Token, ArtifactPath, TotalPages och Format. Tokenet går till webbläsaren exakt som ett normalt viewer‑token gör.

Detta enda anrop ersätter det tidigare paret — att öppna dokumentet i en lokal session och sedan dumpa bilder till delningen med ExportToPng(sharedPath). ExportToPng och ExportToCloud har ingen motsvarighet här.

Läs‑sidan — servera från delningen

Montera FileShare‑hanteraren på samma gren som klienten redan anropar:

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 är generisk i den här releasen: leverantören är typargumentet snarare än ett Location‑värde som växlas inuti middleware. Handlarna heter <Provider>CloudHandler — FileShareCloudHandler, AzureCloudHandler, AmazonS3CloudHandler, GoogleCloudHandler, DropBoxCloudHandler, RedisCloudHandler, FTPCloudHandler och CDNCloudHandler.

CDN‑hanteraren är read-only — den serverar det som redan finns på CDN:n; den laddar inte upp.

Prioritet för första sidan och HTTP 202

FirstPagePriority = true gör att sida 1 kan serveras omedelbart. Senare sidor svarar med HTTP 202 Accepted tills de har skrivits, istället för att blockera begäran.

Det flyttar väntan till klienten, där den är begränsad och synlig. Aktivera återförsök på widgeten:

javascript
const objViewer = $('#div_ctlDoc').docViewer({
    BasePath:   '/',
    ResPath:    'doconut-res/images',
    retryOn409: true      // omförsök 202 (och äldre 409) beredskaps-svar
});

En klient som inte försöker igen visar trasiga rutor för sidor som fortfarande renderas. Återförsöksbudgeten och dess standardvärden (retryInitialDelayMs, retryBackoffFactor, retryMaxDelayMs, retryMaxAttempts, retryMaxTotalMs) är dokumenterade i ViewerConfig.

Detta par — FirstPagePriority plus lässidans poll — är det som ersätter de tidigare inställningarna PageWaitTimeSeconds och StartWaitFromPage. Ingen av dem finns längre.

Valfritt: publicera i bakgrunden

Som standard renderar PublishAsync innan den returnerar. För att returnera så snart token har skapats och låta en hostad worker rendera in i samma mapp, registrera den frivilliga kön:

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

Köa sedan istället för att publicera inline, och sätt Async = true på publiceringsalternativen. Kön är begränsad: när den är mättad kastar EnqueueAsync ett DistributedPublishQueueFullException. Svara med ett 503 och ett Retry-After‑huvud så att klienterna backar bort istället för att fastna:

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.");
}

Utan denna registrering är inget registrerat och publicering förblir synkron — standardvägen är byte‑för‑byte identisk.

När bakgrundspublicering är på, ge lässidan ett icke‑noll ReadReadyPollMs så den väntar kort på en sida som workern ännu inte har skrivit.

Säkerhet över noder

Sessionsbundna token antar att den serverande noden såg den inledande begäran. I en farm gör den det inte, så per‑sessions‑kontrollen skulle avvisa legitima begäranden. Därför sätter den distribuerade referensapplikationen UnsafeMode = true.

UnsafeMode = true är inte säkerhetsmodellen — det är borttagandet av en som inte längre gäller. Ersätt den med signerade åtkomstbiljetter, som varje nod kan verifiera oberoende:

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

För med UnsafeMode = true tillbaka till en en‑nod‑distribution, och behandla det aldrig som en genväg för en misslyckad sessionskontroll på en nod — där är den korrekta lösningen AddSession() / UseSession(). Se Sessions & Security.

Stänga ett publicerat dokument

CloseDocument(token) på den injicerade Viewer avslutar en lokal session. För att ta bort ett dokuments publicerade artefakter från delningen, exponerar publiceraren:

text
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)

Skicka samma FileShareConfig som dokumentet publicerades med.

Mappning per inställning

FöregåendeNu
UseDoconutWebFarm(new WebFarmOptions { … })PublishAsync(...) på skrivsidan plus UseDoconutCloud<FileShareCloudHandler>(...) på lässidan
WebFarmOptions.PathFileShareConfig.RootPath
PageWaitTimeSeconds / StartWaitFromPageDistributedDocumentPublishOptions.FirstPagePriority plus FileShareConfig.ReadReadyPollMs
viewer.OpenDocument(...) then viewer.ExportToPng(share)ett PublishAsync(...) anrop
viewer.ExportToCloud(cloudConfig)PublishAsync(...) med leverantörens konfiguration
UseDoconutCloud(new CloudOptions { Location = … })UseDoconutCloud<THandler>(options => …, pathPrefix)
DocOptions.IsWebfarm / WebfarmPathFortfarande närvarande, stavat IsWebFarm / WebFarmPath — inte behövs på publiceringsvägen
Doconut.Configs.CloudDoconut.Clouds

Checklista

  1. Bygg FileShareConfig i en hjälpfunktion och anropa den från båda halvorna.
  2. Peka RootPath på den delning du redan använder.
  3. Publicera med PublishAsync(...); ta bort alla ExportToPng / ExportToCloud anrop.
  4. Servera med UseDoconutCloud<FileShareCloudHandler>(...) på den gren som klienten anropar.
  5. Aktivera FirstPagePriority och retryOn409 tillsammans, eller låt båda vara avstängda.
  6. Ersätt per-session kontrollen med signerade åtkomstbiljetter — skicka inte UnsafeMode = true ensam.
  7. Verifiera genom att öppna ett dokument på en nod och begära en sen sida från en annan.

Var den här sidan hjälpsam?