Last month, when I wrote a blog on How to use GrapeCity Documents with Azure Functions, I wanted to write another post to dive into Azure Functions in further detail. If, like me, you aren't a big fan of App Service and Service Fabric because of the limitations they bring, Azure Functions will fit in your metaphoric Goldilocks zone. While App Service is stateless, and Service Fabric is stateful, Functions, on the other hand, gives you the freedom of developing both stateless and stateful microservices. This article is an overview of Azure Functions and discusses why you may want to choose Azure Functions as your microservice.
App Service and Service Fabric expect tons of configurations resulting in a steep learning curve and decreased productivity. Functions, in comparison, are like an easy switch for quick development. It can be argued that Functions are opinionated to an extent. Due to this, you benefit from faster development. Also, you have complete freedom of architecting your use cases with any third-party dependencies, in almost any major programming language.
Although Functions may not provide everything that App Services and Service Fabric can offer, in most cases, they are a more practical and productive choice.
Functions are feature-packed FaaS offerings from Microsoft Azure that enable you to run your code directly on-demand in Azure’s infrastructure. Think of them as a pieces of code that are invoked by triggers, wherein you do not have to worry about the underlying infrastructure. They scale on demand and you pay only for what you get. This frees you from countless design decisions that you would otherwise have to make to write a cost-efficient service.
You can choose any programming language of your choice from C#, F#, Node.js, Java, Python, PHP, etc., and bring in dependencies from NPM, NuGet, and Maven. With Function’s small unit of deployment, you can minimize the efforts taken to build and configure your DevOps pipeline. Azure Functions’ core concept revolves around a piece of code or function. This function, when combined with events and data, gives a complete picture of what Azure Functions should be.
Let’s understand about each of these in little detail.
A function is a central concept in Azure Functions. You can write code for a function, in any language of your choice, and save the code and configuration files in the same folder. Azure Functions Runtime reads the configuration file to determine the events to be monitored and how to pass data into and return data from function execution.
Triggers are ways to start the execution of your code. A function must always have a single trigger. Triggers have data that becomes the payload that invokes functions. There are multiple triggers available with Azure Functions.
The following are several triggers, along with the information on when they trigger.
The following function can reroute your API requests based on a request parameter.
[FunctionName("MyProxyService")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
TraceWriter log)
{
log.Info("Requsting proxy service.");
// parse query parameter
string region = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "region", true) == 0)
.Value;
switch (region)
{
case "us":
myRegionEndPoint = "http://myservice.companyname.com/us/";
break;
case "jp":
myRegionEndPoint = "http://myservice.companyname.com/jp/";
break;
default:
req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a valid region");
break;
}
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(new HttpMethod("PATCH"), myRegionEndPoint);
try
{
response = await (await client.SendAsync(request)).Content.ReadAsStringAsync());
req.CreateResponse(HttpStatusCode.OK, response);
}
catch (HttpRequestException ex)
{
req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a valid region");
}
}
}
Syntax:
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, TraceWriter log)
{
if(myTimer.IsPastDue)
{
log.Info("Timer is running late!");
}
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("GitHubWebhookTriggerCSharp ")]
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Extract github comment from request body
string gitHubComment = data?.comment?.body;
return req.CreateResponse(HttpStatusCode.OK, "From Github:" + gitHubComment);
}
Webhooks help you integrate Functions with your service/application which exposes Webhooks to broadcast an event. This type of function can be very helpful in triggering your CI service.
[FunctionName("CosmosTrigger")]
public static void Run([CosmosDBTrigger(
databaseName: "ToDoItems",
collectionName: "Items",
ConnectionStringSetting = "CosmosDBConnection",
LeaseCollectionName = "leases",
CreateLeaseCollectionIfNotExists = true)]IReadOnlyList<Document> documents,
TraceWriter log)
{
if (documents != null && documents.Count > 0)
{
log.Info($"Documents modified: {documents.Count}");
log.Info($"First document Id: {documents[0].Id}");
}
}
[FunctionName("BlobTriggerCSharp")]
public static void Run([BlobTrigger("samples-workitems/{name}")] Stream myBlob, string name, TraceWriter log)
{
log.Info($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
}
[FunctionName("QueueTrigger")]
public static void QueueTrigger(
[QueueTrigger("myqueue-items")] string myQueueItem,
TraceWriter log)
{
log.Info($"C# function processed: {myQueueItem}");
}
[FunctionName("EventHubTriggerCSharp")]
public static void Run([EventHubTrigger("samples-workitems", Connection = "EventHubConnectionAppSetting")] string myEventHubMessage, TraceWriter log)
{
log.Info($"C# Event Hub trigger function processed a message: {myEventHubMessage}");
}
[FunctionName("ServiceBusQueueTriggerCSharp")]
public static void Run(
[ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBusConnection")]
string myQueueItem,
Int32 deliveryCount,
DateTime enqueuedTimeUtc,
string messageId,
TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
log.Info($"EnqueuedTimeUtc={enqueuedTimeUtc}");
log.Info($"DeliveryCount={deliveryCount}");
log.Info($"MessageId={messageId}");
}
Bindings are ways to simplify coding for data input and output. Bindings are optional, and a function can have multiple input and output bindings.
Azure Functions integrate with most of the Azure services as well as some of the third-party services. These integrations can behave as triggers as well as bindings for your function.
Suppose you want to write a new row to the Azure Table storage whenever a new message appears in Azure Queue storage. This scenario can be implemented using an Azure Queue storage trigger and an Azure Table storage output binding.
Functions are a good solution for integrating systems, process granular data, build small microservices and work with IoTs. To facilitate various types of scenarios, you have numerous triggers.
Let’s start with a problem statement: "Send a weekly sales report to all the stakeholders of product X."
From this statement, you can identify that you must write some code that would run in a schedule and send a certain report to a recipient. It is important to understand that the problem can be solved by a function. It is trivial to write a code that generates some reports. However, to schedule them in order in a reliable way can be tricky. The function uses CRON expression to configure schedules.
A CRON expression looks like this:
{second} {minute} {hour} {day} {month} {day-of-week}
Some CRON examples are:
Example | When triggered |
0 0 12 ? | Fire at 12:00 PM (noon) every day |
0 15 10 ? | Fire at 10:15 AM every day |
0 15 10 L ? | Fire at 10:15 AM on the last day of every month |
0 15 10 ? 6L 2002-2005 | Fire at 10:15 AM on every last friday of every month during the years 2002, 2003, 2004, and 2005 |
0 15 10 ? 2005 | Fire at 10:15 AM every day during the year 2005 |
0 14 * ? | Fire every minute starting at 2:00 PM and ending at 2:59 PM, every day |
0 11 11 11 11 ? | Fire every November 11 at 11:11 AM |
Web services are usually invoked outside its environment, irrespective of its design and architecture. So, if you are writing a web service you should then have to create some kind of 'executor' or 'invoker' process. A desktop service, on the other hand, needs a compute instance to run. So, if you do not have an idle or unutilized VM, I think buying a new VM for a single service is an overkill.
A Timer Function fits perfectly into our use case, because of the advantage it brings with inbuilt schedulers. We will create an Azure Functions app that contains a function that would generate a sales report from a database. You may use Visual Studio or Visual Studio Code to build an Azure Functions Project. Let's look at both of them.
Pre-requisites:
Step 1. Create new Azure Functions Project
“Start Visual Studio Code and click on Azure Icon on the activity bar:
If you do not see the Azure icon, you should install Azure Functions Extensions for VS Code, the link to which is provided above.
Click on the Functions folder iconto create a new Functions project. Select C# as your language.
Step 2. Add Azure Functions
Click on Functions iconto create a new function “GenerateScheduleReport”.
You should be choosing TimerTriggered template with Anonymous access rights.
Step 3. Generate Report
Write you report generation code in a C# class.
using System.Threading.Tasks;
namespace GrapeCity.Blogs
{
public class Report
{
public async Task Generate ()
{
// Write your report creation logic here
}
}
}
Step 4. Schedule the report
Since we must schedule the report weekly, we are choosing Monday 8 AM as report generation time. The CRON to be expected here is:
0 0 8 ? * MON *
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json.Linq;
namespace GrapeCity.Blogs
{
public static class GenerateScheduleReport
{
[FunctionName("GenerateScheduleReport ")]
public static async Task<HttpResponseMessage> Run([TimerTrigger("0 0 8 ? * MON *")]TimerInfo myTimer, TraceWriter log)
{
if(myTimer.IsPastDue) {
Report report = new Report();
await report.Generate();
}
}
}
}
Step 5. Run the report
PPress F5 to build and run your function locally.
Pre-requisites:
For Visual Studio, all the steps are identical as above; however, the step to create a Functions project is a little different. See the following steps that are different in Visual Studio.
Azure provides you two kinds of plans for Functions: App Service Plan and Consumption Plan.
Note: With a Consumption Plan you are billed only when your function is executed.
The Consumption Plan allocates computation power while your code is running. With this, you are billed only when your function is executed. Your application is scaled out when your function needs additional power and is scaled down when your code is not running.
In an App Service Plan, you can scale between tiers to allocate a different amount of resources. Your app runs on VMs on Basic, Standard, Premium, and Isolated SKUs. This is also the case in App Service apps. You should choose the App Service Plan only if your function is running continuously, or if the Consumption Plan has any limitation. In most cases, developers tend to choose Consumption Plans.
If you run on an App Service Plan, you should enable the Always on setting so your function app runs correctly. On an App Service Plan, the Functions Runtime goes idle after a few minutes of inactivity, so only HTTP triggers will "wake up" your functions. Always on is available only on an App Service Plan. On a Consumption Plan, the platform activates function apps automatically.
The following are the set of conditions to help you decide if your scenario fits Azure Functions’ capabilities.
Azure Functions are platforms locked in with Azure. This means, once your app is written and deployed, you cannot move to any other serverless or computing solution. You can always update the code, but a function can be deployed only on Azure Functions. There are some third-party packages like serverless, which allow some degree of platform independence. However, the binding technique is unique to Azure. This may change in the future, since Functions are developed in open-source. If choosing Azure is not an issue for you (and it won’t be if you are reading this), Functions are for you.
Functions are small and deployed singularly. Their design favors you to write a simple method call. You can have a bunch of functions written, and deployed independently as Azure Functions. With an App Service or Service Fabric, their focus is more on deploying a full-scale service. If they are overkill for your scenario, Functions are for you.
Functions on App Service Plan have some control over the amount of resource you would need. However, for most of the cases, there is hardly any configuration required or control provided for and by Azure to you. Serverless systems are supposed to abstract the infrastructure. You cannot maintain a service pool since only one would be provided to you. If these are things that don’t seem necessary to you, you just want to run your code, Functions are for you.
For most of us, learning new technology comes with the profession. Most of us don't dread it. But for the sake of productivity, learning new technology creates unnecessary overhead in a software lifecycle which can also be avoided. Functions don't require learning any new framework or design patterns. If you can write a method in any of the languages it supports, you can write Functions.
You can bring your dependencies from your favorite package managers. Depending on the technology you have selected, you can choose among NuGet, Maven, Pip, NPM, and many more.
Choosing Azure Functions can be the best (or worst) design decision you have taken, depending on your use case. Most of the time, Functions can allow you to write quickly, without the overhead of customization and configurations. You can easily integrate multiple Azure or third-party services with an unprecedented degree of ease.
With so many SaaS vendors and providers moving towards a more decoupled and independent architecture, Functions are becoming more relevant for you every day.
Empower your development. Build better applications. Try GrapeCity's Tools for JavaScript and .NET