4
votes

All,

I am migrating existing Worker Role code to an Azure Web job. I am trying to use the WebJob SDK (1.0) so that I have the full integration with the Azure Web Site.

My difficultly is that the JobHost doesn't play nicely with jobs that are outside it's usual Attribute based invoke options (Queues, Blobs etc.)

I already have standard code that I cannot change to listen to Azure Queues/Topics etc. so I cannot use the WebJob code to do this.

Therefore I need to use the WebJob Call method:

var cancelTokenSource = new CancellationTokenSource();
var onStartMethod = typeof(Program).GetMethod("OnStart", BindingFlags.Static | BindingFlags.Public);

host.CallAsync(onStartMethod, cancelTokenSource.Token)
    .ConfigureAwait(false)
    .GetAwaiter()
    .GetResult();

NB: I am using my own CallAsync as all the advice is to use ConfigureAwait(false) when using libraries but the innards of JobHost doesn't do this, so I'm replicating it's "call" code myself but with the ConfigureAwait. The cancellation token doesn't do anything in JobHost as far as I can tell.

My problem is that I then need to call host.RunAndBlock(); to stop the job exiting, which is fine, but then I need to run some clean up processing. I can't make a new call to "CallAsync" with an OnStop method, as the Host is already being cancelled, so all I can do is make a direct call to my OnStop method. Unfortunately then I lose the ability to write to the WebSite log through the provided TextWriter class.

I think what I need is a way for JobHost to invoke my method within RunAndBlock, so I can then pick up the cancellation token fired when the host is shutting down, and then perform my cleanup code.... but there doesn't seem any way to do this.

Is there an obvious way I am missing? JobHost seems really poor at handling scenarios outside it's norm :(

2

2 Answers

4
votes

As victor said, you could use Microsoft.Azure.WebJobs.WebJobsShutdownWatcher
This is an implementation of Amit solution : WebJobs Graceful Shutdown

So I've found a solution doing this :
No modification in the Program.cs

class Program
{
    static void Main()
    {
        var host = new JobHost();
        host.Call(typeof(Startup).GetMethod("Start"));
        host.RunAndBlock();
    }
}

the graceful shutdown goes in the Startup.cs :

public class Startup
{
    [NoAutomaticTrigger]
    public static void Start(TextWriter log)
    {
        var token = new Microsoft.Azure.WebJobs.WebJobsShutdownWatcher().Token;
        //Shut down gracefully
        while (!token.IsCancellationRequested)
        {
            // Do somethings
        }
    }
}

After the while loop, you could also stop started tasks.

2
votes

NB: I am using my own CallAsync as all the advice is to use ConfigureAwait(false) when using libraries but the innards of JobHost doesn't do this, so I'm replicating it's "call" code myself but with the ConfigureAwait. The cancellation token doesn't do anything in JobHost as far as I can tell.

That's expected because you are passing your own cancellation token that is not cancelled by anyone. The webjobs cancellation token is cancelled when the shutdown notification is sent or the host is stopped/disposed. If you want to get a reference to the cancellation token from outside a webjob function, you have to keep it in a static variable or something similar.

If you want to use your own cancellation token you can use Microsoft.Azure.WebJobs.WebJobsShutdownWatcher to get the shutdown notification.

My problem is that I then need to call host.RunAndBlock(); to stop the job exiting, which is fine, but then I need to run some clean up processing. I can't make a new call to "CallAsync" with an OnStop method, as the Host is already being cancelled, so all I can do is make a direct call to my OnStop method. Unfortunately then I lose the ability to write to the WebSite log through the provided TextWriter class.

There is no out-of-the-box solution for this but you could call the cleanup function from the running webjob (if any). If you need cleanup outside of the function and you also want logging then you can only Console logging - write to console. The logs will be displayed on the WebJobs dashboard, on the webJob page. I'm curios, if you need cleanup outside of a function, what is the scenario?