Azure Service Fabric: Creating an Actor Framework Service

In my previous post I discussed about what is service fabric the different programming models and about system services. In this post, we will be looking at how to use Service Fabric in an IoT scenario. The application will be tracking in real time the state of an aircraft and in case of any anomaly will allow us to raise a ticket in the back-end system like a CRM system. The application will consist of stateless services which will pull events from the sensors, a stateless gateway service used by the web client to view the state of the aircraft and also to raise and track request and finally the system will have an actor service to maintain the state of individual aircraft.

In service fabric, an actor service is based on top of the stateful service. So you get access to the ReliableCollections to maintain the state of the service. Reliable Collection classes are available under the Microsoft.ServiceFabric.Data.Collections namespace and they expose a set of APIs which can be used to store the data. Service Fabric generally provides Dictionary and Queue as part of the Reliable Collection.

Reliable Collections out of the box takes care of data replication for high availability, data partitioning for scalability and you also get transaction capabilities. The data is stored in the memory and then serialized using the .Net’s DataContractSerializer and persisted to the local disk of the node. As data is stored locally, all reads and writes incur low latency and high throughput. ReliableCollection also ensures firm consistency of data by providing that transaction commits once data is replicated to a minimum number of replicas. In case of service with five replicas transaction gets committed once data is replicated across a minimum of 3 replicas.

The code inside the actor service runs in a single thread. Whenever a call for a particular actor service comes, the runtime creates a new instance, if it does not exist already. The lifetime of the actor service instance is not tied together with the actor state. Like other .Net objects when all reference to the actor instance is removed the object is garbage collected. However, the state remains and whenever the actor is created again the state is reloaded.

Now we will move to Visual Studio and create a new Service Fabric project. If the Service Fabric SDK is installed in your dev machine, you should be able to see the service fabric project template under the cloud section.

Creating an Actor Framework Service

Service Fabric Project Template

 

Creating an Actor Framework Service

Service Fabric Services Dialog

Under the Service Fabric Service Dialog, you can select the type of project you are looking for. This will show you the entire range of programming models available in Service Fabric. You can see that Service Fabric supports both .Net Framework and .Net Core as well as containers. We will be using an actor service based on .Net core.

 

Creating an Actor Framework Service

Service Fabric Solution Explorer

The solution will have a primary Service Fabric Project and two projects for the Actor Service. The service fabric project consists of the manifest file which contains sections for specifying the number of instances for each service and it takes care of creating the instances of the services when the solution is deployed in a cluster. You also get sections for storing application parameters and a section for the published profile. The application parameter files are also the place where you can specify the number of instances of each service which Service fabric should create.

The Actor service consists of an actor project and actor interface. The interface will be used by other services who will consume this actor service. Under the actor project the Program.cs file contains the main method which registers the actor service.

Creating an Actor Framework Service

The FlightActor.cs is the main actor class which is the entry point for the application. The constructor for the actor service expects a parameter called actorId. The actorId is used to identify a particular instance of an actor. In our case, the actor service will be used to store data from sensors present in individual aircraft. We will be creating as many actor services as many numbers of aircraft are added. In that case, the actorId is used to identify an individual instance of the actor which is holding the state of a particular aircraft.

The state persistence attribute specifies the kind of data persistence which will be used. There are three different types.

  • Persisted: Data is serialized and written to the disk and then replicated across multiple replicas so that a minimum number of a quorum is reached. Data in the persisted state can survive an entire cluster outage as well.
  • Volatile: Data is maintained in the memory and replicated across replicas. Data in the Volatile state can survive during service upgrades, resource load balancing, and node failure. However, in case all the replicas go down the data will be lost.
  • No Persisted State: In this case, the data is not replicated or stored using the reliable collections. This should be used for actors who use external storage for storing the state.

With that let’s move to the Actor Interface Project and in the IFlightActor.cs file specify our new method contract

Task AddSensorData(DateTime dateinUtc, AircraftSensorModel sensorDataModel);
Task<List<AircraftSensorModel>> GetSensorData();
Task ResetData(); 

and here is our model, I am using a struct to store the sensor data

public struct AircraftSensorModel
{
public int AircraftId { get; set; }
public int DeviceId { get; set; }
public double Temperature { get; set; }
public double Humidity { get; set; }
public int EngineRPM { get; set; }
}

Next, we will move to the FlightActor.cs class and will implement the methods.

public async Task AddSensorData(DateTime date, AircraftSensorModel sensorDataModel)
{
await StateManager.AddOrUpdateStateAsync(date.ToLongTimeString(),sensorDataModel,null);
await checkTempAnomalyAsync(); 
} public async Task<List<AircraftSensorModel>> GetSensorData()
{
var result = new List<AircraftSensorModel>();
var states = await StateManager.GetStateNamesAsync();
foreach(var state in states)
{
result.Add(await StateManager.GetStateAsync<AircraftSensorModel>(state));
}
return result;
} public async Task ResetData()
{
await StateManager.ClearCacheAsync();
}

As you can see above, we are using the AddOrUpdateStateAsync method to insert the time of the event and the sensor value as a key-value pair. While retrieving the data we are first getting the all the keys and then enumerating through each one of them to return the sensor data.

Next we will implement the functionality for detecting temperature anomaly


private async Task checkTempAnomalyAsync()
{
      double avgTemp = await fetchLastnrecordTempAvg(size);
      if (avgTemp > 90 || avgTemp < 20)
      {
          //ToDo raiseAlert
      }
}

private async Task fetchLastnrecordTempAvg(int size)
{
     double count=0;
     List result = new List();
     var states = await StateManager.GetStateNamesAsync();
     var topnstates = states.TakeLast(size);
     foreach (var topstate in topnstates)
     {
         var sensorModel = await StateManager.GetStateAsync(topstate);
         count += sensorModel.Temperature;
     }
    return count/size;
}

As data from our sensors will be sent once every minute the we will detect the average temperature in the last 10 minutes.  If the average value is not within a safe limit then either you can call another service to raise and alert or push the alert notification to another event hub from where it can be processed.

Our actor code is now complete. An EventProcessor service will consume the actor service. In our case, this service will connect to an IoT hub and pull the sensor data and then push it to the actor service. The services will be talking to each other using service remoting.

In our next post, we will be creating our Event processor, and we will look at how to call an actor service using the service remoting feature of Service Fabric.

Disclaimer: The Questions and Answers provided on https://www.gigxp.com are for general information purposes only. We make no representations or warranties of any kind, express or implied, about the completeness, accuracy, reliability, suitability or availability with respect to the website or the information, products, services, or related graphics contained on the website for any purpose.

Related Post

Sumanta Kar

A Software Developer by profession working on Azure and .Net for more than 4 years. Passionate about architecture, design and latest technologies. A passionate football fan and a gamer at other times.