In the beginning, there were servers. They lived somewhere in your office; sometimes hidden behind a locked door, sometimes under a desk complete with a "DO NOT TURN OFF!" post-it.
Then came the cloud. The sales pitch was simple: let us manage your infrastructure and we'll take all those worries about 'uptime', 'resilience' and 'scalability' off your hands.
The cloud opened the door for the unbundling of monolithic apps. First came web apps, which could run without IIS or Windows underneath. Those web apps were unbundled again; individual services could be scaled and paid for on-demand: Azure Services.
The Serverless Challenge
Azure Functions grabbed my attention at a meetup. One of the presenters spoke about the advantages it brought in dealing with the traffic demands of the new ordering system for Dominos Pizza.
I wanted to experiment with Functions, but I needed a problem to solve. It had to bes something 'big', something that would prove a challenge to design a 'traditional' web app infrastructure around.
I decided an analytics platform fit the bill, so I set myself the challenge of building the whole thing serverless:
The architecture would consist of the following:
- Collector: An Azure Function built to collect incoming events from pages with the tracking script installed (Azure Functions can be triggered by various inputs, including an HTTP call). If traffic spikes, the function can automatically scale up to handle the load.
- Service Bus: A 'buffer' for incoming events, the service bus can hold captured events until the rest of the infrastructure has a chance to process.
- Processor: The service bus can trigger an Azure Function to process data, in this case the analytics event, and save it to the database. It's also straightforward to do additional processing inside functions, in this case I'm calling out to a third party to parse collected user agents.
- Analytics Store: Standard basic-tier Azure SQL Server database, the most expensive part of the infrastructure at ~£5/mo for up to 2GB storage. I did consider Azure Table Storage as an alternative, but it seems if you want to do reporting, SQL is still your best friend it seems...
- Reporting Services: Another set of Azure Functions which can query the SQL database and transform the resulting data into more usable forms such as daily views, popular browsers, etc.
- Analytics Dashboard: I did consider another Azure Function to serve out the dashboard, but I found Azure Blob Storage can now serve static websites, which was ideal as all I needed to build was a simple react app to call the reporting API's (remembering to configure CORS correctly!).
After a weekend of hacking, the resulting project "launched" (see the demo here)! The solution has been generally stable since 'launch', and has since collected a few thousands analytics events from another of my side projects with very few issues (e.g. all bugs were of my own making).
Anatomy of a Function
Here's a sample Function from my analytics experiment. This particular Function takes information from a client's browser, places it into a Service Bus for processing, and returns a blank pixel (hence 'tracking pixel'):
Azure Functions look a lot like standard MVC or Web API controllers, with a few extra attributes:
- "FunctionName": Obvious one to start, this name will be shown in the Azure Portal.
- "HttpTrigger": Triggers are what kick off the function, this one is triggered by an HTTP message (get or post), but you can write timed triggers, service bus triggers, and so on.
- "ServiceBus": Microsoft has written a few modules for Functions which make connecting to various services easy. In this example, the attribute provides an out parameter that you can place your message to the service bus into.
You'll see model binding and validation is being handled 'manually' in this case. I've yet to experiment with making this more 'MVC-like' with model binding and so on, maybe in v2!
The highs, the lows
As with all new tech, there are bound to be issues. I found the experience of building Azure Functions on the Mac version of Visual Studio to be... 'ok'. There were various issues with package versions and runtimes etc, which did muddy the waters a bit for a newbie like me.
When I finally got my environment set up correctly, I found setting up and publishing Functions to Azure a doddle. The experience of coding functions was also very familiar; I could make use of standard .Net core packages like Entity Framework and HttpClient. Nice.
For applications that need to scale and contract, paying on a 'consumption' model makes sense, especially if your architecture is easier to scale and contract with demand. In larger architectures I can see Functions taking on the role of the "glue" between components by making use of their various triggers: HTTP, Service Bus, Timers, etc.
When it comes to downsides, all the old arguments about microservices come into play: extra care needs to be taken when dealing with versions and deployments, especially if code libraries are going to be shared between services and apps (that EF schema you decided to share seemed like a good idea at the time!), and tracking down bugs and missing data becomes that little bit more difficult than before.
In conclusion, I'd recommend you check out Functions before you start your next .Net project - it may save you trying to find a safe place to run yet another console app!