Verteilte Bereitstellungen

Ein Dokument von mehreren Knoten aus bereitstellen

Wenn mehrere Knoten dieselben Dokumente bereitstellen, ist der Knoten, der ein Dokument rendert, nicht unbedingt der Knoten, der die Seitenanfragen des Browsers beantwortet. Diese Version teilt diese Aufgabe in zwei explizite Hälften – eine Schreibseite, die die Artefakte eines Dokuments in den gemeinsamen Speicher veröffentlicht, und eine Leseseite, die sie von jedem Knoten aus bereitstellt.

Falls Ihre Anwendung derzeit UseDoconutWebFarm(...) aufruft, ist diese Seite Ihr Ersatz. Weder UseDoconutWebFarm noch WebFarmOptions existieren in dieser Version.

Ihre Topologie ändert sich nicht. Zeigen Sie das gemeinsame Root-Verzeichnis auf denselben UNC-Pfad, den Sie heute verwenden, und jeder Knoten funktioniert weiterhin wie bisher. Was sich ändert, ist, dass das Veröffentlichen ein Aufruf ist, den Sie tätigen, anstatt einer Nebeneffekt des Renderns.

Was die Web-Farm-Middleware ersetzt hat

Früher erledigte eine Middleware alles: Sie diente vorgerenderte Bilder aus einem gemeinsamen Ordner aus und pollte, während ein anderer Knoten sie noch schrieb. Das Warten war ein blinder Puffer – „bis zu drei Minuten blockieren, in der Hoffnung, dass die Seite erscheint“.

Jetzt ist dieselbe Aufgabe in zwei Aufrufe aufgeteilt, die Sie sehen und nachvollziehen können:

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

Die gemeinsam genutzte Konfiguration, die beide Hälften benötigen

Der Publisher schreibt mit einer FileShareConfig und der Lese‑Handler dient mit einer. Wenn RootPath oder UsePerTokenSubfolders zwischen ihnen unterschiedlich sind, liefert der andere Knoten 404. Erstellen Sie die Konfiguration an genau einer Stelle und rufen Sie sie von beiden Seiten aus auf – die Referenzanwendung hält aus diesem Grund einen einzigen SharedStore.Config(...)‑Hilfsmechanismus bereit:

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 befindet sich in Doconut.Clouds, zusammen mit AzureConfig, AmazonConfig, GoogleCloudConfig, DropBoxConfig, RedisConfig, CDNConfig und FTPConfig. In der vorherigen Bibliothek waren diese in Doconut.Configs.Cloud; die Klassennamen haben sich nicht geändert.

EigenschaftZweck
LocationCloudLocation.FileShare für einen UNC-/lokalen Share
RootPathDer gemeinsam genutzte Ordner, den jeder Knoten erreichen kann
UsePerTokenSubfoldersBewahrt die Artefakte jedes Dokuments in einem eigenen {token}‑Ordner
ReadReadyPollMsWie lange die Leseseite auf eine Seite wartet, die noch geschrieben wird; 0 deaktiviert das Polling

Schreibseite — Dokument veröffentlichen

Injizieren Sie DistributedDocumentPublisher und veröffentlichen Sie, anstatt eine lokale Sitzung zu öffnen. Ein Aufruf rendert und schreibt das komplette Artefakt‑Set – Seiten, Dokumentinformationen, Suchindex und Metadaten:

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 enthält Token, ArtifactPath, TotalPages und Format. Das Token wird an den Browser gesendet, genau wie ein normales Viewer‑Token.

Dieser einzelne Aufruf ersetzt das vorherige Paar – das Öffnen des Dokuments in einer lokalen Sitzung und anschließend das Ablegen von Bildern im Share mit ExportToPng(sharedPath). ExportToPng und ExportToCloud haben hier keine Entsprechung.

Lese‑Seite — vom Share aus bedienen

Mounten Sie den FileShare‑Handler auf dem gleichen Branch, den der Client bereits aufruft:

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 ist in diesem Release generisch: Der Provider ist das Typargument statt eines Location‑Werts, der innerhalb der Middleware ausgewertet wird. Handler heißen <Provider>CloudHandler — FileShareCloudHandler, AzureCloudHandler, AmazonS3CloudHandler, GoogleCloudHandler, DropBoxCloudHandler, RedisCloudHandler, FTPCloudHandler und CDNCloudHandler.

Der CDN‑Handler ist schreibgeschützt — er liefert nur das, was bereits im CDN liegt; er lädt nichts hoch.

Priorität der ersten Seite und HTTP 202

FirstPagePriority = true macht Seite 1 sofort servierbar. Spätere Seiten geben HTTP 202 Accepted zurück, bis sie geschrieben wurden, anstatt die Anfrage zu blockieren.

Damit wird das Warten zum Client verlagert, wo es begrenzt und sichtbar ist. Aktivieren Sie den Retry‑Mechanismus im Widget:

javascript
const objViewer = $('#div_ctlDoc').docViewer({
    BasePath:   '/',
    ResPath:    'doconut-res/images',
    retryOn409: true      // wiederholt 202 (und Legacy‑409) Readiness‑Antworten
});

Ein Client, der nicht erneut versucht, zeigt kaputte Kacheln für Seiten, die noch gerendert werden. Das Retry‑Budget und seine Vorgaben (retryInitialDelayMs, retryBackoffFactor, retryMaxDelayMs, retryMaxAttempts, retryMaxTotalMs) sind in der Viewer‑Konfiguration dokumentiert (ViewerConfig).

Dieses Paar — FirstPagePriority plus das Lese‑Seiten‑Polling — ersetzt die früheren Einstellungen PageWaitTimeSeconds und StartWaitFromPage. Keine dieser Optionen existiert mehr.

Optional: im Hintergrund veröffentlichen

Standardmäßig rendert PublishAsync, bevor es zurückkehrt. Um sofort zurückzugeben, sobald das Token erzeugt ist, und einen gehosteten Worker in denselben Ordner rendern zu lassen, registrieren Sie die Opt‑In‑Warteschlange:

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

Enqueue‑statt Inline‑Publish und setzen Sie Async = true in den Publish‑Optionen. Die Warteschlange ist begrenzt: Wenn sie gesättigt ist, wirft EnqueueAsync eine DistributedPublishQueueFullException. Antworten Sie mit einem 503 und einem Retry-After‑Header, damit Clients zurücktreten statt zu blockieren:

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

Ohne diese Registrierung ist nichts registriert und das Veröffentlichen bleibt synchron — der Standardpfad ist byte‑für‑byte identisch.

Wenn das Hintergrund‑Publishing aktiviert ist, geben Sie der Lese‑Seite ein von Null verschiedenes ReadReadyPollMs, damit sie kurz wartet, bis ein Worker eine Seite geschrieben hat.

Sicherheit über Knoten hinweg

Sitzungsgebundene Tokens gehen davon aus, dass der bedienende Knoten die Eröffnungsanfrage gesehen hat. In einem Farm‑Setup ist das nicht der Fall, sodass die Sitzungsprüfung legitime Anfragen ablehnen würde. Deshalb setzt die verteilte Referenz‑Anwendung UnsafeMode = true.

UnsafeMode = true ist nicht das Sicherheitsmodell — es ist das Entfernen eines Modells, das nicht mehr gilt. Ersetzen Sie es durch signierte Zugriffstickets, die jeder Knoten eigenständig verifizieren kann:

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

Tragen Sie UnsafeMode = true niemals in eine Single‑Node‑Bereitstellung zurück und behandeln Sie es nicht als Abkürzung für eine fehlgeschlagene Sitzungsprüfung auf einem Knoten — dort ist die korrekte Lösung AddSession() / UseSession(). Siehe Sitzungen & Sicherheit (Sessions & Security).

Schließen eines veröffentlichten Dokuments

CloseDocument(token) on dem injizierten Viewer beendet eine lokale Sitzung. Um die veröffentlichten Artefakte eines Dokuments aus dem Share zu entfernen, stellt der Publisher bereit:

text
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)

Verwenden Sie dieselbe FileShareConfig, mit der das Dokument veröffentlicht wurde.

Einstellung-für-Einstellung Zuordnung

VorherigeJetzt
UseDoconutWebFarm(new WebFarmOptions { … })PublishAsync(...) auf der Schreibseite plus UseDoconutCloud<FileShareCloudHandler>(...) auf der Leseseite
WebFarmOptions.PathFileShareConfig.RootPath
PageWaitTimeSeconds / StartWaitFromPageDistributedDocumentPublishOptions.FirstPagePriority plus FileShareConfig.ReadReadyPollMs
viewer.OpenDocument(...) then viewer.ExportToPng(share)ein PublishAsync(...) Aufruf
viewer.ExportToCloud(cloudConfig)PublishAsync(...) mit der Konfiguration des Anbieters
UseDoconutCloud(new CloudOptions { Location = … })UseDoconutCloud<THandler>(options => …, pathPrefix)
DocOptions.IsWebfarm / WebfarmPathNoch vorhanden, geschrieben IsWebFarm / WebFarmPath — nicht im Veröffentlichungs‑Pfad benötigt
Doconut.Configs.CloudDoconut.Clouds

Checkliste

  1. Erstellen Sie die FileShareConfig in einem Hilfsprogramm und rufen Sie sie von beiden Seiten auf.
  2. Setzen Sie RootPath auf das Share, das Sie bereits verwenden.
  3. Veröffentlichen Sie mit PublishAsync(...); entfernen Sie alle Aufrufe von ExportToPng / ExportToCloud.
  4. Bedienen Sie mit UseDoconutCloud<FileShareCloudHandler>(...) auf dem Zweig, den der Client aufruft.
  5. Aktivieren Sie FirstPagePriority und retryOn409 gemeinsam, oder lassen Sie beide deaktiviert.
  6. Ersetzen Sie die Sitzungs‑Prüfung durch signierte Zugriffstickets — versenden Sie nicht nur UnsafeMode = true.
  7. Verifizieren Sie, indem Sie ein Dokument auf einem Knoten öffnen und von einem anderen Knoten eine späte Seite anfordern.

War diese Seite hilfreich?