Microservices with Steeltoe and Cloud Foundry: A .NET App Using MS Face API

by Aleksey YermolenkoJanuary 31, 2017
Demonstrating how to develop/deploy a cross-platform .NET Core app on Cloud Foundry, this solution is using RabbitMQ and Microsoft Cognitive Services API.

Why Steeltoe

Pivotal Cloud Foundry Steeltoe logo v2

Steeltoe is an open-source project that can help developers to create .NET microservices on Cloud Foundry, providing access to Spring Cloud and NetflixOSS tools powering cloud-native Java apps.

In our previous post, we explained how to consume Spring Cloud / NetflixOSS services from a .NET Core app using Steeltoe. If you want to explore Steeltoe in detail, you may also check out these two articles: Introducing Steeltoe and Steeltoe for ASP.NET 4.x.

 

Microservices-based and cross-platform

With this blog post, we wanted to go further and created a sample .NET Core app using RabbitMQ and Microsoft Cognitive Services API. The app allows users to detect human faces on a photo and extract avatars from it. With .NET Core, the code can be used for both Windows and Linux systems.

This example demonstrates the results of how the app works. 😉 We’ve taken the source photo (featuring the legendary James Watters) from Nima Badiey’s Twitter:

face-api-microsoft-cognitive-servicesImage credit: Nima Badiey

The extracted avatars:

face-api-microsoft-cognitive-services-output-v11

The solution itself consists of a website and two services. All communication between the website and the services was done via RabbitMQ queues. Implementing the Competing Consumers pattern made the system more reliable and scalable, while also helping us to decouple this system into small components.

building-microservices-with-steeltoe-microsoft-cognitive-api-and-cloud-foundry

The source code of the app is available in this GitHub repository.

 

Website implementation

We used Steeltoe to simplify the code needed to initialize the RabbitMQ client. It hooks up automatically the app’s environment variables to the RabbitMQ client and provides a factory for creating instances of the client.

Configuration initialization:

    public class Startup
    {
        public Startup(IHostingEnvironment env, ILoggerFactory logFactory)
        {
            ...

            // Set up configuration sources.
            var builder = new ConfigurationBuilder()
            ...
                .AddEnvironmentVariables()
                // Add to configuration the Cloudfoundry VCAP settings
                .AddCloudFoundry();

            Configuration = builder.Build();
        }
        
        public void ConfigureServices(IServiceCollection services)
        {
            ...
            services.AddRabbitConnection(Configuration);
        }
    }

The FromServices attribute tells the dependency injection resolver to create an instance of the client using ConnectionFactory initialized with a connection string and credentials from environment variables.

    public class MainController : ApiController
    {
        private readonly ConnectionFactory _rabbitConnection;

        public MainController([FromServices] ConnectionFactory rabbitConnection)
        {
            _rabbitConnection = rabbitConnection;
        }
    }

 

Building the project

To create a framework-dependent deployment, we set "type": "platform" in the project.json file. To build the project, you can run the following commands:

dotnet restore

The command restores NuGet packages for third-party dependencies.

dotnet publish -o publish -c Release

The command builds and publishes the project into the publish folder.

 
Cloud Foundry manifest file

The manifest.yml file contains settings for application deployment to Cloud Foundry:

---
applications:
- name: avatarmaker.web
  memory: 512M
  command: dotnet ./AvatarMaker.Web.dll --server.urls "http://*:$PORT"
  buildpack: https://github.com/cloudfoundry-community/asp.net5-buildpack.git
  services:
   - avatarmaker.rmq

Since the .NET framework is installed into containers by Cloud Foundry’s .NET Core buildpack (that’s why we opted for the framework-dependent deployment setting in the project), the command for starting the application looks as follows:

dotnet ./AvatarMaker.Web.dll --server.urls "http://*:$PORT"

We also needed to set the RabbitMQ service as a dependency to bind the service instance automatically to the app.

services:
 - avatarmaker.rmq

 
Deployment

To create an instance of the RabbitMQ service and deploy the application, the following commands should be used.

  1. Get the list of services available in the Cloud Foundry marketplace:

    cf marketplace
    
    service            plans                         description
    mysql              100mb, 1gb                    MySQL databases on demand
    p-rabbitmq         standard                      RabbitMQ is a robust and scalable high-performance multi-protocol messaging broker.
    postgresql95       free                          postgresql 9.5 (beta1) service for application development and testing
    redis              shared-vm, dedicated-vm       Redis service to provide a key-value store
  2. Create an instance of the service:

    cf create-service p-rabbitmq standard avatarmaker.rmq
  3. Deploy the application:

    cf push -p publish

pushing-net-core-app-to-cloud-foundry

 

Services implementation

Similarly to the website implementation process, we relied on Steeltoe to create instances of the RabbitMQ client:

IServiceCollection services = new ServiceCollection();
var config = new ConfigurationBuilder()
                .AddEnvironmentVariables()
                .AddCloudFoundry()
                .Build();
services.AddRabbitConnection(config);
var connectionFactory = services.BuildServiceProvider().GetService<ConnectionFactory>();

The Face API from Microsoft Cognitive Services was used to detect human faces in images. The Recognizer app sends an image URL to the web service, and the latter returns an array of rectangles with faces. The data is then sent to the message broker and processed by the last microservice in the chain—the EmailSender app. It downloads the image, crops it using the list of rectangles, and sends the cropped out face avatars back by email.

microservices-with-steeltoe-and-cloud-foundry-net-app-using-ms-face-api-service-implementationImage credit: Microsoft

 
Building the services

To build the services, see the website building steps described above.

 
Cloud Foundry manifest file

The manifest.yml file for the services is different from the website’s manifest file:

---
applications:
- name: avatarmaker.emailsender
  memory: 512M
  buildpack: https://github.com/cloudfoundry/dotnet-core-buildpack.git
  command: dotnet ./AvatarMaker.EmailSender.dll
  services:
   - avatarmaker.rmq
  no-route: true
  health-check-type: none

This application is a worker without an HTTP endpoint, so no routes are needed:

no-route: true

You have to disable health-check to make Cloud Foundry believe that the app is running properly:

health-check-type: none

 
Deployment

The deployment process is the same as that of the website, but with the first two steps skipped: there is no need to create another service instance.

Note that this demo app has some deficiencies that real-life applications don’t have the luxury to disregard, such as:

  • App settings are hardcoded, while the best practice is to use centralized storage for the settings, such as Spring Cloud Config Server—Steeltoe offers helper classes for it.
  • Nothing was done for error logging, and only the Cloud Foundry log aggregator component was used.

The source code of the app is available in this GitHub repository.

net-core-microservices-with-steeltoe-and-cloud-foundry

 

Conclusion

With Cloud Foundry, a .NET developer can create and deploy a scalable microservices-based .NET Core app fast and cost-effectively. Usage of Steeltoe’s adapter for ASP.NET Core RabbitMQ Client saved the effort required to initialize RabbitMQ connectors—otherwise, connection strings and credentials would need to be extracted from environment variables manually.

The developed system can be deployed on any Cloud Foundry distribution. Since the project uses .NET Core, it supports both Windows and Linux stacks. However, since .NET Core is a cross-platform technology, one should not expect that it supports the full .NET Framework functionality.

 

Related reading