sankarsana / nyactor

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

NYActor - Not yet another actor system

Slim and super-fast actor system for dotnet

Benchmark

  • Handles ~500k rps on 8-core AMD Ryzen 2700x in-memory
  • Handles ~20k rps on 8-core AMD Ryzen 2700x over aspnet core REST

Roadmap

  • Multi-node mode with DHT and DNS discovery to build distributed high-scale apps

Known issues

  • Supports single-node mode only for now

Quick start

  • Install nuget packages
dotnet add package NYActor.Core
dotnet add package NYActor.EventStore

Create your first in-memory Actor

public class SampleActor : Actor
{
    private readonly Random _random = new Random();

    public Task Nope()
    {
        return Task.CompletedTask;
    }

    public Task<int> Random()
    {
        return Task.FromResult(_random.Next());
    }
}

Initialize actor system

var node = new Node()
    .RegisterActorsFromAssembly(typeof(SampleActor).Assembly);

Call actor method

await node
.GetActor<SampleActor>()
.InvokeAsync(e => e.Nope());
var randomValue = await node
    .GetActor<SampleActor>()
    .InvokeAsync(e => e.Random());

Event sourcing and NYActor

Configure dependency injection

var userCredentials = new UserCredentials("admin", "changeit");
var connectionSettingsBuilder = ConnectionSettings.Create()
    .SetDefaultUserCredentials(userCredentials)
    .Build();

var es = EventStoreConnection.Create(connectionSettingsBuilder,
    new IPEndPoint(IPAddress.Any, 1113));

await es.ConnectAsync();

using var node = new Node()
    .ConfigureInjector(e => { e.RegisterInstance(es); })
    .RegisterActorsFromAssembly(typeof(SampleActor).Assembly);

Create an eventsourced actor

public class SampleEventSourcedActor :
    EventStorePersistedActor<SampleEventSourcedState>
{
    public SampleEventSourcedActor(IEventStoreConnection eventStoreConnection) :
        base(eventStoreConnection)
    {
    }

    public async Task Append()
    {
        var @event = new SampleEvent(DateTime.UtcNow);
        await ApplyEventAsync(@event);
    }

    public Task<int> GetInfo()
    {
        var res = State.TotalEvents;
        return Task.FromResult(res);
    }
}

Create a state class

public class SampleEventSourcedState : IApplicable
{
    public int TotalEvents { get; private set; }

    public SampleEventSourcedState()
    {
        TotalEvents = 0;
    }

    public void Apply(object ev)
    {
        TotalEvents++;
    }
}

Define event

public class SampleEvent
{
    public DateTime EventAt { get; }

    public SampleEvent(DateTime eventAt)
    {
        EventAt = eventAt;
    }
}

Advanced actors

Returning back to actor after external job

public class SampleActor : Actor
{
    public Task Nope()
    {
        Task.Run(async () =>
        {
            var minutesToWork = 5;

            await Task.Delay(TimeSpan.FromMinutes(minutesToWork));

            await this.Self().InvokeAsync(e => e.CompleteLongOp(minutesToWork));
        });

        return Task.CompletedTask;
    }

    public Task CompleteLongOp(int result)
    {
        // ...

        return Task.CompletedTask;
    }
}

Creating a second actor inside a first one

public class SampleActor : Actor
{
    public async Task Nope()
    {
        await this.System()
            .GetActor<SampleActor>(Key + "-child")
            .InvokeAsync(e => e.ChildAction());
    }

    public Task ChildAction()
    {
        // ...
        return Task.CompletedTask;
    }
}

Customizing environment

Override actor deactivation interval

var node = new Node()
    .OverrideDefaultDeactivationInterval(TimeSpan.FromSeconds(30))
    .RegisterActorsFromAssembly(typeof(SampleActor).Assembly);

About


Languages

Language:C# 100.0%