Two weeks ago, Azure announced what is arguably its most significant feature change since Azure Resource Manager. Thing is, most people don’t have much use for it today, and might not for a while, so you may not recognize it as such for quite some time.
But make no mistake: Azure Event Grid, now in public preview, is critical to the future of Azure, because it’s the missing link in the microservices architecture and makes serverless computing a first-class citizen in Azure.
For the uninitiated, let’s take a moment to review what it means to be serverless, and how that fits into a microservices architecture.
In traditional software design, we tend to look at our programs as silos. For example, if I build a webstore, that will typically be designed using the n-tier architecture: A pool of webservers to handle requests; a pool of database servers on the backend to hold products, customers, and orders; and a middleware tier that handles the business logic of accepting user input, retrieving data, and processing requests.
This works great in a traditional, on-premises computing environment because we can easily design networks and subnets to host specific servers. We can also use network appliances like load balancers, application gateways, bastion hosts, and firewalls to regulate traffic in and out of these subnets and defend our application in depth against attacks.
And we can even scale to workload changes by adding or removing web servers, application servers, or database servers in each tier.
But this is also inefficient in a lot of ways. It is, for example, difficult to manage and difficult to replicate. It requires dedicated resources to run all the time and for us to overprovision servers to meet occasional demand spikes. And if I have a problem with one of these silos, necessarily, the entire application must go offline to fix the problem.
Enter the microservices architecture.
The Microservices Pattern
Here we can see a hotel website and mobile app backend, designed with the microservices architecture. Each of the colored squares is a microservice.
A microservice is basically a specific kind of task a program needs to provide. In this example, our solution has a user interface API (in green), which is used by both our website and our mobile application to render content.
This UI API, in turn, coordinates requests with three other APIs:
- An authentication API, in blue, which authorizes users and is back-ended via a user database
- A reservation API, in yellow, which handles room inventory and is back-ended by a different database
- An email marketing API, in red, which is used to allow customers to subscribe and unsubscribe to newsletters, receive promotional mailings, get confirmation emails, etc., and which is back-ended by a third party email provider, such as Constant Contact, MailChimp, or the like.
Again, all of these APIs are independent. They are built independently, usually by teams that are dedicated to that task, and they deploy independently. They have their own dependencies. They focus on a task or a narrowly defined set of tasks, rather than doing everything, as we do in monoliths and n-tier applications.
This makes a microservices-based application easier to manage, faster to deploy, and recyclable.
Because each microservice is self-contained, I can make changes to it without worrying about the effect on other microservices (provided I don’t change the inputs the microservice accepts and the outputs it generates, that is). That means each team isn’t waiting on the other team to complete work, nor is that team wedded to the same sprints other teams are on.
In fact, I can use continuous integration/continuous delivery to eliminate sprints altogether, since the complexity of my code is so significantly reduced. This not only increases the pace of deployments, it significantly reduces cost by eliminating DevOps tasks (and, thus, people/salary).
Better yet, I can scale to the workload demand for each task, rather than each silo. For example, if I used an n-tier architecture for this hotel website, and I saw a significant demand increase for reservations, I would have to scale out the entire middleware tier, which probably uses very expensive servers. In a microservices approach, I can scale just the reservations API, which reduces the cost impact.
Finally, each of these microservices can be leveraged by more than one solution. For example, I could run a routine every night that uses my reservations API to put available rooms on a discount booking website, so that I have less waste inventory.
Containers and Serverless
So needless to say, microservices provide significant benefits over the n-tier pattern. And thanks to the abstract nature of public cloud hosting, it’s easily accomplished.
The reason everyone is in love with containers these days – the very first word spoken at every tech conference is “Docker” – is because containers make deployments fast, reliable, and repeatable.
Using these tools, I can replicate the n-tier and monolithic approaches to my architecture. But containers really shine when I use them to host microservices, because I get the benefit of being able to ensure my workload, and its environment, is the same every time I create a new instance.
This gets even better when I consider using serverless code.
While a container gives me a repeatable and reliable result every time I launch a new instance, I still have to get it right the first time. And I’m still managing an environment, albeit that configuring a container is much easier than configuring its underlying host OS.
Serverless code abstracts away pretty much all environment configuration. You just write code that works in this predefined environment, and the cloud provider takes care of the rest.
Azure Functions – Microsoft’s primary serverless offering – is actually based in App Service WebJobs, so you do have an underlying Web App you can configure, and you can ask for that Web App to persist between requests. But this is optional, and it can even be managed from my CI/CD pipeline.
This makes serverless basically containers on steroids. The process of creating an environment to run my code, scaling it to the workload, and deprovisioning it when it’s no longer needed is completely automated by the cloud provider. In other words, I need only a skeletal DevOps crew to manage serverless computing; basically, I just need someone to manage my source control and configure my related resources, such as Azure SQL Database, Traffic Manager, CDN, Service Bus and the like.
With all these advantages, it’s no wonder Microsoft has staked its vision for the future, in large part, on serverless computing.
Azure Event Grid
Serverless technology, such as Azure Functions, works on the model of trigger-input-output.
That is, a serverless function isn’t even provisioned until something else tells it to exist. That “something else” can be a schedule (e.g., do work every 5 minutes, every day, once a month, etc.), an HTTP request on a given endpoint/URL, the uploading of an object to a storage account, etc.
This has been a significant limitation in the use of serverless, at least as far as Azure goes. If I want to create a web API that sees limited traffic, Azure Functions is perfect. If I need to perform a task on a schedule, it’s amazing. If I have a message queue to process, I can’t ask for anything better.
But handling events – changes in the status or activities of my other Azure services – has been spotty. While Azure Functions has a long list of Azure service-based triggers to which it can respond, not every service has been capable of triggering a Function trigger and not every event in a given service can trigger a Function.
For example, I can create a trigger for a new blob being created or modified in Storage, but there’s no trigger for a deleted blob. Triggers are also missing for the creation of new containers, the changing of container visibility, accessing a blob via a secure means (SAS signature or account key), and the like.
All of these are examples of events I might need to handle via some automated process. For example, I might need to add to a configuration file the names of containers created by another automated process, or I might want to audit restricted access to my blob Storage, sending a push notification if access is obtained from a previously unseen IP address.
Azure Event Grid provides this missing piece.
Azure Event Grid allows you to create a webhook for just about any event in almost every Azure resource. You can also directly hand off requests to an Azure Function, a Logic App, or Azure Automation. In short, if Azure can log it, it can also send a JSON object to an HTTP endpoint with that logged information.
Created a new Web App? Event Grid will describe that Web App – its name, its domain, its App Service Plan,etc. – in a HTTP POST to whatever endpoint you specify with a JSON body that describes that request.
These events are raised at the service level and the application level. That means I cannot only raise an event, for example, upon the creation of a new Azure SQL Database, I can also raise an event to a new record being inserted in a table of that database. That’s powerful.
While in public preview, Azure is only exposing full event access for resource group/subscription changes, event hubs and custom events you create. It is also only available in the West Central US (Wyoming) and West US 2 (Seattle area) regions.
The Game Changer
This is a game changer, because it’s another step in solving the cloud’s biggest challenge: Transferring state between services.
While the disconnected nature of microservices-based applications makes them spry, easy to manage, and, generally, pretty elegant, they have a major weak point: Retaining state between requests.
Using our hotel web services example from before, if I connect to the hotel’s website and make a reservation, the UI API will hand off my request to the reservations API, and that API will, in turn, return a response. But what if I want to add in another step? For example, one that automatically queries a third-party events listing for the same time I am in town and then uses the email marketing API to send me an email with suggested activities during my trip. To do that, I need to persist my identity and stay dates from the reservations API, to that third party data store, and back again to the email marketing API.
This is very easily done in a monolith; I just create a session or a context. But it’s not easily accomplished in microservices, which intentionally divorce tasks into separate concerns.
With Event Grid, each step in this process can raise a new trigger and deliver a JSON object to that payload to whatever is going to handle the request.
In other words, the one remaining benefit of the mololith/n-tier architecture – statefulness – is soon to become a nonissue for serverless computing.
That removes an enormous barrier to the adoption of serverless technology, and is another step toward making serverless the default technology choice for new architecture, and – within perhaps the next decade – the primary means of delivering cloud-based solutions.