在 Aspire 中,事件功能允许在各种 AppHost 生命周期期间发布和订阅事件。 事件系统比生命周期事件更灵活。 这两者都允许在事件回调期间运行任意代码,但事件驱动可以更细致地控制事件的计时和发布,并提供对自定义事件的支持。
Aspire 的事件机制是 📦Aspire 托管 NuGet 包的一部分。 此包提供了一组位于Aspire.Hosting.Eventing命名空间中的接口和类,用于在Aspire AppHost 项目中发布和订阅事件。 事件范围限定为 AppHost 本身及其内部资源。
在本文中,您将学习如何在 Aspire 中使用事件功能。
AppHost 事件处理
以下事件在 AppHost 中可用,并按以下顺序发生:
- BeforeStartEvent:此事件在 AppHost 启动之前被引发。
- ResourceEndpointsAllocatedEvent:分配终结点后,将为每个资源引发此事件。
- AfterResourcesCreatedEvent:此事件在 AppHost 创建资源后引发。
上述所有事件都类似于 AppHost 生命周期。 也就是说,IDistributedApplicationLifecycleHook 的实现可以同样地处理这些事件。 但是,使用事件 API,可以在触发这些事件时运行任意代码,甚至定义自定义事件——任何实现 IDistributedApplicationEvent 接口的事件。
订阅 AppHost 事件
若要订阅内置的 AppHost 事件,请使用事件 API。 获得分布式应用程序生成器实例后,请继续访问 IDistributedApplicationBuilder.Eventing 属性并调用 Subscribe<T>(Func<T,CancellationToken,Task>) API。 请考虑以下示例 AppHost AppHost.cs 文件:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var apiService = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");
builder.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(cache)
.WaitFor(cache)
.WithReference(apiService)
.WaitFor(apiService);
builder.Eventing.Subscribe<ResourceEndpointsAllocatedEvent>(
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("2. \"{ResourceName}\" ResourceEndpointsAllocatedEvent", @event.Resource.Name);
return Task.CompletedTask;
});
builder.Eventing.Subscribe<BeforeStartEvent>(
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("1. BeforeStartEvent");
return Task.CompletedTask;
});
builder.Eventing.Subscribe<AfterResourcesCreatedEvent>(
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("3. AfterResourcesCreatedEvent");
return Task.CompletedTask;
});
builder.Build().Run();
上述代码基于初学者模板,并添加了对 Subscribe API 的调用。
Subscribe<T> API 返回可用于取消订阅事件的 DistributedApplicationEventSubscription 实例。 通常放弃返回的订阅,因为通常不需要取消订阅事件,因为当 AppHost 关闭时整个应用被拆毁。
当您运行 AppHost 时,仪表板被显示 Aspire 之后,您应能在控制台中看到以下日志输出:
info: Program[0]
1. BeforeStartEvent
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 9.4.0
info: Aspire.Hosting.DistributedApplication[0]
Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
Application host directory is: ../AspireApp/AspireApp.AppHost
info: Program[0]
2. "cache" ResourceEndpointsAllocatedEvent
info: Program[0]
2. "apiservice" ResourceEndpointsAllocatedEvent
info: Program[0]
2. "webfrontend" ResourceEndpointsAllocatedEvent
info: Program[0]
2. "aspire-dashboard" ResourceEndpointsAllocatedEvent
info: Aspire.Hosting.DistributedApplication[0]
Now listening on: https://localhost:17178
info: Aspire.Hosting.DistributedApplication[0]
Login to the dashboard at https://localhost:17178/login?t=<YOUR_TOKEN>
info: Program[0]
3. AfterResourcesCreatedEvent
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
日志输出确认事件处理程序按 AppHost 生命周期事件的顺序执行。 订阅顺序不会影响执行顺序。 首先触发 BeforeStartEvent,然后依次触发每个资源的 ResourceEndpointsAllocatedEvent,最后触发 AfterResourcesCreatedEvent。
资源事件管理
除了 AppHost 事件,还可以订阅资源事件。 资源事件是特定于单个资源触发的。 资源事件定义为 IDistributedApplicationResourceEvent 接口的实现。 以下资源事件按列出的顺序可用:
- InitializeResourceEvent:由协调器引发,以向资源发出信号,指示它们应自行初始化。
- ResourceEndpointsAllocatedEvent:当编排器为资源分配终结点时引发。
- ConnectionStringAvailableEvent:当连接字符串可用于资源时触发。
- BeforeResourceStartedEvent:在业务流程协调程序启动新资源之前引发。
- ResourceReadyEvent:当资源最初转换为就绪状态时触发。
订阅资源事件
若要订阅资源事件,请使用便捷的扩展方法。On* 获得分布式应用程序生成器实例和资源生成器后,使用该实例并链式调用所需的 On* 事件 API。 请考虑以下示例 AppHost.cs 文件:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
cache.OnResourceReady(static (resource, @event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("5. OnResourceReady");
return Task.CompletedTask;
});
cache.OnInitializeResource(
static (resource, @event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("1. OnInitializeResource");
return Task.CompletedTask;
});
cache.OnBeforeResourceStarted(
static (resource, @event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("4. OnBeforeResourceStarted");
return Task.CompletedTask;
});
cache.OnResourceEndpointsAllocated(
static (resource, @event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("2. OnResourceEndpointsAllocated");
return Task.CompletedTask;
});
cache.OnConnectionStringAvailable(
static (resource, @event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("3. OnConnectionStringAvailable");
return Task.CompletedTask;
});
var apiService = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");
builder.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(cache)
.WaitFor(cache)
.WithReference(apiService)
.WaitFor(apiService);
builder.Build().Run();
前面的代码订阅InitializeResourceEvent资源上的 ResourceReadyEvent 、ResourceEndpointsAllocatedEvent、 ConnectionStringAvailableEventBeforeResourceStartedEvent和cache事件。 调用 AddRedis 时,它将返回一个 IResourceBuilder<T>,其中 T 是 RedisResource。 将方法 On* 链式调用以订阅事件。 这些 On* 方法返回相同的 IResourceBuilder<T> 实例,因此可以链接多个调用:
-
OnInitializeResource:订阅InitializeResourceEvent。 -
OnResourceEndpointsAllocated:订阅 ResourceEndpointsAllocatedEvent 事件。 -
OnConnectionStringAvailable:订阅 ConnectionStringAvailableEvent 事件。 -
OnBeforeResourceStarted:订阅 BeforeResourceStartedEvent 事件。 -
OnResourceReady:订阅 ResourceReadyEvent 事件。
运行 AppHost 时,当显示仪表板 Aspire 时,您应在控制台中看到以下日志输出:
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 9.4.0
info: Aspire.Hosting.DistributedApplication[0]
Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
Application host directory is: ../AspireApp/AspireApp.AppHost
info: Program[0]
1. OnInitializeResource
info: Program[0]
2. OnResourceEndpointsAllocated
info: Program[0]
3. OnConnectionStringAvailable
info: Program[0]
4. OnBeforeResourceStarted
info: Aspire.Hosting.DistributedApplication[0]
Now listening on: https://localhost:17222
info: Aspire.Hosting.DistributedApplication[0]
Login to the dashboard at https://localhost:17222/login?t=<YOUR_TOKEN>
info: Program[0]
5. OnResourceReady
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
Note
某些事件阻止执行。 例如,当 BeforeResourceStartedEvent 被发布时,该资源的启动过程会被阻塞,直到在给定资源上与该事件相关的所有订阅完成执行。 事件是否会被阻止,取决于您发布它的方式(请参阅以下部分)。
发布事件
订阅任何内置事件时,无需自行发布事件,因为 AppHost 业务流程协调程序会管理以代表你发布内置事件。 但是,可以使用事件 API 发布自定义事件。 若要发布事件,必须首先将事件定义为 IDistributedApplicationEvent 或 IDistributedApplicationResourceEvent 接口的实现。 需要根据事件是全局 AppHost 事件还是特定于资源的事件来确定要实现的接口。
然后,可以通过调用以下任一 API 来订阅和发布事件:
- PublishAsync<T>(T, CancellationToken):将事件发布给特定事件类型的所有订阅者。
- PublishAsync<T>(T, EventDispatchBehavior, CancellationToken):使用指定的调度行为将事件发布到订阅特定事件类型的所有订阅者。
提供编号EventDispatchBehavior
事件被发送时,您可以控制事件如何发送给订阅者。 使用 EventDispatchBehavior 枚举来定义事件调度行为。 有以下行为可用:
- EventDispatchBehavior.BlockingSequential:按顺序触发事件,并在所有事件都处理完成之前使其阻塞。
- EventDispatchBehavior.BlockingConcurrent:同时触发事件并阻止事件,直到全部处理。
- EventDispatchBehavior.NonBlockingSequential:按顺序触发事件,但不阻塞。
- EventDispatchBehavior.NonBlockingConcurrent:同时触发事件,但不会去阻塞。
默认行为为 EventDispatchBehavior.BlockingSequential。 若要覆盖此行为,在调用发布 API(如 PublishAsync)时,请将期望的行为作为参数提供。
AppHost 生命周期事件
Eventing提供最大的灵活性。 但是,本部分介绍替代方法:生命周期事件。
Aspire AppHost 公开了几个生命周期,可以通过实现IDistributedApplicationLifecycleHook接口来挂钩。 可以使用以下生命周期方法:
| Order | Method | Description |
|---|---|---|
| 1 | BeforeStartAsync | 在分布式应用程序启动之前运行。 |
| 2 | AfterEndpointsAllocatedAsync | 在协调器为应用程序模型中的资源分配端点之后运行。 |
| 3 | AfterResourcesCreatedAsync | 在业务流程协调程序创建资源后运行。 |
注册生命周期钩子
若要注册生命周期挂钩,请实现 IDistributedApplicationLifecycleHook 接口,并使用 AddLifecycleHook API 向 AppHost 注册挂钩:
using Aspire.Hosting.Lifecycle;
using Microsoft.Extensions.Logging;
var builder = DistributedApplication.CreateBuilder(args);
builder.Services.AddLifecycleHook<LifecycleLogger>();
builder.Build().Run();
internal sealed class LifecycleLogger(ILogger<LifecycleLogger> logger)
: IDistributedApplicationLifecycleHook
{
public Task BeforeStartAsync(
DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
logger.LogInformation("1. BeforeStartAsync");
return Task.CompletedTask;
}
public Task AfterEndpointsAllocatedAsync(
DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
logger.LogInformation("2. AfterEndpointsAllocatedAsync");
return Task.CompletedTask;
}
public Task AfterResourcesCreatedAsync(
DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
{
logger.LogInformation("3. AfterResourcesCreatedAsync");
return Task.CompletedTask;
}
}
前面的代码:
- 将 IDistributedApplicationLifecycleHook 接口实现为
LifecycleLogger。 - 使用 AddLifecycleHook API 向 AppHost 注册生命周期钩子。
- 为每个事件记录一则消息。
当运行此 AppHost 时,将对每个事件执行生命周期钩子。 生成以下输出:
info: LifecycleLogger[0]
1. BeforeStartAsync
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 9.4.0
info: Aspire.Hosting.DistributedApplication[0]
Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
Application host directory is: ../AspireApp/AspireApp.AppHost
info: LifecycleLogger[0]
2. AfterEndpointsAllocatedAsync
info: Aspire.Hosting.DistributedApplication[0]
Now listening on: https://localhost:17043
info: Aspire.Hosting.DistributedApplication[0]
Login to the dashboard at https://localhost:17043/login?t=<YOUR_TOKEN>
info: LifecycleLogger[0]
3. AfterResourcesCreatedAsync
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
连接到 AppHost 生命周期的首选方法是使用事件 API。 有关详细信息,请参阅 AppHost 事件。
另请参阅
- Aspire 编排概述
在 中编排资源