Azure Function Deployment
Deploying JobFlow as a set of Functions allows for lower-cost, on-demand processing. When parts of the system are idle, Azure can shut down those pieces to save costs. When a message is detected in a queue, Azure will start the appropriate Function up to process the message, leaving other pieces alone until needed.
Utilizing Azure Functions for a JobFlow system follows a consistent template for each Function, making creation and deployment simple and straightforward.
JobFlow Worker Function
The basic template for a JobFlow Worker Function is as follows:
public class Template
{
private readonly IWorker<MyDocumentItem> _handler;
public Template(
IWorker<MyDocumentItem> handler)
{
_handler = handler;
}
[FunctionName("Template")]
[return: ServiceBus("job-result", Connection = "JobQueueConnectionString")]
public async Task<Message<WorkResponse>> Run([ServiceBusTrigger("myqueue", Connection = "JobQueueConnectionString")]Message<WorkRequest<MyDocumentItem>> message, ILogger log)
{
return new(await _handler.ProcessJob(message.Data));
}
}
The IJobWorkProcessor<T>
interface is a convenience interface for defining Job workers and is particularly useful for defining ASP.Net background workers (see Transport Listeners). For Azure Functions, you can use whatever interface you wish.
The key points on defining the Function are:
- Trigger - the Function trigger needs to be listening to the appropriate queue and queue type.
Message<JobWorkRequest<T>>
argument - all the messages sent by JobFlow are wrapped in aMessage<T>
class, including the Request and Responses messages for workers.Message<JobWorkResponse>
return value - the Function return needs to be aMessage
ofJobWorkResponse
and placed on the appropriate queue. This queue is defined by the[return: ServiceBus]
attribute and needs to be of typeMessage<JobWorkResponse>
.
Note: See also: Message Classes
JobFlow Core Functions
The Core Functions would include the JobWorkResponse handler and the scheduled Job handler. Both of these functions follow similar templates and differ only slightly from a standard Worker Function. In particular, they don't have a return value.
public class JobResultFunction
{
private readonly IMessageHandler<WorkResponse> _messageProcessor;
public JobResultFunction(
IMessageHandler<WorkResponse> messageProcessor)
{
_messageProcessor = messageProcessor;
}
[FunctionName("JobResultFunction")]
public async Task Run([ServiceBusTrigger("myqueue", Connection = "ServiceBusConnectionString")] Message<WorkResponse> message, ILogger log)
{
await _messageProcessor.HandleAsync(message);
}
}
The handler for a schedule message is similar except using the Message type of Message<ScheduledJobData>
.
public class ScheduledJobFunction
{
private readonly IMessageHandler<ScheduledJobData> _messageHandler;
public ScheduledJobFunction(IMessageHandler<ScheduledJobData> messageHandler)
{
_messageHandler = messageHandler;
}
[FunctionName("ScheduledJobFunction")]
public async Task Run([ServiceBusTrigger("myqueue", Connection = "ServiceBusConnectionString")]Message<ScheduledJobData> message, ILogger log)
{
await _messageHandler.HandleAsync(message);
}
}
Note: Ensure each of the Core Functions is listening to the appropriate queue.
In the future, a command line interface will likely handle creating these templates for you.