Reliable Services communication

Azure Service Fabric gives you the flexibility to implement custom communication stacks using protocols of your choice. To implement a custom communication stack, you need to implement the ICommunicationListener interface. The Reliable Services application framework provides a couple of inbuilt communication stacks that you can use, such as the default stack built on RPC proxy, WCF, REST (Web API), and HTTP (ASP.NET).

Let us build a custom stack using ASP.NET, Web API, and open web interface for .NET (OWIN) self-hosting in Service Fabric stateless Reliable Service.

This sample is inspired from the official Service Fabric Web API services with OWIN self-hosting sample from MSDN: https://azure.microsoft.com/en-us/documentation/articles/service-fabric-reliable-services-communication-webapi/.
If you are not familiar with Web API. This is a great link to start: https://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api.
You can find an overview of the Katana project here: https://www.asp.net/aspnet/overview/owin-and-katana/an-overview-of-project-katana.

Using Visual Studio, create a Service Fabric application with a single stateless service named AgeCalculatorService:

Create stateless service template

Once the project is ready, we will pull in the Microsoft.AspNet.WebApi.OwinSelfHost NuGet package for Web API in the service project. This package includes all the necessary Web API packages and the host packages.

After installing the package, we will build a basic Web API project structure. Let us add a Controllers directory and a simple controller named AgeController:

    public class AgeController : ApiController 
{
public string Get(DateTime dateOfBirth)
{
return $"You are {DateTime.UtcNow.Year - dateOfBirth.Year}
years old.";
}
}

Next, to register routes and other configurations, add a Startup class in the project root directory:

namespace AgeCalculatorService 
{
using System.Web.Http;

using Owin;

public static class Startup
{
public static void ConfigureApp(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();

config.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });

appBuilder.UseWebApi(config);
}
}
}

Our application is now ready. Service Fabric executes your service in service host process, which is an executable that runs your service code. You compile your service in the form of an executable file that registers your service type and executes your code. When you open the Program.cs file, you will find the Main method which is the entry point of the service host process. This method contains the code essential for mapping your service to the related service type. You will find that a parameter named context is passed to your service instance, which, depending on the service type, stateful or stateless, will either be a StatefulServiceContext or a StatelessServiceContext. Both the context classes are derived from ServiceContext. If you want to setup dependency injection in your service, you can utilize the RegisterServiceAsync method to do that:

    internal static class Program 
{
private static void Main()
{
try
{
ServiceRuntime.RegisterServiceAsync(
"AgeCalculatorServiceType",
context => new
AgeCalculatorService(context)).GetAwaiter().GetResult();

ServiceEventSource.Current.ServiceTypeRegistered(
Process.GetCurrentProcess().Id,
typeof(AgeCalculatorService).Name);

Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}

Your application runs in its own process and this code shows just that. Your application is nothing but a console application that is managed by the Service Fabric runtime. OWIN decouples ASP.NET from web servers. Therefore, you can start a self-hosted OWIN web server inside your application process. We will implement the ICommunicationListener interface to launch an OWIN server, which will use the IAppBuilder interface to initialize the ASP.NET Web API. The ICommunicationListener interface provides three methods to manage a communication listener for your service:

  • OpenAsync: Start listening for requests
  • CloseAsync: Stop listening for requests, finish any in-flight requests, and shut down gracefully
  • Abort: Cancel everything and stop immediately

Before we implement the communication stack, we need to configure the endpoints for our service. Service Fabric ensures that the endpoints are available for our service to use. The Service Fabric host process runs under restricted credentials and therefore your application won't have sufficient permissions to setup ports that it needs to listen on. You can setup endpoints for your service in PackageRootServiceManifest.xml:

  <Resources> 
<Endpoints>
<Endpoint Name="ServiceEndpoint" Type="Input" Protocol="http"
Port="80" />
</Endpoints>
</Resources>

By using the endpoint configuration, Service Fabric knows to set up the proper Access Control List (ACL) for the URL that the service will listen on. Service Fabric guarantees that only one instance of a stateless service will be deployed on a single node. Therefore, you don't need to worry about multiple applications listening on the same port in this case. In other cases, your service replicas might get deployed to the same host and therefore might be listening on the same port. Therefore, your communication listener must support port sharing. Microsoft recommends that your application communication listeners listen to traffic on an endpoint that is built using combination of partition ID and replica/instance ID, which is guaranteed to be unique.

Create a class named OwinCommunicationListener that implements the ICommunicationListener interface. We will add private class member variables for values and references that the listener will need to function. These private members will be initialized through the constructor and used later when you set up the listening URL.

   internal class OwinCommunicationListener : ICommunicationListener 
{
private readonly string appRoot;

private readonly string endpointName;

private readonly ServiceEventSource eventSource;

private readonly ServiceContext serviceContext;

private readonly Action<IAppBuilder> startup;

private string listeningAddress;

private string publishAddress;

private IDisposable webApp;

public OwinCommunicationListener(
Action<IAppBuilder> startup,
ServiceContext serviceContext,
ServiceEventSource eventSource,
string endpointName)
: this(startup, serviceContext, eventSource, endpointName,
null)
{
}

public OwinCommunicationListener(
Action<IAppBuilder> startup,
ServiceContext serviceContext,
ServiceEventSource eventSource,
string endpointName,
string appRoot)
{
if (startup == null)
{
throw new ArgumentNullException(nameof(startup));
}

if (serviceContext == null)
{
throw new ArgumentNullException(nameof(serviceContext));
}

if (endpointName == null)
{
throw new ArgumentNullException(nameof(endpointName));
}

if (eventSource == null)
{
throw new ArgumentNullException(nameof(eventSource));
}

this.startup = startup;
this.serviceContext = serviceContext;
this.endpointName = endpointName;
this.eventSource = eventSource;
this.appRoot = appRoot;
}
}

Now, let's implement the OpenAsync method. In this method, we will start the web server and assign it the URL it will listen on. Since this is a stateless service, we do not need to have the partition and replica identifiers suffixed to the URL to make it unique:

    public Task<string> OpenAsync(CancellationToken cancellationToken) 
{
var serviceEndpoint =
this.serviceContext.CodePackageActivationContext.
GetEndpoint(this.endpointName);
var protocol = serviceEndpoint.Protocol;
var port = serviceEndpoint.Port;

if (this.serviceContext is StatefulServiceContext)
{
var statefulServiceContext = this.serviceContext as
StatefulServiceContext;

this.listeningAddress = string.Format(
CultureInfo.InvariantCulture,
"{0}://+:{1}/{2}{3}/{4}/{5}",
protocol,
port,
string.IsNullOrWhiteSpace(this.appRoot) ?
string.Empty : this.appRoot.TrimEnd('/') + '/',
statefulServiceContext.PartitionId,
statefulServiceContext.ReplicaId,
Guid.NewGuid());
}
else if (this.serviceContext is StatelessServiceContext)
{
this.listeningAddress = string.Format(
CultureInfo.InvariantCulture,
"{0}://+:{1}/{2}",
protocol,
port,
string.IsNullOrWhiteSpace(this.appRoot) ?
string.Empty : this.appRoot.TrimEnd('/') + '/');
}
else
{
throw new InvalidOperationException();
}

this.publishAddress = this.listeningAddress.Replace("+",
FabricRuntime.GetNodeContext().IPAddressOrFQDN);

try
{
this.eventSource.Message("Starting web server on " +
this.listeningAddress);

this.webApp = WebApp.Start(this.listeningAddress,
appBuilder => this.startup.Invoke(appBuilder));

this.eventSource.Message("Listening on " +
this.publishAddress);

return Task.FromResult(this.publishAddress);
}
catch (Exception ex)
{
this.eventSource.Message(
"Web server failed to open endpoint {0}. {1}",
this.endpointName,
ex.ToString());

this.StopWebServer();

throw;
}
}

This method starts the web server and returns the address that the server is listening on. This address is registered with the Naming Service, which is a cluster service. A client can use the service name and get this address from the Naming Service of the cluster. Let's implement the CloseAsync and the Abort methods to complete the implementation:

        public void Abort() 
{
this.eventSource.Message("Aborting web server on endpoint
{0}", this.endpointName);

this.StopWebServer();
}

public Task CloseAsync(CancellationToken cancellationToken)
{
this.eventSource.Message("Closing web server on endpoint
{0}", this.endpointName);

this.StopWebServer();

return Task.FromResult(true);
}
private void StopWebServer()
{
if (this.webApp != null)
{
try
{
this.webApp.Dispose();
}
catch (ObjectDisposedException)
{
// no-op
}
}
}

Finally, we need to override the CreateServiceInstanceListeners method in our service implementation to create and return an instance of OwinCommunicationListener in the AgeCalculatorService class.

         protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() 
{
var endpoints =
Context.CodePackageActivationContext.GetEndpoints()
.Where(endpoint => endpoint.Protocol == EndpointProtocol.Http || endpoint.Protocol == EndpointProtocol.Https)
.Select(endpoint => endpoint.Name);

return endpoints.Select(endpoint => new ServiceInstanceListener(
serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, endpoint), endpoint));
}

Remove the default implementation of RunAsync in the AgeCalculatorService class as we don't need to perform any processing in the background. Build and deploy the application on your local cluster. You can find the address of the application in Service Fabric Explorer on which you can send a request to your API:

Service endpoint

In the browser, send a request to your application with your date of birth appended as a query string parameter to the request. For example: http://localhost/api/Age?dateOfBirth=12-12-2001.

After submitting the request, you should see a result as follows:

Output of age calculator
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.253.55