Turning a console app into a long-running Azure WebJob

A few days ago I created a continuous running Azure service using an Azure WorkerRole. Although this service is running stable and it just does what it has to do, I was not completely ok with it. The Azure WorkerRole is a great way of using Azure for long-running logic but it feels a bit deprecated. WorkerRoles are hanging around from the start of Azure. For a few year now we have a second mechanism called WebJobs.

So I decided to look into this functionality.

A WebJob is just some script running along the side of a WebApp. It uses the resources of the machine the WebApp is running on.

WebJobs can be triggered (eg. when a Blob is created in Blob Storage), they can be scheduled (start a certain date or interval) or they can run continuously. The last option is what I was looking for.

There are several programming options to run Background tasks with WebJobs:

In my example I just use a console app to generate an executable:

namespace SimpleConsoleApplication
{
  internal class Program
  {
    private static void Main(string[] args)
    {
      Console.WriteLine("Start of the application");
 
      Console.WriteLine("Press a key to exit...");
 
      Console.ReadKey();
    }
  }
}

It does nothing spectacular? Yep. It just sits and waits for a key to be pressed. This is to hold the app from closing directly. This is a genuine long running (boring) app 🙂

But this is essentially the same functionality as my MQTT The Things Network service. The only thing that’s really different is that messages from the MQTT pipeline are handled like events.

so this is how our app is running unti we press a key:

wj01

Close the app and right-click the project in the Solution Explorer. There is an option to publish the app as Azure WebJob:

wj02

Note: You need the Azure SDK and tooling to be installed in Visual Studio.

A dialog is shown to alter the way the WebJob will be running. Our WebJob will run continuously:

wj03

After that, the WebJob Publish Nuget package (this is not the full Microsoft.Azure.WebJobs package…) will be installed and we have to select the app service in the next dialog:

wj04

And the next one:

wj05

When asked, create the app service plan. Our WebJob must be hosted in an app service, so it has to land on a ‘machine’.

The size is important! The bigger size you select, the better the WebJob performs but that is more expensive. There is also a Free tier but do NOT select that one for a long running task! Our WebJob has to run continuously and that’s only supported in the non-free service plan (this is the Aways-On option).

Free WebJobs stop approx. twenty minutes after the last access to the webApp has taken place…

I configure the basic size:

wj06

After pressing OK, press Create for creating the app service. After creation of the resources, let’s publish the WebJob:

wj07

Check if the publish step succeeded:

wj08

The service plan and service are created in Azure:

wj09

But the WebJob is not ‘running’ yet continuously. Normally WebJobs are part of a website. When the website is running, the associated jobs are running. In general, we have to force the WebApp (and the WebJob) to be always on. Go to the WebApp and then go to the settings and finally, go to the application settings:

wj10

At the right side, I forced the WebApp to be ‘always on’ (and btw. I also disabled PHP). Now press the save button and wait for the WebApp to be updated.

After that, you can check out our WebJob in the settings:

wj11

Our WebJob says it’s status is ‘Pending Restart’. Hmm, I was hoping for ‘Running’.

Just above the list of all WebJobs (..) there is a Log button. Press it:

wj12

This opens a new page in the browser:

wj13

It seems an exception is occurring:

[07/19/2016 11:59:12 > 49f2ab: SYS INFO] Run script 'SimpleConsoleApplication.exe' with script host - 'WindowsScriptHost'
[07/19/2016 11:59:12 > 49f2ab: SYS INFO] Status changed to Running
[07/19/2016 11:59:13 > 49f2ab: INFO] Start of the application
[07/19/2016 11:59:13 > 49f2ab: INFO] Press a key to exit...
[07/19/2016 11:59:13 > 49f2ab: ERR ]
[07/19/2016 11:59:13 > 49f2ab: ERR ] <strong>Unhandled Exception</strong>: System.InvalidOperationException: <strong>Cannot read keys</strong> when either application does not have a console or when console input has been redirected from a file. Try Console.Read.
[07/19/2016 11:59:13 > 49f2ab: ERR ]    at System.Console.ReadKey(Boolean intercept)
[07/19/2016 11:59:13 > 49f2ab: ERR ]    at SimpleConsoleApplication.Program.Main(String[] args)
[07/19/2016 11:59:13 > 49f2ab: SYS ERR ] Job failed due to exit code -532462766
[07/19/2016 11:59:13 > 49f2ab: SYS INFO] Process went down, waiting for 60 seconds
[07/19/2016 11:59:13 > 49f2ab: SYS INFO] Status changed to PendingRestart

The issue is that a WebJob has no UI, not even a console. So the ‘Console.ReadKey();’ is failing. And therefore the WebJob is closed immediately.

Let’s fix this.

Replace the Main function with this code:

private static void Main(string[] args)
{
  Console.WriteLine("Start of the application");
 
  Console.WriteLine("The app will not close using this loop...");
 
  //Console.ReadKey();
 
  while (true)
  {
    Thread.Sleep(1000);
  }
}

We just loop indefinitely with a short moment of sleeping.

Now deploy the same project again! Press the refresh link on the logging page:

wj14

The logging is now showing the WebJob is running. And no exception is thrown anymore. Finally, the WebJob has the status ‘Running’:

wj15

This is the most simple way to start using WebJobs as a continuously running process, coming from a Console app.

Pro tip

There is also an official WebJob template available in Visual Studio. That template integrates far more into the dashboard of Azure and uses Azure Storage for logging. I can recommend the template when you are looking for a more permanent WebJob.

It is possible to transform our project into the more professional format. First, we have to add the official Nuget package “Microsoft.Azure.WebJobs”.

This will generate a lot of extra assemblies and dependencies:

wj16

And the WebJob suddenly relies on Azure Storage:

wj17

So add some Azure Storage too:

wj18

And get the keys:

wj19

We have to add two connection strings fo get it all working in the app.config:

<connectionStrings>
    <add name="AzureWebJobsStorage" connectionString="DefaultEndpointsProtocol=https;AccountName=simplewebjobstorage;AccountKey=[key]" />
    <add name="AzureWebJobsDashboard" connectionString="DefaultEndpointsProtocol=https;AccountName=simplewebjobstorage;AccountKey=[key]" />
  </connectionStrings>

Finally, you will have to change the code a bit.

The Main method only has to hold three lines to hold the WebJob from  stopping:

var host = new JobHost();
host.CallAsync(typeof(Functions).GetMethod("ProcessMethod"));
host.RunAndBlock(); 

But where should my logic go?

Add a public class named Functions with a public method ProcessMethod:

public class Functions
{
   [NoAutomaticTrigger]
   public static async Task ProcessMethod(TextWriter log)
   {    
      // Do something.      
      log.WriteLine("This ProcessMethod method is executed");
      Console.WriteLine("This ProcessMethod method is executed");
 
      await Task.Delay(TimeSpan.FromMinutes(1));
   } 
} 

Now your code resembles a real WebJob. Have fun with it.

Ninja tip

There are also SDK’s available for Azure projects. These are the WebJob templates:

wjsdk

These SDK templates also support an additional dashboard.

More information is available at https://azure.microsoft.com/en-us/documentation/articles/websites-dotnet-webjobs-sdk/

 

Advertenties

Reacties zijn gesloten.