使用持久任务调度程序(预览版)导出编排历史记录

业务流程历史记录导出功能允许你的应用从 可优化的任务计划程序0 中提取终端业务流程实例(已完成、失败或终止状态)的执行历史记录,并将其写入Azure Blob 存储。 使用此功能对调度器外部的编排数据进行审核、合规性检查、分析和长期存档。

注释

编排历史记录导出功能目前为预览版,并适用于 持久性任务 .NET SDK。 它需要 Microsoft.DurableTask.ExportHistory 包。

Tip

ExportHistoryWebApp 提供了完整的可运行引用示例。 我们建议在遵循本指南时将其用作参考。

导出编排历史记录的工作原理

导出历史记录在内部使用持久实体和业务流程,通过以下过程可靠地管理导出作业。

  1. 创建导出作业,指定时间窗口并设置导出模式 ExportHistoryClient
  2. SDK 将创建一个持久实体(ExportJob),用于跟踪作业的状态和进度。
  3. 内部业务流程列出与指定时间窗口和状态筛选器匹配的终端业务流程实例。
  4. 对于每个匹配实例,活动将从 Durable Task Scheduler 中提取完整的执行历史记录。
  5. 历史记录已序列化(默认情况下使用 gzip 压缩的 JSONL),并写入Azure Blob 存储。
  6. 任务会记录进度检查点,以便在中断时能够恢复。

批处理和连续作业的导出模式

导出历史记录支持两种模式:

模式 Behavior
批处理 导出在固定时间范围内达到终端状态的实例(completedTimeFromcompletedTimeTo),然后将作业标记为已完成。
连续的 completedTimeFrom 开始,无限持续地跟踪终端实例,且无结束时间。 作业保持活动状态,直到将其删除。

先决条件

  • .NET 8 SDK 或更高版本
  • 持久任务计划程序任务中心(或本地模拟器)
  • 用于本地开发的Azure 存储帐户(或 Azurite
  • 以下 NuGet 包:
    • Microsoft.DurableTask.ExportHistory
    • Microsoft.DurableTask.Client.AzureManaged
    • Microsoft.DurableTask.Worker.AzureManaged

启用编排历史记录导出

  1. 安装导出历史记录包。

    dotnet add package Microsoft.DurableTask.ExportHistory
    
  2. 为 Durable Task Scheduler 安装 Azure 托管客户端包和工作器包。

    dotnet add package Microsoft.DurableTask.Client.AzureManaged
    dotnet add package Microsoft.DurableTask.Worker.AzureManaged
    
  3. 在工作端和客户端上注册导出历史记录。

    using Microsoft.DurableTask.Client;
    using Microsoft.DurableTask.Client.AzureManaged;
    using Microsoft.DurableTask.ExportHistory;
    using Microsoft.DurableTask.Worker;
    using Microsoft.DurableTask.Worker.AzureManaged;
    
    string connectionString = builder.Configuration.GetValue<string>("DURABLE_TASK_CONNECTION_STRING")
        ?? throw new InvalidOperationException("Missing DURABLE_TASK_CONNECTION_STRING");
    
    string storageConnectionString = builder.Configuration.GetValue<string>("EXPORT_HISTORY_STORAGE_CONNECTION_STRING")
        ?? throw new InvalidOperationException("Missing EXPORT_HISTORY_STORAGE_CONNECTION_STRING");
    
    string containerName = builder.Configuration.GetValue<string>("EXPORT_HISTORY_CONTAINER_NAME")
        ?? throw new InvalidOperationException("Missing EXPORT_HISTORY_CONTAINER_NAME");
    
    // Register the worker with export history support.
    // This registers internal entities, orchestrations, and activities that manage export jobs.
    builder.Services.AddDurableTaskWorker(worker =>
    {
        worker.UseDurableTaskScheduler(connectionString);
        worker.UseExportHistory();
    });
    
    // Register the client with export history support.
    // This configures the Azure Blob Storage destination and registers the ExportHistoryClient.
    builder.Services.AddDurableTaskClient(client =>
    {
        client.UseDurableTaskScheduler(connectionString);
        client.UseExportHistory(options =>
        {
            options.ConnectionString = storageConnectionString;
            options.ContainerName = containerName;
    
            // Optional: set a virtual folder path prefix for blob names (for example, "exports/daily").
            // When set, blobs are written to "{prefix}/{hash}.{ext}" instead of "{hash}.{ext}".
            options.Prefix = builder.Configuration.GetValue<string>("EXPORT_HISTORY_PREFIX");
        });
    });
    

创建和管理导出作业

启用导出历史记录后,请使用 ExportHistoryClient 创建和管理作业。

创建批量导出作业

在以下示例中,批处理导出作业导出在固定时间范围内达到终端状态的所有业务流程实例。

ExportHistoryClient exportClient = app.Services.GetRequiredService<ExportHistoryClient>();

ExportJobCreationOptions options = new(
    mode: ExportMode.Batch,
    completedTimeFrom: DateTimeOffset.UtcNow.AddHours(-24),
    completedTimeTo: DateTimeOffset.UtcNow,
    destination: new ExportDestination("my-export-container")
    {
        // Virtual folder path prefix applied to all blob names for this job
        Prefix = "exports/daily",
    });

ExportHistoryJobClient jobClient = await exportClient.CreateJobAsync(options);
ExportJobDescription description = await jobClient.DescribeAsync();

创建一个连续导出作业

在以下示例中,连续导出作业会从开始时间起持续监控终端实例。

ExportJobCreationOptions options = new(
    mode: ExportMode.Continuous,
    completedTimeFrom: DateTimeOffset.UtcNow,
    completedTimeTo: null,
    destination: null);

ExportHistoryJobClient jobClient = await exportClient.CreateJobAsync(options);

destination 为 null 时,作业将使用在 ExportHistoryStorageOptions 中配置的默认容器和前缀。

获取导出作业详细信息

使用以下代码检索作业的完整说明,包括作业的状态、进度计数器和任何错误。

ExportJobDescription? job = await exportClient.GetJobAsync("my-job-id");

注释

GetJobAsync 如果指定的作业 ID 不存在,则抛出 ExportJobNotFoundException。 在查询可能已被删除的作业时处理此异常。

ExportJobDescription 包括:

财产 Description
JobId 唯一作业标识符。
Status 当前状态:Pending、、ActiveFailedCompleted
CreatedAt 当作业被创建时,
LastModifiedAt 作业上次更新时间。
ScannedInstances 到目前为止扫描的实例总数。
ExportedInstances 到目前为止导出的实例总数。
LastError 最后一条错误消息(如果有)。

列出作业

使用类似于以下示例的代码导出活动作业的列表。

ExportJobQuery query = new()
{
    Status = ExportJobStatus.Active,
    CreatedFrom = DateTimeOffset.UtcNow.AddDays(-7),
    PageSize = 50,
};

AsyncPageable<ExportJobDescription> jobs = exportClient.ListJobsAsync(query);

await foreach (ExportJobDescription job in jobs)
{
    Console.WriteLine($"{job.JobId}: {job.Status} ({job.ExportedInstances} exported)");
}

以下为 ExportJobQuery 支持的筛选器属性:

财产 Description
Status 按作业状态筛选:Pending、、ActiveFailedCompleted
JobIdPrefix 筛选 ID 以该前缀开头的作业。
CreatedFrom 仅返回在此时间或之后创建的作业。
CreatedTo 仅返回在此时间或之前创建的作业。
PageSize 每个页面的最大结果数。
ContinuationToken 用于检索下一页结果的令牌。

删除任务

使用以下代码来删除作业。

ExportHistoryJobClient jobClient = exportClient.GetJobClient("my-job-id");
await jobClient.DeleteAsync();

注释

DeleteAsync 引发 ExportJobNotFoundException 如果作业不存在。 删除作业不会从 Azure Blob 存储中删除已导出的 Blob。

导出作业创建选项

ExportJobCreationOptions 控制导出作业行为,并包含以下参数。

参数 Required Description 默认
mode Yes Batch 用于固定窗口或 Continuous 用于持续导出。
completedTimeFrom 是(Batch) 时间窗口(含)的开始时间,具体取决于实例达到终端状态的时间。 连续模式下,若未提供,则默认为 UtcNow
completedTimeTo 是(Batch) 时间窗口结束(含)。 对于连续模式,必须省略。 将来不能。
destination 覆盖此作业的默认的 Blob 容器和前缀。 使用来自ExportHistoryStorageOptions的默认值
jobId 自定义作业标识符。 自动生成的 GUID
format 导出格式:JSONL(gzip-compressed)或 JSON(未压缩)。 带有 gzip 的 JSONL
runtimeStatus 按终端状态进行筛选: CompletedFailedTerminated 所有终端状态
maxInstancesPerBatch 每个批处理要处理的实例数(1-1000)。 100

导出的数据被存储在 Azure Blob 存储中

导出的历史记录使用以下参数写入Azure Blob 存储。

  • 容器:默认来自 EXPORT_HISTORY_CONTAINER_NAME,或逐个作业 destination 覆盖。
  • Blob 名称:派生自(completedTimestamp, instanceId)的 SHA-256 哈希。
  • 文件格式
    • 默认值: .jsonl.gz (JSON 行、gzip 压缩 — 每行一个历史记录事件)
    • 可选: .json (未压缩的历史记录事件的 JSON 数组)
  • 带前缀的 Blob 路径:配置前缀时(例如, exports/daily),Blob 路径变为 exports/daily/{hash}.{ext}。 如果没有前缀,Blob 将直接写入容器根目录。{hash}.{ext}

每个 Blob 都包含一个用于追溯的 instanceId 元数据标记。

导出历史记录配置的环境变量

将这些环境变量与 Durable Task .NET SDK 导出历史记录示例配合使用。

变量 Description 示例默认值
DURABLE_TASK_CONNECTION_STRING 持久任务计划程序连接字符串 Endpoint=http://localhost:8080;TaskHub=default;Authentication=None
EXPORT_HISTORY_STORAGE_CONNECTION_STRING Azure 存储的历史 blob 导出连接字符串 UseDevelopmentStorage=true
EXPORT_HISTORY_CONTAINER_NAME 导出历史记录的 Blob 容器 export-history
EXPORT_HISTORY_PREFIX Blob 名称的可选虚拟文件夹路径前缀 取消设置
ASPNETCORE_URLS 供示例 HTTP 主机使用的侦听 URL 框架默认值

导出历史记录的Azure权限

使用 Azure 资源 取代 本地模拟器时,应用标识需要访问 Durable Task Scheduler 和 Blob 存储:

  1. 在应用的任务中心上授予 Durable Task Data Contributor
  2. 授予 Storage Blob Data Contributor 存储导出的历史记录 blob 的存储帐户。

导出作业时的重要注意事项

  • 并发清除操作
    如果在导出作业运行时清除业务流程实例,则导出可能会受到影响。 在导出读取实例之前被清除的实例将会在导出数据中丢失。 避免同时运行清除操作与涵盖同一时间窗口的活动导出作业。

  • Blob 清理
    删除导出作业不会从 Azure Blob 存储 中移除已导出的 blob。 如果需要删除导出的数据,请分别从存储帐户中删除 Blob 数据。

验证导出是否正常工作

创建导出作业后,通过检查这两个信号来验证它是否正常工作:

  • 作业状态从 Pending 转变为 Active,最终转变为 Completed(适用于批处理模式)。
  • Blob 条目显示在配置的导出容器中。

您可以以编程方式轮询作业状态。

ExportJobDescription? job;
do
{
    await Task.Delay(TimeSpan.FromSeconds(5));
    job = await exportClient.GetJobAsync(jobId);
    Console.WriteLine($"Status: {job?.Status}, Exported: {job?.ExportedInstances}");
}
while (job?.Status is ExportJobStatus.Pending or ExportJobStatus.Active);

对于本地开发,请运行此Azure CLI命令来检查容器:

az storage blob list \
  --connection-string "UseDevelopmentStorage=true" \
  --container-name export-history \
  --output table

Tip

ExportHistoryWebApp 示例包含一个文件,其中有适用于 VS Code 的现成 REST 客户端请求。 打开它并单击“ 发送请求 ”以快速测试创建、获取、列出和删除操作。

后续步骤