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 write side som publicerar ett dokuments artefakter till delad lagring, och en read side som serverar dem från vilken nod som helst.

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

Din topologi förändras inte. Peka den delade roten mot 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 mellanmjukvara allt: den 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 sådan. 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älp för detta ändamål:

csharp
internal static class SharedStore
{
    public static FileShareConfig Config(string rootPath, bool asyncPublish) => new()
    {
        Location              = CloudLocation.FileShare,
        RootPath              = rootPath,               // e.g. \\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 låg dessa i Doconut.Configs.Cloud; klassnamnen ä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

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

return Content(result.Token);

DistributedDocumentPublishResult innehåller Token, ArtifactPath, TotalPages och Format. Tokenet skickas till webbläsaren exakt som ett vanligt visartoken.

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-sida — 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 på inne i middleware. Handlers heter <Provider>CloudHandler — FileShareCloudHandler, AzureCloudHandler, AmazonS3CloudHandler, GoogleCloudHandler, DropBoxCloudHandler, RedisCloudHandler, FTPCloudHandler och CDNCloudHandler.

CDN‑hanteraren är skrivskyddad — den serverar det som redan finns på CDN; 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 är skrivna, 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 i widgeten:

javascript
const objViewer = $('#div_ctlDoc').docViewer({
    BasePath:   '/',
    ResPath:    'doconut-res/images',
    retryOn409: true      // retries 202 (and legacy 409) readiness responses
});

En klient som inte återförsöker 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 pollning — 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 i samma mapp, registrera den valfria 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 en 503 och ett Retry-After‑header så att klienter backar istället för att hänga:

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

Utan denna registrering registreras inget 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

Session‑bundna token antar att den serverande noden såg den inledande begäran. I en farm gör den det inte, så per‑session‑kontrollen skulle avvisa legitima begäranden. Det är därför den distribuerade referensapplikationen sätter 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 => { /* … */ });

Ta aldrig med UnsafeMode = true tillbaka till en en-nod‑distribution, och behandla det aldrig som en genväg för en misslyckad session‑kontroll 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 inställning för 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 / WebfarmPathFinns fortfarande, stavat IsWebFarm / WebFarmPath — behövs inte på publiceringssökvä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ämna båda avstängda.
  6. Ersätt per‑session‑kontrollen med signerade åtkomstbiljetter — skicka inte bara UnsafeMode = true.
  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?