- 后端
- 微服务
【免费下载链接】akka.net
Canonical actor model implementation for .NET with local + distributed actors in C# and F#.
本文以 Akka.NET 仓库中 health-checks.md 文档为主线,系统讲解 Akka.Hosting 原生的IAkkaHealthCheck/WithHealthCheck健康检查体系:如何把它接入Microsoft.Extensions.Diagnostics.HealthCheckService、如何用委托或依赖注入方式编写自定义检查、如何启用 ActorSystem 存活、Cluster 就绪与 Akka.Persistence 持久化插件等内置检查,并结合仓库源码剖析注册、适配与标签过滤的底层实现。
1. 为什么不再使用 Akka.HealthChecks,而是 IAkkaHealthCheck
Akka.NET 官方已弃用独立的 Akka.HealthChecks 包,转而提供构建在 Akka.Hosting 之上的更简单、可配置性更强的方案:IAkkaHealthCheck接口与WithHealthCheck系列扩展方法。通过 Akka.Hosting 的扩展方法,所有注册到AkkaConfigurationBuilder的健康检查都会自动注册进Microsoft.Extensions.Diagnostics.HealthCheckService,其调用时机与 ASP.NET Core、Entity Framework 等框架健康检查完全一致。
这个设计的关键在于"桥接":Akka 侧的检查对象实现的是 Akka 自己的接口,而微软侧的HealthCheckService只认识IHealthCheck。仓库源码中的 HealthCheckAdapter 正是这个桥接器——它在每次检查时构造一个AkkaHealthCheckContext(携带ActorSystem与当前注册的Registration元数据),再转发给真正的IAkkaHealthCheck.CheckHealthAsync执行。
2. 核心 API:IAkkaHealthCheck、AkkaHealthCheckContext 与 AkkaHealthCheckRegistration
三个核心类型都定义在 IAkkaHealthCheck.cs:
IAkkaHealthCheck:唯一需要实现的方法为Task<HealthCheckResult> CheckHealthAsync(AkkaHealthCheckContext context, CancellationToken cancellationToken = default)。其 XML 文档明确说明,检查可以涵盖 Akka.Persistence、Akka.Cluster 等插件的健康状态,也可以直接向特定 actor 发消息并等待应答。AkkaHealthCheckContext:检查执行时的上下文,包含ActorSystem:当前关联的 ActorSystem;ActorRegistry:等价于ActorRegistry.For(ActorSystem),便于按类型解析已注册的 actor;Registration:对应的HealthCheckRegistration,可从中读取FailureStatus等注册元数据(在测试环境中可能为 null,源码注释已说明)。
AkkaHealthCheckRegistration:注册记录,承载四组元数据,源码中的约束值得注意:FailureStatus:检查失败时上报的状态,传null时默认HealthStatus.Unhealthy;Tags:过滤用标签集合,内部以StringComparer.OrdinalIgnoreCase构建,大小写不敏感;Timeout:每次调用的超时,不允许为 0 或负数,但允许Timeout.InfiniteTimeSpan(构造函数与 setter 都会抛ArgumentOutOfRangeException);- 工厂函数:实例型注册直接包装已有
IAkkaHealthCheck;DI 型注册则保存Func<IServiceProvider, IAkkaHealthCheck>,延迟到执行时从 DI 容器解析。
2.1 注册进微软健康检查服务
AkkaConfigurationBuilder.Bind() 中有一段关键装配逻辑:通过ServiceCollection.AddOptions<HealthCheckServiceOptions>().PostConfigure<AkkaConfigurationBuilder>,在 Host 构建完成后把akka.HealthChecks中每个注册项转换为HealthCheckRegistration并追加到opts.Registrations。也就是说,你不需要手工调用AddHealthChecks().Add(...),只要 Host 上已调用AddHealthChecks(),Akka 的检查就会自动挂载。
转换逻辑在 AkkaHealthCheckExtensions:ToHealthCheckRegistration()会为每个注册项强制追加常量akka标签(public const string AkkaTag = "akka"),并生成一个惰性工厂——在健康检查服务实际执行时,才从IServiceProvider解析出ActorSystem与检查实例,包装成HealthCheckAdapter。
3. 委托式健康检查:最低仪式感的写法
AkkaConfigurationBuilder提供两个WithHealthCheck重载(见 AkkaHostingExtensions.cs):
public static AkkaConfigurationBuilder WithHealthCheck(this AkkaConfigurationBuilder builder, string name, IAkkaHealthCheck healthCheck, HealthStatus? failureStatus = null, IEnumerable<string>? tags = null, TimeSpan? timeout = null); public static AkkaConfigurationBuilder WithHealthCheck(this AkkaConfigurationBuilder builder, string name, Func<ActorSystem, ActorRegistry, CancellationToken, Task<HealthCheckResult>> healthCheck, HealthStatus? failureStatus = null, IEnumerable<string>? tags = null, TimeSpan? timeout = null);委托重载内部会用 DelegateHealthCheck 把你的 lambda 包装为IAkkaHealthCheck。下面是官方文档给出的完整示例,演示"向注册表中的 actor 发送Identify探测其存活":
builder .WithActorSystemLivenessCheck() // have to opt-in to the built-in health check .WithHealthCheck("FooActor alive", async (system, registry, cancellationToken) => { /* * N.B. CancellationToken is set by the call to MSFT.EXT.DIAGNOSTICS.HEALTHCHECK, * so that value could be "infinite" by default. * * Therefore, it might be a really, really good idea to guard this with a non-infinite * timeout via a LinkedCancellationToken here. */ try { var fooActor = await registry.GetAsync<FooActor>(cancellationToken); try { var r = await fooActor.Ask<ActorIdentity>(new Identify("foo"), cancellationToken: cancellationToken); if (r.Subject.IsNobody()) return HealthCheckResult.Unhealthy("FooActor was alive but is now dead"); } catch (Exception e) { return HealthCheckResult.Degraded("FooActor found but non-responsive", e); } } catch (Exception e2) { return HealthCheckResult.Unhealthy("FooActor not found in registry", e2); } return HealthCheckResult.Healthy("fooActor found and responsive"); });这段示例值得逐层解读,它同时示范了三档健康状态的语义划分:
| 场景 | 返回状态 | 含义 |
|---|---|---|
actor 在注册表中且Identify应答正常 | Healthy | 目标 actor 存在且响应 |
Ask抛出异常(超时/无响应) | Degraded | actor 存在但暂时无响应 |
| 注册表查询失败 | Unhealthy | 目标根本不在注册表中 |
r.Subject.IsNobody() | Unhealthy | actor 曾被注册但已死亡 |
示例中那条注释是生产实践的重要提醒:传入的CancellationToken由微软健康检查服务注入,默认可能是"无限"的,因此涉及Ask、GetAsync等网络/远程操作的检查,最好自行用LinkedCancellationTokenSource加一个非无限超时兜底。仓库的测试 HealthChecksSpec 中ShouldReturnAppropriateResults用例正是按"未注册 → Unhealthy、注册存活 → Healthy、PoisonPill杀死后 → Unhealthy"三条路径验证了上述状态机,并断言了failureStatus: Degraded、tags: ["foo", "actor"]、timeout: 30s等自定义参数被完整记录到注册项中。
4. 依赖注入式健康检查(1.5.51+)
自 Akka.Hosting 1.5.51 起,健康检查支持从 DI 容器注入依赖。核心是泛型重载WithHealthCheck:
public AkkaConfigurationBuilder WithHealthCheck<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>( string name, HealthStatus? failureStatus = null, IEnumerable<string>? tags = null, TimeSpan? timeout = null) where T : class, IAkkaHealthCheck { var registration = new AkkaHealthCheckRegistration(name, GetServiceOrCreateInstance, failureStatus, tags, timeout); HealthChecks[registration.Name] = registration; return this; static T GetServiceOrCreateInstance(IServiceProvider sp) => ActivatorUtilities.GetServiceOrCreateInstance<T>(sp); }实现上有两点值得注意:
- 懒解析:注册时只记录一个
IServiceProvider -> T工厂(AkkaHealthCheckRegistration的 internal 构造函数专门接受 factory),真正创建实例发生在健康检查服务执行时。 - 健康检查类本身不必注册进 DI:工厂使用
ActivatorUtilities.GetServiceOrCreateInstance<T>()——已注册则解析,未注册则用容器内的可用服务构造新实例。但构造函数的依赖必须可解析。同时,[DynamicallyAccessedMembers(PublicConstructors)]特性保证了 NativeAOT/trim 场景下构造器不会被裁剪。
完整示例(来自官方文档):
// Define a custom health check with DI support public class MyHealthCheckWithDependencies : IAkkaHealthCheck { private readonly ILogger<MyHealthCheckWithDependencies> _logger; private readonly IMyService _myService; public MyHealthCheckWithDependencies( ILogger<MyHealthCheckWithDependencies> logger, IMyService myService) { _logger = logger; _myService = myService; } public async Task<HealthCheckResult> CheckHealthAsync( AkkaHealthCheckContext context, CancellationToken cancellationToken = default) { try { _logger.LogInformation("Running health check with DI"); var isHealthy = await _myService.CheckServiceHealthAsync(cancellationToken); return isHealthy ? HealthCheckResult.Healthy("Service is healthy") : HealthCheckResult.Unhealthy("Service is not healthy"); } catch (Exception ex) { _logger.LogError(ex, "Health check failed"); return HealthCheckResult.Unhealthy($"Health check failed: {ex.Message}"); } } } // Register the health check using the generic WithHealthCheck<T>() method builder .WithActorSystemLivenessCheck() .WithHealthCheck<MyHealthCheckWithDependencies>( name: "MyServiceHealth", failureStatus: HealthStatus.Degraded, tags: new[] { "ready", "service" }, timeout: TimeSpan.FromSeconds(5));注意failureStatus与timeout都是命名参数透传的:前者决定"检查未主动返回结果时兜底上报什么状态",后者约束单次执行的时限。
5. 内置健康检查一览
所有 Akka.NET 健康检查都会被打上
akka标签(由 AkkaHealthCheckExtensions 强制追加),因此可以方便地通过健康检查端点按标签过滤,只暴露或只运行 Akka 相关的检查。
5.1 WithActorSystemLivenessCheck:ActorSystem 存活探针
builder.WithActorSystemLivenessCheck( failureStatus: HealthStatus.Unhealthy, // 可选,默认 Unhealthy tags: new[] { "akka", "liveness" }); // 可选,默认 ["akka", "liveness"]其实现 ActorSystemLivenessCheck 只有两行核心逻辑:
if (context.ActorSystem.WhenTerminated.IsCompleted) { return Task.FromResult(new HealthCheckResult(status: context.Registration.FailureStatus, description: "ActorSystem has terminated.")); } return Task.FromResult(new HealthCheckResult(HealthStatus.Healthy, description: "ActorSystem is running."));从源码结构看,它通过WhenTerminated是否已完成来判断 ActorSystem 是否已终止,失败时上报的是注册项的FailureStatus而非硬编码Unhealthy——这就是failureStatus参数的意义:如果你希望 ActorSystem 终止只降级为Degraded而不影响探针整体判定,可以显式传入。文档同时提醒:Akka.Hosting 通常在 ActorSystem 死亡时本来就会主动关停进程,因此该检查更多是一种防御性的显式声明。
5.2 WithAkkaClusterReadinessCheck:集群就绪探针
对 Akka.Cluster 用户,AkkaClusterReadinessCheck 解决的是"节点还没加入集群就先被负载均衡打流量"的问题:在成功 join 并进入Up/WeaklyUp之前,检查持续返回Unhealthy,从而让 LB/服务网格把本节点摘除。
public Task<HealthCheckResult> CheckHealthAsync(AkkaHealthCheckContext context, CancellationToken cancellationToken = default) { if (WeHaveJoined && FinishedJoining != null) return Task.FromResult(HealthyResult(FinishedJoining.Value)); var cluster = Cluster.Get(context.ActorSystem); WeHaveJoined = cluster.SelfMember.Status is MemberStatus.Up or MemberStatus.WeaklyUp; if (WeHaveJoined) { FinishedJoining = DateTime.UtcNow; return Task.FromResult(HealthyResult(FinishedJoining.Value)); } return Task.FromResult(UnhealthyResult(DateTime.UtcNow, context.Registration.FailureStatus)); }实现细节:
- 判定条件是本节点 SelfMember 的状态为
MemberStatus.Up或WeaklyUp,而非"已发起 Join"; - 类内部缓存了
WeHaveJoined/FinishedJoining,一旦观测到成功加入就锁定,Healthy 结果里会附上"从启动到观测到成功加入"的耗时(BeganJoining在构造时取DateTime.UtcNow); - 默认注册名为
akka.cluster.join,默认标签为["akka", "ready", "akka.cluster"],其中ready专门用于 readiness 端点过滤(见 WithAkkaClusterReadinessCheck 扩展)。
此外,WithClustering支持通过ClusterOptions一键开启:
public bool ClusterReadyCheck { get; set; } // 默认 false在 WithClustering 实现 中,当options is { ClusterReadyCheck: true }时会自动调用builder.WithAkkaClusterReadinessCheck(),省去手工注册。
5.3 Akka.Persistence 健康检查:Journal 与 Snapshot Store
Akka.Hosting 生态为持久化插件提供了开箱即用的检查。Akka.Persistence.Hosting 中的JournalHealthCheck与SnapshotStoreHealthCheck(均为 internal)分别调用 Akka.Persistence 内置的健康检查 API:
var persistence = Persistence.Instance.Apply(context.ActorSystem); var journalResult = await persistence.CheckJournalHealthAsync(_journalPluginId, cancellationToken); return journalResult.ToHealthCheckResult();底层 Persistence.CheckJournalHealthAsync / CheckSnapshotStoreHealthAsync 的工作方式是:向对应插件 actor 发送CheckJournalHealth/CheckSnapshotStoreHealth消息并Ask回PersistenceHealthCheckResult,且会用CancellationTokenSource.CreateLinkedTokenSource把调用方 token 与Settings.AskTimeout关联起来——即使外层 token 是"无限"的,问讯本身也有超时保护,这正好呼应了第 3 节里对自定义检查的警告。
插件侧返回的 PersistenceHealthStatus 三档状态(Healthy/Degraded/Unhealthy)会通过HealthCheckExt.ToHealthStatus()映射为微软侧的同名状态,Description、Exception和附加Data字典(如集群成员数、unreachable 数等诊断数据)一并透传到HealthCheckResult。
配置方式是在 journal/snapshot 构建器上链式调用WithHealthCheck()(见 AkkaPersistenceJournalBuilder 与 AkkaPersistenceHostingExtensions):
builder.Services.AddAkka("MyActorSystem", configurationBuilder => { configurationBuilder // Journal with health check .WithJournal( new SqlServerJournalOptions { ConnectionString = "...", IsDefaultPlugin = true }, journal => journal .AddWriteEventAdapter<MyAdapter>("adapter", new[] { typeof(MyEvent) }) .WithHealthCheck( unHealthyStatus: HealthStatus.Degraded, name: "sql-journal")) // Snapshot store with health check .WithSnapshot( new SqlServerSnapshotOptions { ConnectionString = "...", IsDefaultPlugin = true }, snapshot => snapshot .WithHealthCheck( unHealthyStatus: HealthStatus.Degraded, name: "sql-snapshot")); });参数语义(以 journal 构建器为准,snapshot 构建器对称):
unHealthyStatus(默认HealthStatus.Degraded):插件报告不健康时最终上报的状态;name(可选):不传时默认使用插件 HOCON id,例如akka.persistence.journal.{JournalId};tags(可选):journal 检查默认标签为["akka", "persistence", "journal"],snapshot 侧则为akka、persistence、snapshot-store家族,便于按标签过滤。
也可以把 journal 和 snapshot 合并配置:
builder.Services.AddAkka("MyActorSystem", configurationBuilder => { configurationBuilder .WithJournalAndSnapshot( new SqlServerJournalOptions { ConnectionString = "...", IsDefaultPlugin = true }, new SqlServerSnapshotOptions { ConnectionString = "...", IsDefaultPlugin = true }, journal => journal.WithHealthCheck(), snapshot => snapshot.WithHealthCheck()); });这些检查会自动完成:校验持久化插件配置是否正确、测试到底层存储(数据库、云存储等)的连通性、插件正常时上报Healthy、发现问题时上报可配置的Degraded/Unhealthy。对有特殊检查需求的插件,构建器还提供WithCustomHealthCheck(AkkaHealthCheckRegistration),允许你直接塞入自己的注册项替代默认实现。
一个容易踩坑的点在 AkkaPersistenceJournalBuilder.Build() 的实现里:健康检查的注入发生在Build()最前面、任何早退逻辑之前,因此只调用journal.WithHealthCheck()而不配置任何 event adapter,检查项也会被正常注册——仓库测试 UnifiedApiSpecs 中的Journal_health_check_should_be_registered_without_event_adapters正是针对这一回归场景。
6. 端到端示例:在 Web Host 中落地
仓库自带示例 Akka.Hosting.Asp.LoggingDemo 展示了完整的接线方式:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddHealthChecks(); builder.Services.AddAkka("MyActorSystem", (configurationBuilder, serviceProvider) => { configurationBuilder .ConfigureLoggers(setup => { setup.LogLevel = LogLevel.DebugLevel; setup.ClearLoggers(); setup.AddLoggerFactory(); }) .WithAkkaClusterReadinessCheck() .WithActorSystemLivenessCheck() .WithHealthCheck<TestHealth>("di-test", HealthStatus.Unhealthy, new[] { "test", "custom" }) .WithActors((system, registry) => { /* 启动并注册 actor */ }); });要点复述:AddHealthChecks()与AddAkka(...)的先后无所谓,真正生效的是AkkaConfigurationBuilder.Bind()里的PostConfigure钩子,它在 Host 组装阶段统一完成注入。之后你就可以按 ASP.NET Core 惯例映射/health端点、用tags过滤器区分 liveness(akka、liveness)与 readiness(ready)探针。
7. 关键结论速查
| 主题 | 结论 | 依据 |
|---|---|---|
| 检查的挂载方式 | 通过PostConfigure<HealthCheckServiceOptions>自动注入,无需手工Add<T>() | AkkaConfigurationBuilder.csBind() |
| 统一标签 | 每个检查强制追加akka标签,大小写不敏感 | AkkaHealthCheckExtensions.cs |
| 委托检查包装 | lambda 被包装为 internal 的DelegateHealthCheck | DelegateHealthCheck.cs |
| DI 解析 | WithHealthCheck<T>使用ActivatorUtilities.GetServiceOrCreateInstance<T>,检查类无需预注册 | AkkaConfigurationBuilder.cs |
| Liveness 判定 | ActorSystem.WhenTerminated.IsCompleted即判失败 | ActorSystemCheck.cs |
| Readiness 判定 | SelfMember 状态为Up/WeaklyUp才 Healthy,默认标签含ready | AkkaClusterHealthCheck.cs |
| 持久化检查超时 | 与Settings.AskTimeout关联的 linked CTS,天然有上限 | Persistence.cs |
| 行为回归验证 | 状态机三路径 + DI 解析 + 自定义参数透传 | HealthChecksSpec.cs |
掌握以上内容后,你可以为自己的 Akka.NET 服务建立完整的 liveness/readiness 双探针体系:用WithActorSystemLivenessCheck兜底进程活性、用WithAkkaClusterReadinessCheck控制集群流量接入、用持久化检查保障存储连通性,再以委托或 DI 式WithHealthCheck补上针对关键 actor 的业务级探测——全部自动纳入微软健康检查框架的调度、超时与标签过滤体系。
- 后端
- 微服务
【免费下载链接】akka.net
Canonical actor model implementation for .NET with local + distributed actors in C# and F#.
相关推荐
GyroFlow免费开源视频防抖神器:告别画面抖动,专业级稳定效果轻松实现
GyroFlow免费开源视频防抖神器:告别画面抖动,专业级稳定效果轻松实现 你是否曾为拍摄的视频画面抖动而烦恼?无论是手持拍摄的日常vlog、运动相机记录下的极
视频处理桌面应用音视频终极指南:Kubernetes健康检查探针深度解析与实战配置
终极指南:Kubernetes健康检查探针深度解析与实战配置 Kubernetes健康检查探针是保障容器化应用稳定运行的核心机制,能够自动检测并恢复异常容器,有
文档教程云原生OneUptime容器健康检查终极指南:与Kubernetes探针深度集成
OneUptime容器健康检查终极指南:与Kubernetes探针深度集成 OneUptime作为完整的开源可观测性平台,提供了强大的容器健康检查能力,能够与K
可观测性后端运维前端云原生微服务AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考