Skip to content

make AkkaHostedService public + virtual #306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,25 @@ namespace Akka.Hosting
where T : Akka.Actor.IExtensionId { }
public Akka.Hosting.AkkaConfigurationBuilder WithExtensions(params System.Type[] extensions) { }
}
[Akka.Annotations.InternalApi]
public class AkkaHostedService : Microsoft.Extensions.Hosting.IHostedService
{
protected Akka.Actor.ActorSystem? ActorSystem;
protected readonly Akka.Hosting.AkkaConfigurationBuilder ConfigurationBuilder;
protected Akka.Actor.CoordinatedShutdown? CoordinatedShutdown;
protected readonly Microsoft.Extensions.Hosting.IHostApplicationLifetime? HostApplicationLifetime;
protected readonly Microsoft.Extensions.Logging.ILogger<Akka.Hosting.AkkaHostedService> Logger;
protected readonly System.IServiceProvider ServiceProvider;
public AkkaHostedService(Akka.Hosting.AkkaConfigurationBuilder configurationBuilder, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILogger<Akka.Hosting.AkkaHostedService> logger, Microsoft.Extensions.Hosting.IHostApplicationLifetime? applicationLifetime) { }
public virtual System.Threading.Tasks.Task StartAsync(System.Threading.CancellationToken cancellationToken) { }
public virtual System.Threading.Tasks.Task StopAsync(System.Threading.CancellationToken cancellationToken) { }
}
public static class AkkaHostingExtensions
{
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAkka(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string actorSystemName, System.Action<Akka.Hosting.AkkaConfigurationBuilder> builder) { }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAkka(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string actorSystemName, System.Action<Akka.Hosting.AkkaConfigurationBuilder, System.IServiceProvider> builder) { }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAkka<T>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string actorSystemName, System.Action<Akka.Hosting.AkkaConfigurationBuilder, System.IServiceProvider> builder)
where T : Akka.Hosting.AkkaHostedService { }
public static Akka.Hosting.AkkaConfigurationBuilder AddHocon(this Akka.Hosting.AkkaConfigurationBuilder builder, Akka.Configuration.Config hocon, Akka.Hosting.HoconAddMode addMode) { }
public static Akka.Hosting.AkkaConfigurationBuilder AddHocon(this Akka.Hosting.AkkaConfigurationBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration, Akka.Hosting.HoconAddMode addMode) { }
public static Akka.Hosting.AkkaConfigurationBuilder AddHoconFile(this Akka.Hosting.AkkaConfigurationBuilder builder, string hoconFilePath, Akka.Hosting.HoconAddMode addMode) { }
Expand Down
53 changes: 30 additions & 23 deletions src/Akka.Hosting/AkkaHostedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
Expand All @@ -11,36 +12,42 @@ namespace Akka.Hosting
/// <summary>
/// INTERNAL API
/// </summary>
internal sealed class AkkaHostedService : IHostedService
/// <remarks>
/// Open for modification in cases where users need fine-grained control over <see cref="Actor.ActorSystem"/> startup and
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning to users: there will be tears if you don't study our source code when you try to extend this, and we will not help you with whatever weird DI stuff you're trying to do. Best of luck.

/// DI - however, extend at your own risk. Look at the Akka.Hosting source code for ideas on how to extend this.
/// </remarks>
[InternalApi]
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
public class AkkaHostedService : IHostedService
{
private ActorSystem? _actorSystem;
private CoordinatedShutdown? _coordinatedShutdown; // grab a reference to CoordinatedShutdown early
private readonly IServiceProvider _serviceProvider;
private readonly AkkaConfigurationBuilder _configurationBuilder;
private readonly IHostApplicationLifetime? _hostApplicationLifetime;
private readonly ILogger<AkkaHostedService> _logger;
protected ActorSystem? ActorSystem;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made all of these accessible in derivative classes.

protected CoordinatedShutdown? CoordinatedShutdown; // grab a reference to CoordinatedShutdown early
protected readonly IServiceProvider ServiceProvider;
protected readonly AkkaConfigurationBuilder ConfigurationBuilder;
protected readonly IHostApplicationLifetime? HostApplicationLifetime;
protected readonly ILogger<AkkaHostedService> Logger;

public AkkaHostedService(AkkaConfigurationBuilder configurationBuilder, IServiceProvider serviceProvider,
ILogger<AkkaHostedService> logger, IHostApplicationLifetime? applicationLifetime)
{
_configurationBuilder = configurationBuilder;
_hostApplicationLifetime = applicationLifetime;
_serviceProvider = serviceProvider;
_logger = logger;
ConfigurationBuilder = configurationBuilder;
HostApplicationLifetime = applicationLifetime;
ServiceProvider = serviceProvider;
Logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
public virtual async Task StartAsync(CancellationToken cancellationToken)
{
try
{
_actorSystem = _serviceProvider.GetRequiredService<ActorSystem>();
_coordinatedShutdown = CoordinatedShutdown.Get(_actorSystem);
await _configurationBuilder.StartAsync(_actorSystem);
ActorSystem = ServiceProvider.GetRequiredService<ActorSystem>();
CoordinatedShutdown = CoordinatedShutdown.Get(ActorSystem);
await ConfigurationBuilder.StartAsync(ActorSystem);

async Task TerminationHook()
{
await _actorSystem.WhenTerminated.ConfigureAwait(false);
_hostApplicationLifetime?.StopApplication();
await ActorSystem.WhenTerminated.ConfigureAwait(false);
HostApplicationLifetime?.StopApplication();
}

// terminate the application if the Sys is terminated first
Expand All @@ -51,22 +58,22 @@ async Task TerminationHook()
}
catch (Exception ex)
{
_logger.Log(LogLevel.Critical, ex, "Unable to start AkkaHostedService - shutting down application");
_hostApplicationLifetime?.StopApplication();
Logger.Log(LogLevel.Critical, ex, "Unable to start AkkaHostedService - shutting down application");
HostApplicationLifetime?.StopApplication();
}
}

public async Task StopAsync(CancellationToken cancellationToken)
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// ActorSystem may have failed to start - skip shutdown sequence if that's the case
// so error message doesn't get conflated.
if (_coordinatedShutdown == null)
if (CoordinatedShutdown == null)
{
return;
}

// run full CoordinatedShutdown on the Sys
await _coordinatedShutdown.Run(CoordinatedShutdown.ClrExitReason.Instance)
await CoordinatedShutdown.Run(CoordinatedShutdown.ClrExitReason.Instance)
.ConfigureAwait(false);
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/Akka.Hosting/AkkaHostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public static IServiceCollection AddAkka(this IServiceCollection services, strin
/// and manages its lifecycle in accordance with Akka.NET best practices.
/// </remarks>
public static IServiceCollection AddAkka(this IServiceCollection services, string actorSystemName, Action<AkkaConfigurationBuilder, IServiceProvider> builder)
{
return AddAkka<AkkaHostedService>(services, actorSystemName, builder);
}

public static IServiceCollection AddAkka<T>(this IServiceCollection services, string actorSystemName, Action<AkkaConfigurationBuilder, IServiceProvider> builder) where T:AkkaHostedService
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the generic code is identical to the non-generic one, we should just call the generic one from inside the non-generic extension method. Saves us the time to have to check that both implementation are the same in the future.

{
var b = new AkkaConfigurationBuilder(services, actorSystemName);
services.AddSingleton<AkkaConfigurationBuilder>(sp =>
Expand All @@ -72,7 +77,7 @@ public static IServiceCollection AddAkka(this IServiceCollection services, strin
else
{
// start the IHostedService which will run Akka.NET
services.AddHostedService<AkkaHostedService>();
services.AddHostedService<T>();
}

return services;
Expand Down