Files
scadalink-design/AkkaDotNet/11-Hosting.md
Joseph Doherty de636b908b Add Akka.NET reference documentation
Notes and documentation covering actors, remoting, clustering, persistence,
streams, serialization, hosting, testing, and best practices for the Akka.NET
framework used throughout the ScadaLink system.
2026-03-16 09:08:17 -04:00

196 lines
6.8 KiB
Markdown

# 11 — Akka.Hosting
## Overview
Akka.Hosting is the recommended integration layer between Akka.NET and the `Microsoft.Extensions.*` ecosystem (Hosting, DependencyInjection, Configuration, Logging). It replaces the traditional HOCON-only configuration approach with a code-first, type-safe API and provides an `ActorRegistry` for injecting actor references into non-actor services.
In our SCADA system, Akka.Hosting is the entry point for bootstrapping the entire ActorSystem — configuring Remoting, Cluster, Singleton, Persistence, and all other modules through a fluent C# API that integrates with the standard .NET Generic Host.
## When to Use
- Always — Akka.Hosting should be the primary way to configure and start Akka.NET in any new .NET 10 application
- Configuring all Akka.NET modules (Remoting, Cluster, Persistence, etc.) in code rather than HOCON files
- Registering top-level actors in the `ActorRegistry` for injection into ASP.NET controllers, Windows Services, or other DI consumers
- Managing ActorSystem lifecycle tied to the .NET host lifecycle
## When Not to Use
- There are no scenarios where you should avoid Akka.Hosting in new projects on .NET 6+
- Legacy projects on older .NET may need the traditional HOCON approach if they cannot adopt `Microsoft.Extensions.Hosting`
## Design Decisions for the SCADA System
### Windows Service Host
The SCADA application runs as a Windows Service using the .NET Generic Host:
```csharp
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddAkka("scada-system", (akkaBuilder, sp) =>
{
var siteConfig = sp.GetRequiredService<SiteConfiguration>();
akkaBuilder
.ConfigureLoggers(loggers =>
{
loggers.LogLevel = Akka.Event.LogLevel.InfoLevel;
loggers.AddLoggerFactory(); // Bridge to Microsoft.Extensions.Logging
})
.WithRemoting(options =>
{
options.Hostname = "0.0.0.0";
options.PublicHostname = siteConfig.NodeHostname;
options.Port = 4053;
})
.WithClustering(new ClusterOptions
{
Roles = new[] { "scada-node" },
SeedNodes = siteConfig.SeedNodes
})
.WithSingleton<DeviceManagerActor>("device-manager",
(system, registry, resolver) => resolver.Props<DeviceManagerActor>(),
new ClusterSingletonOptions { Role = "scada-node" })
.WithSingletonProxy<DeviceManagerActor>("device-manager",
new ClusterSingletonProxyOptions { Role = "scada-node" })
.WithDistributedData()
.WithActors((system, registry, resolver) =>
{
var healthActor = system.ActorOf(
resolver.Props<ClusterHealthActor>(), "cluster-health");
registry.Register<ClusterHealthActor>(healthActor);
});
});
var host = builder.Build();
await host.RunAsync();
```
### ActorRegistry for DI Integration
The `ActorRegistry` stores named `IActorRef` instances that can be injected into non-actor services:
```csharp
// Registering actors
registry.Register<DeviceManagerActor>(deviceManagerRef);
registry.Register<ClusterHealthActor>(healthActorRef);
// Consuming in a Windows Service health check endpoint
public class HealthCheckService : IHostedService
{
private readonly IRequiredActor<ClusterHealthActor> _healthActor;
public HealthCheckService(IRequiredActor<ClusterHealthActor> healthActor)
{
_healthActor = healthActor;
}
public async Task<HealthStatus> CheckHealthAsync()
{
var actorRef = await _healthActor.GetAsync();
var status = await actorRef.Ask<ClusterHealthStatus>(new GetHealth(), TimeSpan.FromSeconds(5));
return status.IsHealthy ? HealthStatus.Healthy : HealthStatus.Unhealthy;
}
}
```
### Site-Specific Configuration
Use `Microsoft.Extensions.Configuration` (appsettings.json, environment variables) for site-specific values (hostnames, seed nodes, device lists). Inject these into the Akka configuration:
```json
// appsettings.json
{
"ScadaSite": {
"NodeHostname": "nodeA.scada.local",
"SeedNodes": [
"akka.tcp://scada-system@nodeA.scada.local:4053",
"akka.tcp://scada-system@nodeB.scada.local:4053"
],
"DeviceConfigPath": "C:\\ProgramData\\SCADA\\devices.json"
}
}
```
```csharp
builder.Services.Configure<SiteConfiguration>(
builder.Configuration.GetSection("ScadaSite"));
```
## Common Patterns
### Combining HOCON and Hosting
For module-specific settings not yet covered by Hosting APIs, you can mix HOCON with code-first configuration:
```csharp
akkaBuilder.AddHocon(ConfigurationFactory.ParseString(@"
akka.cluster.split-brain-resolver {
active-strategy = keep-oldest
keep-oldest.down-if-alone = on
stable-after = 15s
}
"), HoconAddMode.Prepend);
```
### Logging Bridge
Bridge Akka.NET's internal logging to `Microsoft.Extensions.Logging` so all logs go through the same pipeline (Serilog, NLog, etc.):
```csharp
akkaBuilder.ConfigureLoggers(loggers =>
{
loggers.ClearLoggers();
loggers.AddLoggerFactory();
});
```
### Coordinated Shutdown Integration
Akka.Hosting automatically ties `CoordinatedShutdown` to the host's lifetime. When the Windows Service stops, the ActorSystem leaves the cluster gracefully and shuts down. No additional configuration is needed.
## Anti-Patterns
### Bypassing Hosting to Create ActorSystem Manually
Do not create `ActorSystem.Create()` alongside Akka.Hosting. This creates a second, unmanaged ActorSystem. Always use `AddAkka()` on the service collection.
### Registering Actors That Don't Exist Yet
The `ActorRegistry` resolves asynchronously (`GetAsync()`), but registering an actor reference that is never created will cause consumers to hang indefinitely. Always register actors in the `WithActors` callback where they are created.
### Hardcoding Configuration
Do not hardcode hostnames, ports, or seed nodes in the `AddAkka` configuration. Use `Microsoft.Extensions.Configuration` so that each site's deployment can override these values via appsettings.json or environment variables.
## Configuration Guidance
### NuGet Packages
```
Akka.Hosting
Akka.Remote.Hosting
Akka.Cluster.Hosting (includes Sharding, Tools)
Akka.Persistence.Hosting
```
These Hosting packages replace the need to install the base packages separately — they include their dependencies.
### Startup Order
Akka.Hosting initializes components in the order they are registered. Register in dependency order:
1. Loggers
2. Remoting
3. Clustering (depends on Remoting)
4. Persistence
5. Distributed Data
6. Singletons and Singleton Proxies (depend on Clustering)
7. Application actors
## References
- GitHub / Documentation: <https://github.com/akkadotnet/Akka.Hosting>
- Petabridge Blog: <https://petabridge.com/blog/akkadotnet-hosting-aspnet/>
- DI with Akka.NET: <https://getakka.net/articles/actors/dependency-injection.html>