Charlie Chaplin in the movie "New Times"About Azure Service Fabric has already written a lot of articles and even books, the benefit of about a year the product was in the preview state. However, on April 1, 2016, without any joke,
Azure Service Fabric finally reached the state of General availability , and there is reason to believe that it will linger here seriously and for a long time. And if so - why not walk through it, if not from an applied one, then at least from academic interest? Moreover, the information on Azure Service Fabric in Russian is clearly not enough.
Why was Azure Service Fabric needed at all? In the software world, there are quite a few servers associated with the ecosystems of a particular language or platform. Historically, in the Java ecosystem, such servers are almost the most - Tomcat, JBoss, WebSphere, etc. Alas, the .NET platform cannot yet boast such a wealth of choice. The only thing that comes to mind is IIS, Azure cloud services and their local Windows Azure Pack twin (not counting
Topshelf type wrappers). Azure Service Fabric is designed to expand this short list in the direction of the currently popular SOA concept and the highly fashionable subconcept of microservices, simplifying the deployment of services and ensuring their scalability and fault tolerance. And after this lyrical digression, we finally go on the offensive.
Galloping across Europe
The main source of knowledge about Azure Service Fabric (hereinafter ASF) is
documentation . I strongly recommend and at the same time I note that the Russian translation lags behind the source text at least in the screenshots, so it’s better to focus on the original. So, let's go - physically, ASF is a cluster of several servers running service instances. Something like that:
')
Drawn with creately.comWhat is the service? According to Wikipedia,
service is defined as “A service is a self-contained unit of functionality,” in other words, it is “a combat unit in itself,” separately and independently deployed and used. In ASF, each such “combat unit” has its own manifest, in which the service name, configuration, end points, etc. are defined. Despite atomicity, the deployment unit is still not a service, but a set of services, called an application — in the application manifest, you can , define service partitioning to maintain scalability, and for fault tolerance in case of failure of ASF servers, each partition can have multiple instances of services. Both services and applications are versioned independently, which allows you to update the application without updating all services in it at once. For a deployed application, ASF creates the required number of service instances, locates on cluster nodes, starts, synchronizes state between instances, re-creates instances for load balancing - for example, in the picture above, Service 1 has an end point to communicate over TCP and in two instances is placed on two nodes Service 2 has outputs on TCP and HTTP and is located on one of the three nodes in one copy, while Service 3 does not provide external communications and runs on one node in one copy. If one of the nodes dies untimely, the ASF rebalances the load on the rest. In general, ensuring the smooth functioning of services is the work of ASF.
Services in ASF are divided into two types - stateful and stateless, in other words, storing their state and not having one. You can use these types by simply spawning your services from them, or you can build more complex structures on their basis — say, ASF Actors technology is itself built on a stateful service. For the first acquaintance with ASF, I use the stateless service as more simple.
There will be a garden city
... but not immediately. To begin with, we set up a development environment.
From here we download and install the desired version of the SDK - you can stand-alone, or you can with elements for integration into Visual Studio. For an integrated version, you need a Visual Studio 2015 or “15” Preview, and the Community option is probably suitable, and the Visual Studio Enterprise 2015 Update 2 was used when writing the article, and everything is guaranteed to work in it. After installing the SDK, the Service Fabric Application appears in the list of project templates (namely, in the Visual C # / Cloud section) and you can finally get down to business - create a new ASF application, call it MyFabric, click OK, select the type of service Stateless, call MyStateless “And stupidly see what's what” in the newly created decision. Note that the solution is unconditionally created for x64, and in its composition:
- MyStateless project
- Program.cs is a service entry point. All she does is register the service in ASF, write this significant event in the log, and then fall asleep forever.
- MyStateless.cs is the actual service code. Contains a simple infinite loop, every second writing to the log and interrupted by an external CancellationToken.
- ServiceEventSource.cs - ASF uses ETW via EventSource. This class describes some commonly used events, providing convenient methods for logging.
- ServiceManifest.xml - MyStateless service manifest for ASF.
- Settings.xml is the service configuration, referred to in the manifest as ConfigPackage.
- MyFabric project
- ApplicationManifest.xml - the manifest of the entire MyFabric application for ASF.
- ApplicationParameters / (Local | Cloud) .xml - override the parameters of the application manifest.
- PublishProfiles / (Local | Cloud) .xml - application deployment profiles in various environments. In fact, they represent the address of the ASF cluster and the overrides from ApplicationParameters.
Let's run the solution for execution and later build and deploy we see just such a nice picture of SF Explorer (by the way - in the settings you can change the dark scheme to light):

What is in front of us? We have before us the management of a local 5-node ASF cluster (no matter how silly this may sound) with the fabric: / MyFabric / MyStateless application deployed on it. You should not be embarrassed by the inscription “2 applications” - on the cluster is the default application System of 4 stateful services used by the cluster itself. According to the MyFabric manifest, the MyStateless service is required only in one instance, and in our case this instance hit the 3rd node of the cluster. The application can be seen in the Diagnostic Events window:

You can have fun driving a single copy on the nodes - to do this, from the drop-down menu on the right of the Actions in the desired node, select any option Deactivate. In this case, the VS Diagnostic Events window reflects all the details of what is happening inside the ASF, in particular the fact that the “old” instance of MyStateless canceled and stopped, and the “new” started counting again.

This is not surprising - the service has no status.
I note here that SF Explorer itself and other cluster management is accessible from the “daisy” in the tray, and the Diagnostic Events window is available from the View / Other Windows menu. If this window suddenly doesn’t display anything,
add the line “MyCompany-MyFabric-MyStateless” to the list of ETW providers under the gear.
Cycle of life
Having fun to break the cluster, we proceed to the analysis of the details of the functioning of our service. In general, the life cycle of a service is determined by the set of virtual methods of the StatelssService class, from which our MyStateless is spawned, namely:
protected virtual IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() protected virtual Task RunAsync(CancellationToken cancellationToken) protected virtual Task OnOpenAsync(CancellationToken cancellationToken) protected virtual Task OnCloseAsync(CancellationToken cancellationToken) protected virtual void OnAbort()
There are only five interesting methods, and to understand the sequence of their calls, we modify the service code by redefining the methods and adding logging (yes, good old debug printing):
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { ServiceEventSource.Current.ServiceMessage(this, "CreateServiceInstanceListeners"); return new ServiceInstanceListener[0]; } protected override Task OnOpenAsync(CancellationToken cancellationToken) { ServiceEventSource.Current.ServiceMessage(this, "OnOpenAsync"); return Task.FromResult(0); } protected override Task OnCloseAsync(CancellationToken cancellationToken) { ServiceEventSource.Current.ServiceMessage(this, "OnCloseAsync"); return Task.FromResult(0); } protected override void OnAbort() { ServiceEventSource.Current.ServiceMessage(this, "OnAbort"); } protected override async Task RunAsync(CancellationToken cancellationToken) { long iterations = 0; while (!cancellationToken.IsCancellationRequested) { ServiceEventSource.Current.ServiceMessage(this, "Working-{0}", ++iterations); await Task .Delay(TimeSpan.FromSeconds(1), cancellationToken) .ContinueWith(_ => { }, TaskContinuationOptions.NotOnFaulted); } }
RunAsync is additionally reworked for correct completion instead of throwing an exception. Again we look at Diagnostic Events:

The first expectedly called CreateServiceInstanceListeners - in it the service creates all the necessary endpoints for communication with the outside world and within the cluster. Behind him is expected OnOpenAsync - to yes? So no - then comes RunAsync. The logic of this behavior is unclear, but according to the OnOpenAsync
documentation, it is in some way an additional event, but the RunAsync call is necessary (and quite sufficient). After it, OnOpenAsync is actually called and the service is already running until OnCloseAsync / OnAbort stops (caused, for example, by redeploying the service):

Configuration
There is nothing to configure in the template code, but there is something to be found - every second logging is good, but it could be more rarely. To do this, in the Settings.xml configuration, enter the parameter:
<Section Name="MyConfigSection"> <Parameter Name="MyFrequency" Value="10" /> </Section>
In the code, add the receipt of this value:
var configurationPackage = Context.CodePackageActivationContext.GetConfigurationPackageObject("Config"); var frequencyInSeconds = int.Parse(configurationPackage.Settings.Sections["MyConfigSection"].Parameters["MyFrequency"].Value);
And use the resulting value:
await Task .Delay(TimeSpan.FromSeconds(frequencyInSeconds), cancellationToken) .ContinueWith(_ => { }, TaskContinuationOptions.NotOnFaulted);
Now the service writes to the log every 10 seconds. Let me remind you that the service parameters can be redefined both in the application manifest and in the deployment profiles for different environments - as a rule, they are different for development environments and BETA / PROD.
Just add codes
A service is not a service when without communications — let MyStateless be a-la NTP time service, and use
ASF Remoting to simplify interactions with it. This technology allows you to communicate with the service as with a normal .NET object through the service interface. Create an IMyStateless interface with the single Now () method:
public interface IMyStateless : IService { Task<DateTimeOffset> Now(); }
To create a listener, modify the CreateServiceInstanceListeners:
return new[] { new ServiceInstanceListener(this.CreateServiceRemotingListener) };
And the usual console application will be the client:
public static void Main() { while (!Console.KeyAvailable) { var myStateless = ServiceProxy.Create<IMyStateless>(new Uri("fabric:/MyFabric/MyStateless")); Console.WriteLine(myStateless.Now().Result); Thread.Sleep(TimeSpan.FromSeconds(10)); } }
We add only the code, the configuration is not necessary to edit. In order to use the client, the ASF application must first be published - select Publish from the context menu of the MyFabric node, select Local.xml as the Target Profile, click Publish and wait for the message about the successful publication. Now you can start the client and make sure that the time of the local cluster is exactly the same as the system time (which is unexpected).
We grow above ourselves
Strictly speaking, ASF means stateless services that do not store state using the ASF itself. As is rightly said in the
documentation , it makes sense to partition stateless services when they still keep their state in any other convenient way (in the database, caches, text files, etc.). However, all services are actually partized - just stateless use a special case of SingletonPartition (and this is reflected in the application manifest). So let's start by scaling this single partition - we’ll increase the number of MyStateless instances by adjusting ApplicationParameters / Local.xml:
<Parameter Name="MyStateless_InstanceCount" Value="5" />
The node tree in SF Explorer no longer fits into the screenshot, so believe and check for yourself that five instances of the service are evenly distributed (that is, one at a time) across all five nodes of the cluster. With this scaling, problems may arise related to unshared resources (for example, ports), but for Remoting in our case, ASF balances calls between running instances on its own. For simplicity, let's add the InstanceId logging to the Now () method and in the Diagnostic Events window we will see that the calls fall into different MyStateless instances.
It is speculative to assume that MyStateless still stores some state outside ASF and partition it using the partitioning scheme by name:
<StatelessService ServiceTypeName="MyStatelessType" InstanceCount="[MyStateless_InstanceCount]"> <NamedPartition> <Partition Name="even" /> <Partition Name="odd" /> </NamedPartition> </StatelessService>
Redeploying and in SF Explorer we see that two partitions of five copies each again evenly (that is, one copy from the partition) are distributed over all five nodes of the cluster.
It was our victory, but now the client does not work, falling with the exception of FabricException and the informative message "Invalid partition key / ID '{0}' for selector {1}" - we fix the client:
var isEven = true; while (!Console.KeyAvailable) { var myStateless = ServiceProxy.Create<IMyStateless>( new Uri("fabric:/MyFabric/MyStateless"), new ServicePartitionKey(isEven ? "even" : "odd")); Console.WriteLine(myStateless.Now().Result); isEven = !isEven; Thread.Sleep(TimeSpan.FromSeconds(10)); }
And at the same time logging service:
ServiceEventSource.Current.ServiceMessage(this, "Now - " + Context.PartitionId + " - " + Context.InstanceId);
Now everything is fine - the client sends a call to the desired partition, and ASF balances calls between peer service instances inside the partition. MyStateless is remastered and made fault tolerant.
Web API
And in order not to get up two times, at the same time we will briefly touch upon the topic of ASF Web API services, since they are also created from templates without state. From the context menu of the MyFabric / Services node, select Add, the service type of the Stateless Web API, call MyWebApi and get a new project with the files:
- Program.cs - already recognizable contains the registration of the service in the ASF.
- MyWebApi.cs is a service code that, instead of implementing RunAsync, registers a listener with OwinCommunicationListener.
- ServiceEventSource.cs / ServiceManifest.xml / Settings.xml - play the same roles with amended content on the Web API.
- OwinCommunicationListener.cs - ASF's Web API templates are based on Owin, so this listener implements the ICommunicationListener methods by starting and stopping the Owin Web server.
- Startup.cs - configures routing in Owin
- ValuesController.cs - a simple test controller, the path to which is configured in Startup.cs
After publishing to SF Explorer, we see the second service fabric: / MyFabric / MyWebApi. To check its performance, you can open the page in the browser at
http://localhost:8201/api/values
— the controller's XML response from Get () is expected to appear in it, and at
http://localhost:8201/api/values/123
return the response from Get (int id).
“As George put it, there was so much to chew on!”
And this is where our first steps end. Obviously, this short article did not exhaust even a tenth of what can be said about ASF. In addition to the board, besides, there is such a large-scale topic as stateful services - it requires a separate article, effort and time, but if it works, then there will be a short distance to the Actor model in ASF. It is possible that it will come to this - as they say, write comments. The code from the article is available in
GitHub , and
here you can see the “native” examples from Microsoft.