Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d18f233
Updated developement scenario
eaba Jan 24, 2022
4f35347
Fix markdown linting
eaba Jan 24, 2022
d77ce45
Fix linting
eaba Jan 24, 2022
39f6951
Fix linting
eaba Jan 24, 2022
e8e2ae1
Fix linting
eaba Jan 24, 2022
4d0d53a
Fix linting
eaba Jan 24, 2022
c05fe2b
Fix linting
eaba Jan 24, 2022
6a3239d
Merge branch 'dev' into developement_scenario_docs
eaba Jan 24, 2022
9b258ef
Merge branch 'dev' into developement_scenario_docs
eaba Jan 24, 2022
5322eee
Improve `toc.yml` for all sections
eaba Jan 24, 2022
6076345
Console deployment
eaba Jan 25, 2022
131ea83
Sleep for 5 seconds
eaba Jan 25, 2022
3366beb
Add ASP.NET Core page
eaba Jan 25, 2022
32a90c9
Fix startup issue
eaba Jan 25, 2022
14078e9
Fix typo
eaba Jan 25, 2022
cd8d012
Fix linting issue
eaba Jan 25, 2022
19dc1a1
Fix trailing space
eaba Jan 25, 2022
1de4307
Merge branch 'dev' into developement_scenario_docs
eaba Jan 25, 2022
c5f79a8
Added Headless Service
eaba Jan 25, 2022
cc3dd6f
Merge branch 'dev' into developement_scenario_docs
eaba Jan 25, 2022
cf1b246
Fixed typo
eaba Jan 25, 2022
34b6472
Merge branch 'developement_scenario_docs' of https://github.com/eaba/…
eaba Jan 25, 2022
7c85315
Fix linting
eaba Jan 25, 2022
a6d73f9
* Resolves https://github.com/akkadotnet/akka.net/pull/5533/#discussi…
eaba Jan 25, 2022
def2eaa
Fix linting
eaba Jan 25, 2022
01904db
Merge branch 'dev' into developement_scenario_docs
eaba Feb 9, 2022
a94324a
Merge branch 'dev' into developement_scenario_docs
Aaronontheweb Feb 9, 2022
e6675d9
update solution projects
eaba Feb 18, 2022
1c02a80
Merge branch 'dev' into developement_scenario_docs
eaba Feb 18, 2022
dfa250e
Merge branch 'dev' into developement_scenario_docs
eaba Feb 18, 2022
cb97f84
Fix invalid path
eaba Feb 18, 2022
8c7d9f2
Solution project auto updated itself
eaba Feb 18, 2022
7468b44
Merge branch 'dev' into developement_scenario_docs
Aaronontheweb Feb 23, 2022
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
389 changes: 389 additions & 0 deletions docs/articles/intro/deployment-scenarios.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,389 @@
---
uid: deployment-scenarios
title: Deployment Scenarios
---
# Deployment Scenarios

## Console Application

```csharp
PM> install-package Akka
PM> install-package Akka.Remote
```

```csharp
using Akka;
using Akka.Actor;
using Akka.Configuration;

namespace Foo.Bar
{
class Program
{
static void Main(string[] args)
{
using (var system = ActorSystem.Create("my-actor-server"))
{
//start two services
var service1= system.ActorOf(ServiceOne.Prop(), "service1");
var service2 = system.ActorOf(ServiceTwo.Prop(), "service2");
Console.ReadKey();
}
}
}
}
```

## ASP.NET Core

When deploying Akka.NET in ASP.NET Core, one major concern is how to expose `actor` in ASP.NET Core controllers. We will design an `interface` for this!

```csharp
public interface IActorBridge
{
void Tell(object message);
Task Ask<T>(object message);
}
```

### Host Akka.NET with `IHostedService`

With the `IActorBridge` created, next is to host Akka.NET with `IHostedService` which will also implement the `IActorBridge`:

```csharp
using Akka.Actor;
using Akka.DependencyInjection;

namespace WebApplication1
{
public class AkkaService: IHostedService, IActorBridge
{
private ActorSystem _actorSystem;
private readonly IConfiguration _configuration;
private readonly IServiceProvider _serviceProvider;
private IActorRef _actorRef;

private readonly IHostApplicationLifetime _applicationLifetime;

public AkkaService(IServiceProvider serviceProvider, IHostApplicationLifetime appLifetime, IConfiguration configuration)
{
_serviceProvider = serviceProvider;
_applicationLifetime = appLifetime;
_configuration = configuration;
}

public async Task StartAsync(CancellationToken cancellationToken)
{

var bootstrap = BootstrapSetup.Create();


// enable DI support inside this ActorSystem, if needed
var diSetup = DependencyResolverSetup.Create(_serviceProvider);

// merge this setup (and any others) together into ActorSystemSetup
var actorSystemSetup = bootstrap.And(diSetup);

// start ActorSystem
_actorSystem = ActorSystem.Create("akka-system", actorSystemSetup);

_actorRef = _actorSystem.ActorOf(MyActor.Prop());

// add a continuation task that will guarantee shutdown of application if ActorSystem terminates
await _actorSystem.WhenTerminated.ContinueWith(tr => {
_applicationLifetime.StopApplication();
});
}

public async Task StopAsync(CancellationToken cancellationToken)
{
// strictly speaking this may not be necessary - terminating the ActorSystem would also work
// but this call guarantees that the shutdown of the cluster is graceful regardless
await CoordinatedShutdown.Get(_actorSystem).Run(CoordinatedShutdown.ClrExitReason.Instance);
}

public void Tell(object message)
{
_actorRef.Tell(message);
}

public async Task<T> Ask<T>(object message)
{
return await _actorRef.Ask<T>(message);
}
}
}

```

Typically you use a very lightweight `ActorSystem` inside ASP.NET applications, and offload heavy-duty work to a separate Windows Service via Akka.Remote / Akka.Cluster.

### Interaction Between Controllers and Akka.NET

In the sample below, we use an Web API Controller:

```csharp
[ApiController]
[Route("[controller]")]
public class WriteApiController : ControllerBase
{

private readonly ILogger<WriteApiController> _logger;
private readonly IActorBridge _bridge;

public WriteApiController(ILogger<WriteApiController> logger, IActorBridge bridge)
{
_logger = logger;
_bridge = _bridge;
}

[HttpPost]
[Route("post")]
public async Task<IActionResult> Post([FromBody] object data)
{
_bridge.Tell(data);
//you can use Ask if you need response from the Actor
//await _bridge.Ask<T>(data);
return Ok();
}
}
```

### Wire up Akka.NET and ASP.NET Core in `Startup.cs`

We need to replace the default `IHostedService` with `AkkaService` and also inject `IActorBridge` and we do that with `Startup.cs`

```csharp
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{

services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Akka.NET", Version = "v1" });
});

// creates instance of IPublicHashingService that can be accessed by ASP.NET
services.AddSingleton<IActorBridge, AkkaService>();

// starts the IHostedService, which creates the ActorSystem and actors
services.AddHostedService<AkkaService>(sp => (AkkaService)sp.GetRequiredService<IActorBridge>());
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Akka.Net v1"));
}

app.UseRouting();

//app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
```

Visit our site's blog post for [Best Practices for Integrating Akka.NET with ASP.NET Core and SignalR](https://petabridge.com/blog/akkadotnet-aspnetcore/)

## Windows Service
Copy link
Member

Choose a reason for hiding this comment

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

Emphasize Headless services, not Windows Services - Headless Service is the deployment class, Windows Service is a specific way of running and managing them.

Installing a Headless Service as a Windows Service should be mentioned in passing but there's abundant literature on how to do that inside Microsoft's documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted


The quickest way to get started with deploying Akka.NET as Windows Service is by creating a `BackgroundService` - [learn more](https://docs.microsoft.com/en-us/dotnet/core/extensions/windows-service).
Copy link
Member

Choose a reason for hiding this comment

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

I would just stick to recommending IHostedService here as that's what is more maintstream.

I'd also embed the video from and link to https://petabridge.com/blog/akkadotnet-ihostedservice/

Which would look like this:

### Program.cs

```csharp
Copy link
Member

Choose a reason for hiding this comment

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

we need DocFx references here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Where will the code live? The examples folder?

using Backgroundservice;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = ".NET Joke Service";
})
.ConfigureServices(services =>
{
services.AddHostedService<AkkaService>();
services.AddHttpClient<JokeService>();
})
.Build();

await host.RunAsync();
```

### AkkaService.cs

```csharp
public sealed class AkkaService : BackgroundService
{
private readonly ILogger<AkkaService> _logger;
private readonly JokeService _jokeService;
private readonly ActorSystem _actorSystem;
private readonly IActorRef _actorRef;

public AkkaService(JokeService jokeService, ILogger<AkkaService> logger, IServiceProvider serviceProvider)
{
_jokeService = jokeService;
_logger = logger;
var bootstrap = BootstrapSetup.Create();


// enable DI support inside this ActorSystem, if needed
var diSetup = DependencyResolverSetup.Create(serviceProvider);

// merge this setup (and any others) together into ActorSystemSetup
var actorSystemSetup = bootstrap.And(diSetup);

// start ActorSystem
_actorSystem = ActorSystem.Create("akka-system", actorSystemSetup);
_actorRef = _actorSystem.ActorOf(MyActor.Prop());
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
string joke = await _jokeService.GetJokeAsync();
_actorRef.Tell(joke);
_logger.LogWarning(joke);

await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
```

### JokeService.cs

```csharp
public class JokeService
{
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _options = new()
{
PropertyNameCaseInsensitive = true
};

private const string JokeApiUrl =
"https://karljoke.herokuapp.com/jokes/programming/random";

public JokeService(HttpClient httpClient) => _httpClient = httpClient;

public async Task<string> GetJokeAsync()
{
try
{
// The API returns an array with a single entry.
Joke[]? jokes = await _httpClient.GetFromJsonAsync<Joke[]>(
JokeApiUrl, _options);

Joke? joke = jokes?[0];

return joke is not null
? $"{joke.Setup}{Environment.NewLine}{joke.Punchline}"
: "No joke here...";
}
catch (Exception ex)
{
return $"That's not funny! {ex}";
}
}
}

public record Joke(int Id, string Type, string Setup, string Punchline);
```

This is of course, one way of deploying Akka.NET HEADLESS Service. Our blog post [How to Build Headless Akka.NET Services with IHostedService](https://petabridge.com/blog/akkadotnet-ihostedservice/) is a good alternative!
Copy link
Member

Choose a reason for hiding this comment

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

Ah good, you did link to it down here. Consider using a DocFx note block for this https://dotnet.github.io/docfx/spec/docfx_flavored_markdown.html#note-warningtipimportant


## Azure PaaS Worker Role
Copy link
Member

Choose a reason for hiding this comment

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

Azure PaaS is pretty ancient. I'd push this way down on the list of options.


The following sample assumes that you have created a new Azure PaaS Cloud Service that contains a single
empty Worker Role. The Cloud Service project templates are added to Visual Studio by installing the
[Azure .Net SDK](http://azure.microsoft.com/en-gb/downloads/).

The Worker Role implementation can be tested locally using the Azure Compute Emulator before deploying to the cloud. The MSDN Azure article ["Using Emulator Express to Run and Debug a Cloud Service Locally"](https://msdn.microsoft.com/en-us/library/azure/dn339018.aspx) describes this in more detail.

The Azure PaaS Worker Role implementation is very similar to the [Windows Service](#windows-service).
The quickest way to get started with Akka.Net is to create a simple Worker Role which invokes the top-level
user-actor in the RunAsync() method, as follows:

### WorkerRole.cs

```csharp
using Akka.Actor;

namespace MyActorWorkerRole
{
public class WorkerRole : RoleEntryPoint
{
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);

private ActorSystem _actorSystem;

public override bool OnStart()
{
// Setup the Actor System
_actorSystem = ActorSystem.Create("MySystem");

return (base.OnStart());
}

public override void OnStop()
{
this.cancellationTokenSource.Cancel();
this.runCompleteEvent.WaitOne();

// Shutdown the Actor System
_actorSystem.Shutdown();

base.OnStop();
}

public override void Run()
{
try
{
this.RunAsync(this.cancellationTokenSource.Token).Wait();
}
finally
{
this.runCompleteEvent.Set();
}
}

private async Task RunAsync(CancellationToken cancellationToken)
{
// Create an instance to the top-level user Actor
var workerRoleActor = _actorSystem.ActorOf<WorkerRoleActor>("WorkerRole");

// Send a message to the Actor
workerRoleActor.Tell(new WorkerRoleMessage("Hello World!"));

while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(1000, cancellationToken);
}
}
}
}
```
Loading