Search code examples
c#azureazure-worker-rolesakka.net

How to configure a remote actor configuration in an Azure worker role (AKKA.NET)


I've been struggling for the past 2 days trying to figure out exactly how to deploy a basic console application into an Azure worker role and have it be accessible remotely from some sort of client based application e.g. MVC web app. Answer below


Solution

  • I started by having my Actor state contained in local console application that my MVC app was communicating with. I wished to deploy my application with the Azure ecosystem, and thought that having the state maintained within a worker role, with a think "client" MVC app hosted in an app service would be the best way forward.

    Ensure that your Actor system is extracted out of your Solution into it's own project. Create a new Worker role CloudService project within your solution.

    Azure Cloud Service Project Make sure you select worker role Select Worker Role

    I configured my WorkRole as follows:

    public class WorkerRole : RoleEntryPoint
    {
        private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);
        private static ActorSystem ActorSystemInstance;
        public override void Run()
        {
            Trace.TraceInformation("Game.State.WorkerRole is running");
    
            try
            {
                this.RunAsync(this.cancellationTokenSource.Token).Wait();
            }
            finally
            {
                this.runCompleteEvent.Set();
            }
        }
    
        public override bool OnStart()
        {                                                                                  
            ActorSystemInstance = ActorSystem.Create("GameSystem");            
            // Set the maximum number of concurrent connections
            ServicePointManager.DefaultConnectionLimit = 12;
    
            // For information on handling configuration changes
            // see the MSDN topic at https://go.microsoft.com/fwlink/?LinkId=166357.
    
            bool result = base.OnStart();
    
            Trace.TraceInformation("Game.State.WorkerRole has been started");
    
            return result;
        }
    
        public override void OnStop()
        {
            ActorSystemInstance.Terminate();
            Trace.TraceInformation("Game.State.WorkerRole is stopping");
    
            this.cancellationTokenSource.Cancel();
            this.runCompleteEvent.WaitOne();
    
            base.OnStop();
    
            Trace.TraceInformation("Game.State.WorkerRole has stopped");
        }
    
        private async Task RunAsync(CancellationToken cancellationToken)
        {
            var gameController = ActorSystemInstance.ActorOf<GameControllerActor>("GameController");
    
            while (!cancellationToken.IsCancellationRequested)
            {
                Trace.TraceInformation("Working");
                await Task.Delay(1000);
            }
        }
    }
    

    And my HOCON file (within the app.config) as follows:

    <akka>
    <hocon>
      <![CDATA[
      akka {
        loglevel = DEBUG
    
        actor {
          provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
          debug {
            receive = on
            autoreceive = on
            lifecycle = on
            event-stream = on
            unhandled = on
          }
        }
    
    
        remote {
          helios.tcp {
            transport-class = "Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"  
            transport-protocol = tcp
            enforce-ip-family = true
            port = xxxx //the port configured in your worker role endpoint
            hostname = "0.0.0.0" //This is the local hostname of the worker role, using 0.0.0.0 will set the Remote actor to "listen" on all available DNS/IP addresses including the loopback (127.0.0.1)
            pulic-hostname = "xx.xx.xx.xx" //IP Address OR DNS name, but whatever is set here is what MUST be used in the Actor Selector path on the client in order for proper "association" to occur. I did find that DNS name was required for my application as I was using SignalR as a bridge between the Actor system and the web client.
    
          }
        }
      }
      ]]>
    </hocon>
    

    We need to define our endpoints within the worker role configuration, so that we "expose" a port through which we can communicate with the outside world. So go to your WorkerRole settings.

    Endpoint Configuration

    Once the Worker Role is deployed, you should be able to confirm that the port is open and available by telnet'ing into the server via it's IP and the port you configured previously.

    The most important part with setting our ActorSelection from our client is that the IP/DNS address below MUST match with the IP/DNS set in the public-hostname setting in the HOCON config within the worker role

     ActorReferences.GameController =
                ActorSystem.ActorSelection("akka.tcp://GameSystem@xx.xxx.xxx.xxx:8091/user/GameController")
                    .ResolveOne(TimeSpan.FromSeconds(3))
                    .Result;
    

    For completeness here is my client HOCON config:

    akka {
        loglevel = OFF
    
        actor {
          provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
          debug {
            receive = on
            autoreceive = on
            lifecycle = on
            event-stream = on
            unhandled = on
          }
        }
    
    
        remote {
          helios.tcp {
            transport-class = "Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"  
            transport-protocol = tcp
            enforce-ip-family = true
            port = 0 //A port will be provided for us... not important as we won't be calling into the client externally
            public-hostname = "yoursitename.azurewebsites.net" //Remember this is simply the DNS name of the client machine
            hostname = "127.0.0.1"
          }
        }
      }
    

    I really hope this helps someone else out there. I've really not found much documentation describing an Akka.NET deployment to Azure (without having the Actor system deployed to a volatile IIS app service). Let me know if I can improve the answer in any way.