We are living in a golden age of programming. The language and tooling have never been better. Not a day goes by that I’m not impressed by some new programming language feature (how cool are nullable reference types in C#?); or a new technology (Live Share and Teletype are going to change the lives of remote developers); or some adaptation of an existing technology (Cassandra driver compatibility for Cosmos DB allowing seamless data-layer swapping). Applications that would have taken me months to write even five years ago can now be hammered out in a day thanks to better frameworks, tools, and languages.
At the same time, however, the difficulty of the problems that we face has risen. There is an economic theory presented by Cyril Northcote Parkinson, and, in fact, named for him (Parkinson’s Law), who was a writer for The Economist magazine some years ago. He stated that the amount of work will grow to fill the time available to it. Our congruent theory is that the customer’s expectations of applications have grown to fill the slack created by better tooling.
Running applications on Azure has followed this same pattern of increasing simplicity. In the early days, Azure was a thin veneer over virtual machines, and since then it has grown and mutated to the point where Sean Feldman and I give an entire presentation on how to choose which service to use to host your code. From virtual machines to Azure Functions, we can tune just how much abstraction there is between our application code and the hardware. This article is about picking between two of the abstractions that run furthest from the hardware: WebJobs and Azure Functions.
Let’s start by talking about App Service, which is the technology that underlies both of these offerings. App Service started its life as a way of hosting applications inside of IIS (Internet Information Services) on Windows. Many discount hosting providers offered virtual hosting, which meant that your application shared resources on a machine with dozens or maybe even hundreds of other applications. The separation between the applications was provided by IIS and, as such, the configurability and isolation of these services was pretty limited. However, the cost of those hosting sites was very low. Initially Azure didn’t have anything to compete in that space and price point so App Service was created. At first, the capabilities were limited to hosting applications in IIS, but this soon changed.
One of the more common requirements for web applications is to be able to run background tasks. Batch processing, scheduled tasks, and long-running processes are all common in modern applications. The issue with running these on IIS is that it consumes one of the precious threads dedicated to serving content and the process may be interrupted by an app pool recycle. There are, of course, some tricks to push off app pool recycles until your task has completed, but ideally we’d like to run the task outside of IIS. WebJobs were created to provide this capability. Developers can call out to a WebJob via a messaging system such as Storage Queues or Azure Service Bus and have it complete the task while the main application continues on.
The advantages to such a system are numerous:
- Frees up IIS threads
- Can easily be run on a separate machine to avoid scalability issues
- Offers a higher degree of resilience to app pool recycles
WebJobs were the first attempt to solve this problem. WebJobs have built-in triggers for a number of different events inside of Azure: storage queues, blobs, service bus queues, topics and schedule triggers. This means that it is possible, even easy, to set up a WebJob that monitors a blob storage account for new items. Upon discovering a new item, it will be launched to process it. Scott Hanselman gives a great example on his blog of using a WebJob to resize an image.
In order to run WebJobs, you’ll need to already be running an App Service plan. For most people looking to add onto an existing hosting plan, this is a given and there is no additional cost; however the same resources are used, so creating a WebJob will sap some of the performance from the App Service. You can run multiple WebJobs in an App Service.
Deploying WebJobs is quite easy since they deploy using the same infrastructure as any App Service. This allows deploying from source control, FTP, or even Dropbox (but please don’t do that). Deploying from Visual Studio is also possible (again, please don’t do that), although you’re much better off deploying from a continuous build tool such as TeamCity or Visual Studio Team Services (VSTS).
Azure Functions take the concepts from WebJobs and expand on them in some interesting ways. First, Functions enable a whole raft of new trigger types. It is now possible to trigger on such things as Cosmos DB’s change feed, Event Hubs and WebHooks. As a web developer, the HTTP trigger is one of the most interesting. HTTP triggers enable builing an entire website or web application entirely with triggers.
HTTP triggers also unlock the ability to build very small webooks that can be deployed at a very low cost. For instance, Azure Functions are well suited for building a simple Slack bot, or a service for GitHub integration. or to be slotted into a workflow automation service like IFTTT or Logic Apps.
More interesting than the triggers is the change in hosting model. WebJobs are tightly coupled with the App Service plan that hosts them. This means that if you have one WebJob of 20 in a plan that requires additional resources, your only option is to scale the entire App Service plan.
This brings a lot of overhead since now each instance of the App Service is running all the WebJobs. Functions can be deployed using this same model, which may be desirable if you have few to deploy and an existing App Service, or they can be deployed in a pay-per-use model that some refer call Functions as a Service (FaaS) or serverless.
Of course, the functions still run on a server, so serverless really means that they scale rapidly and outside of the constraints of a single piece of hardware. The pool of servers from which your function may draw is vastly larger than you’d get running on your own App Service. This unlocks the ability to scale very rapidly for unexpectedly large workloads, and to not have to manually scale for expected peaks in demand.
What is often overlooked is the ability to scale the services down. Functions are billed using a rather comical metric known as a gigabyte second, meaning that you’re billed for the amount of memory your function uses and for how long the function ran. If nobody is using the Function, then there is no minimum cost per month: it is just free. This model is amazing for startups that are operating on a shoestring budget, or for workloads that have long cycles of almost no use followed by high use (registering for university classes spikes in late summer, and submitting tax returns in late January to mid-April in the United State of America).
Functions are the logical successors to WebJobs for the vast majority of workloads. This appears to be the opinion that is held by the Azure Functions team as well: Chris Anderson a PM on the Functions team writes that Azure Functions are the logical successor to WebJobs. In fact, Azure Functions are actually written on top of the WebJobs SDK.
The ease of scaling out to huge numbers of machines is also wildly enticing. A number of projects with which I’ve been involved have taken what were once long-running batch jobs and split them up into 10,000 small steps that can be run independently on Azure Functions, which handle scaling them out to hundreds or thousands of nodes as needed.
As demands on programmers to provide polished and powerful solutions intensifies, Azure Functions provide a solution to handling high-scale operations for very little cost. I’m utterly convinced that Azure Functions will eat the lunches of not only traditional hosting models, but also steal away supper from containers and orchestration systems such as Kubernetes. The hands-off management approach reduces the demands on operations, while maintaining low costs. My friends, I have seen the future and it is serverless.
- 15 Simple ASP.NET Performance Tuning Tips - January 31, 2018
- .NET Event Counters – When logging isn’t fast enough - December 12, 2017
- Azure WebJobs vs. Azure Functions - November 27, 2017
- Debugging Tricky HTTP Problems with Fiddler and Charles - November 1, 2017
- 3 New C# 8 Features We Are Excited About - September 18, 2017