Sparkdo.Messaging.Scheduling.Scheduler 3.0.0-preview.1

This is a prerelease version of Sparkdo.Messaging.Scheduling.Scheduler.
dotnet add package Sparkdo.Messaging.Scheduling.Scheduler --version 3.0.0-preview.1
                    
NuGet\Install-Package Sparkdo.Messaging.Scheduling.Scheduler -Version 3.0.0-preview.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sparkdo.Messaging.Scheduling.Scheduler" Version="3.0.0-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sparkdo.Messaging.Scheduling.Scheduler" Version="3.0.0-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="Sparkdo.Messaging.Scheduling.Scheduler" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sparkdo.Messaging.Scheduling.Scheduler --version 3.0.0-preview.1
                    
#r "nuget: Sparkdo.Messaging.Scheduling.Scheduler, 3.0.0-preview.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sparkdo.Messaging.Scheduling.Scheduler@3.0.0-preview.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sparkdo.Messaging.Scheduling.Scheduler&version=3.0.0-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Sparkdo.Messaging.Scheduling.Scheduler&version=3.0.0-preview.1&prerelease
                    
Install as a Cake Tool

Sparkdo.Messaging.Scheduling.Scheduler

消息的延迟与 Cron 投递:把 Sparkdo.Scheduler 接成 IMessageScheduler 的实现。 到点之后消息原样发布——调度时写一次字节,投递时不再序列化。

项 值
包 Sparkdo.Messaging.Scheduling.Scheduler
交付框架 net11.0
依赖 Sparkdo.Messaging(内核)+ Sparkdo.Scheduler(跨域项目引用);随包还挂 Scheduler 与 Json 两个生成器做分析器
入口 AddMessagingScheduling(Action<MessagingSchedulingOptions>?)
消费面 IMessageScheduler(契约在 Sparkdo.Messaging.Abstractions)
AOT / 裁剪 兼容

接线

builder.Services
    .AddSparkdoJson()                                       // 宿主不是可执行文件时也必须调一次
    .AddScheduler(options => options.ApplicationName = "orders")   // Sparkdo.Scheduler 自己的接线
    .AddSparkdoMessaging(
        provider => new MessageSerializerRegistry()
            .Add(new JsonMessageSerializer(provider.GetRequiredService<IJsonSerializer>())),
        bus => bus
            .AddTransport(new InMemoryTransport(), TransportDefault.Default)
            .RequireScheduler()                             // 声明:缺调度要在建图时就炸
            .AddHandler<OrderPlacedHandler, OrderPlaced>())
    .AddMessagingScheduling(options => options.Queue = "messaging")
    .AddSparkdoMessagingHosting();
两件事不用自己做 说明
登记作业 ScheduledPublishJob 由 Scheduler 的生成器在程序集加载时登记,不进 DI
写 JSON context ScheduledPublishArgs 的 context 由本包自带([SparkdoJsonContext])。用户不必为自己的消息类型写 context——消息进调度之前就已经是字节了

用法

public sealed class OrderService(IMessageScheduler scheduler)
{
    public async ValueTask RemindLater(OrderId id, DateTimeOffset when, CancellationToken ct)
    {
        var receipt = await scheduler.ScheduleAsync(
            new PaymentReminder(id.Value),
            ScheduleSpec.At(when),                    // 或 Delay(TimeSpan) / Cron("每分钟", "* * * * *")
            SendOptions.Default,
            ct);

        if (!receipt.Accepted)
        {
            // receipt.Reason 是后端给的原文,本包不吞也不改写
            throw new InvalidOperationException($"没排上:{receipt.Reason}");
        }

        _token = receipt.Token;                       // Occurrence(一次性)或 Schedule(Cron 计划)
    }

    public ValueTask<bool> Cancel(ScheduleToken token, CancellationToken ct) => scheduler.CancelAsync(token, ct);
}
事 语义
谁解析调度面 建图期定:RequireScheduler() → 容器注册的 IMessageScheduler → 有 IScheduleBackend 就包成核心的 MessageScheduler → 声明了 NativeSchedule 的传输(今天没有)→ 都没有
注入到的 IMessageScheduler AddSparkdoMessaging 注册的转发器,取的是图上的那一份。图上没有 → 抛"本图没有调度面",修法直接点名 AddMessagingScheduling() 与 RequireScheduler()
ScheduleAsync 干了什么 查路由、写一次字节、组信封、交给后端。到点用的是这一份字节
返回的 Token Kind = Occurrence(Delay / At)或 Kind = Schedule(Cron)。CancelAsync 拿它去撤
CancelAsync 的 false 已经执行过、或找不到——不是错误
到期时刻从哪来 宿主登记的 TimeProvider(没登记用 TimeProvider.System)。宿主换了时钟(测试、回放、多节点对表),延迟就跟着那一个"现在"走

ScheduleSpec 三种

写法 落成 Scheduler 的哪个动作 约束
ScheduleSpec.Delay(TimeSpan) EnqueueAsync(DueAt = 时钟现在 + 延迟) 延迟必须 > 0
ScheduleSpec.At(DateTimeOffset) EnqueueAsync(DueAt = 指定时刻) —
ScheduleSpec.Cron(scheduleName, cron) ScheduleCronAsync 两个参数都不能是空白;凭据指向计划而不是某一次执行

ScheduleSpec 是 readonly struct,五个读属性(Kind / DelayDuration / DueAt / ScheduleName / CronExpression)只有构造它的那个工厂对应的格子有意义。

到点之后发生什么

Scheduler 触发 ScheduledPublishJob
  → 从 args 重建 Envelope(MessageId / 身份 / 内容类型 / 目的地 / Source / Headers)
  → IRawPublisher.PublishRawAsync(body, ...)
  → 走的是与当场发布**同一条**派发管道(中间件一视同仁)
后果 说法
载荷是死的 排进去之后改消息类型的字段,到点发出的还是当时那份字节。跨这个窗口的兼容要自己管
中间件照样跑 审计、幂等、工作单元都在这条链上——到点发布不是旁路
处理器看到的 Source 调度那一刻写进 ScheduledPublishArgs.Source,与当场发布是同一个表达式。JSON 按名读,所以升级前入队的记录读出来是 null(Version 没升,也不拿目的地址顶上)。把 Source 当必读信息就要把这段窗口算进去

选项与失败面

选项 / 场景 口径
MessagingSchedulingOptions.Queue null = 用 Scheduler 的默认队列;空串在注册时被 Validate() 拒。它同时作用于 EnqueueOptions.Queue 与 ScheduleOptions.Queue
SendOptions 任何覆盖项 给了就抛:SendOptions 的覆盖项(Identity / MessageId / ContentType / Endpoint / ExpirationTime / Headers)在调度路径上还不生效:到点重发带不过去。修法:先用 SendOptions.Default
停用 Cron 计划 不在消息端口里:CancelAsync 收到 Kind = Schedule 的令牌直接抛"停用 Cron 计划('…')不在消息端口里:它要操作者审计。修法:走 Sparkdo.Scheduler 的管理命令(PauseScheduleCommand + 真实 OperatorActor)"。消息端口没有操作者,替它编一个等于伪造审计记录
传输声明了 NativeSchedule 抛"传输 'x' 声明了原生调度(NativeSchedule),但还没有传输实现调度端口"
后端不收 Accepted = false + Reason 原文带回来;不重试、不改写
幂等键 调度时没设 IdempotencyKey:调度重试的幂等语义要想清楚再定。消息标识本身已可用于接收侧去重(配 Inbox 中间件)
Cron 表达式 本包不解析,交给 Sparkdo.Crontab(经 Scheduler 域)

出证

dotnet test src/messaging/test/Sparkdo.Messaging.Scheduling.Scheduler.Tests

三条主用例各自钉一件事(都在假时钟上跑,不真等):

用例 断言
延迟投递 4 分钟延迟 → 到点前 0 条、+1 分钟恰好 1 条,Token.Kind == Occurrence
Cron 投递 ScheduleSpec.Cron("每分钟", "* * * * *") → 1 条然后 2 条,Token.Kind == Schedule
取消 撤掉未执行的排程;撤 Cron 计划那一条断的是拒绝(文案里带"审计")

另有一条 ScheduledSourceTests 断到点重建的信封 Source / Destination 与当场发布一致。

Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.0.0-preview.1 61 10/30/2026