Despliegues Distribuidos

Servir un documento desde varios nodos

Cuando varios nodos sirven los mismos documentos, el nodo que renderiza un documento no es necesariamente el nodo que responde a las solicitudes de página del navegador. Esta versión divide esa tarea en dos mitades explícitas — un lado de escritura que publica los artefactos de un documento en almacenamiento compartido, y un lado de lectura que los sirve desde cualquier nodo.

Si su aplicación actualmente llama a UseDoconutWebFarm(...), esta página es su reemplazo. Ni UseDoconutWebFarm ni WebFarmOptions existen en esta versión.

Su topología no cambia. Apunte la raíz compartida al mismo camino UNC que usa hoy y cada nodo seguirá funcionando como lo hace ahora. Lo que cambia es que la publicación es una operación que usted llama, en lugar de un efecto secundario del renderizado.

Qué reemplazó al middleware web-farm

Anteriormente un solo middleware hacía todo: servía imágenes pre-renderizadas desde una carpeta compartida y encuestaba mientras otro nodo aún las escribía. La espera era un presupuesto ciego — "bloquear hasta tres minutos esperando que la página aparezca".

Ahora la misma tarea son dos llamadas que puede ver y razonar:

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

La configuración compartida que ambas mitades necesitan

El publicador escribe con un FileShareConfig y el manejador de lectura sirve con uno. Si RootPath o UsePerTokenSubfolders difieren entre ellos, el otro nodo responde 404. Construya la configuración en un solo lugar y llámela desde ambos lados — la aplicación de referencia mantiene un único asistente SharedStore.Config(...) por esta razón:

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 se encuentra en Doconut.Clouds, junto con AzureConfig, AmazonConfig, GoogleCloudConfig, DropBoxConfig, RedisConfig, CDNConfig y FTPConfig. En la biblioteca anterior estaban en Doconut.Configs.Cloud; los nombres de las clases no cambiaron.

PropiedadPropósito
LocationCloudLocation.FileShare para un recurso UNC/local
RootPathLa carpeta compartida a la que puede acceder cualquier nodo
UsePerTokenSubfoldersMantenga los artefactos de cada documento en su propia carpeta {token}
ReadReadyPollMsCuánto tiempo espera el lado de lectura por una página que aún se está escribiendo; 0 desactiva el sondeo

Lado de escritura — publicar el documento

Inyecte DistributedDocumentPublisher y publique en lugar de abrir una sesión local. Una llamada renderiza y escribe el conjunto completo de artefactos — páginas, información del documento, índice de búsqueda y metadatos:

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 lleva Token, ArtifactPath, TotalPages y Format. El token se envía al navegador exactamente como lo hace un token de visor normal.

Esta única llamada reemplaza al par anterior — abrir el documento en una sesión local y luego volcar imágenes al recurso compartido con ExportToPng(sharedPath). ExportToPng y ExportToCloud no tienen equivalente aquí.

Lado de lectura — servir desde el share

Monte el controlador FileShare en la misma rama que ya llama el cliente:

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 es genérico en esta versión: el proveedor es el argumento de tipo en lugar de un valor Location conmutado dentro del middleware. Los controladores se nombran <Provider>CloudHandler — FileShareCloudHandler, AzureCloudHandler, AmazonS3CloudHandler, GoogleCloudHandler, DropBoxCloudHandler, RedisCloudHandler, FTPCloudHandler y CDNCloudHandler.

El controlador CDN es solo lectura — sirve lo que ya está en el CDN; no sube nada.

Prioridad de la primera página y HTTP 202

FirstPagePriority = true hace que la página 1 sea servible inmediatamente. Las páginas posteriores responden HTTP 202 Accepted hasta que se escriban, en lugar de bloquear la solicitud.

Eso traslada la espera al cliente, donde está limitada y visible. Habilite reintentos en el widget:

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

Un cliente que no reintenta muestra mosaicos rotos para las páginas que aún se están renderizando. El presupuesto de reintentos y sus valores predeterminados (retryInitialDelayMs, retryBackoffFactor, retryMaxDelayMs, retryMaxAttempts, retryMaxTotalMs) están documentados en ConfiguraciónDelVisor.

Este par — FirstPagePriority más la encuesta del lado de lectura — es lo que reemplaza las configuraciones anteriores PageWaitTimeSeconds y StartWaitFromPage. Ninguna de ellas existe ya.

Opcional: publicar en segundo plano

Por defecto PublishAsync renderiza antes de devolver. Para devolver tan pronto como se genera el token y permitir que un trabajador alojado renderice en la misma carpeta, registre la cola optativa:

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

Entonces encole en lugar de publicar en línea, y establezca Async = true en las opciones de publicación. La cola está limitada: cuando está saturada, EnqueueAsync lanza DistributedPublishQueueFullException. Responda con un 503 y un encabezado Retry-After para que los clientes retrocedan en lugar de detenerse:

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

Sin este registro no se registra nada y la publicación permanece sincrónica — la ruta predeterminada es idéntica byte a byte.

Cuando la publicación en segundo plano está activada, dé al lado de lectura un ReadReadyPollMs distinto de cero para que espere brevemente una página que el trabajador aún no ha escrito.

Seguridad entre nodos

Los tokens vinculados a la sesión asumen que el nodo que sirve vio la solicitud inicial. En una granja no lo hizo, por lo que la verificación por sesión rechazaría solicitudes legítimas. Por eso la aplicación de referencia distribuida establece UnsafeMode = true.

UnsafeMode = true no es el modelo de seguridad — es la eliminación de uno que ya no aplica. Reemplácelo con tickets de acceso firmados, que cada nodo puede verificar de forma independiente:

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

Nunca lleve UnsafeMode = true a una implementación de un solo nodo, y nunca lo trate como un atajo para una verificación de sesión fallida en un nodo — allí la solución correcta es AddSession() / UseSession(). Consulte Sesiones y Seguridad.

Cerrando un documento publicado

CloseDocument(token) en el Viewer inyectado elimina una sesión local. Para eliminar los artefactos publicados de un documento del recurso compartido, el publicador expone:

text
bool CloseDistributedDocument(FileShareConfig fileShareConfig, string token)

Pase el mismo FileShareConfig con el que se publicó el documento.

Mapeo configuración por configuración

AnteriorAhora
UseDoconutWebFarm(new WebFarmOptions { … })PublishAsync(...) en el lado de escritura más UseDoconutCloud<FileShareCloudHandler>(...) en el lado de lectura
WebFarmOptions.PathFileShareConfig.RootPath
PageWaitTimeSeconds / StartWaitFromPageDistributedDocumentPublishOptions.FirstPagePriority más FileShareConfig.ReadReadyPollMs
viewer.OpenDocument(...) then viewer.ExportToPng(share)una llamada a PublishAsync(...)
viewer.ExportToCloud(cloudConfig)PublishAsync(...) con la configuración del proveedor
UseDoconutCloud(new CloudOptions { Location = … })UseDoconutCloud<THandler>(options => …, pathPrefix)
DocOptions.IsWebfarm / WebfarmPathAún presente, escrito IsWebFarm / WebFarmPath — no necesario en la ruta de publicación
Doconut.Configs.CloudDoconut.Clouds

Lista de verificación

  1. Construya el FileShareConfig en una función auxiliar y llámelo desde ambas mitades.
  2. Apunte RootPath al recurso compartido que ya usa.
  3. Publique con PublishAsync(...); elimine cualquier llamada a ExportToPng / ExportToCloud.
  4. Sirva con UseDoconutCloud<FileShareCloudHandler>(...) en la rama que llama el cliente.
  5. Active FirstPagePriority y retryOn409 juntos, o desactívelos ambos.
  6. Reemplace la verificación por sesión con tickets de acceso firmado — no envíe UnsafeMode = true solo.
  7. Verifique abriendo un documento en un nodo y solicitando una página tardía desde otro.

¿Fue útil esta página?