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:
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 pageDen 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:
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.
| Egenskap | Syfte |
|---|---|
Location | CloudLocation.FileShare för en UNC/lokal delning |
RootPath | Den delade mappen som varje nod kan nå |
UsePerTokenSubfolders | Behåll varje dokuments artefakter i sin egen {token}‑mapp |
ReadReadyPollMs | Hur 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:
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:
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:
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:
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:
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:
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:
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)Skicka samma FileShareConfig som dokumentet publicerades med.
Mappning inställning för inställning
| Föregående | Nu |
|---|---|
UseDoconutWebFarm(new WebFarmOptions { … }) | PublishAsync(...) på skrivsidan plus UseDoconutCloud<FileShareCloudHandler>(…) på lässidan |
WebFarmOptions.Path | FileShareConfig.RootPath |
PageWaitTimeSeconds / StartWaitFromPage | DistributedDocumentPublishOptions.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 / WebfarmPath | Finns fortfarande, stavat IsWebFarm / WebFarmPath — behövs inte på publiceringssökvägen |
Doconut.Configs.Cloud | Doconut.Clouds |
Checklista
- Bygg
FileShareConfigi en hjälpfunktion och anropa den från båda halvorna. - Peka
RootPathpå den delning du redan använder. - Publicera med
PublishAsync(...); ta bort allaExportToPng/ExportToCloud-anrop. - Servera med
UseDoconutCloud<FileShareCloudHandler>(…)på den gren som klienten anropar. - Aktivera
FirstPagePriorityochretryOn409tillsammans, eller lämna båda avstängda. - Ersätt per‑session‑kontrollen med signerade åtkomstbiljetter — skicka inte bara
UnsafeMode = true. - 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?