I have been getting into Azure lately and out of myriad of available services, I chose Azure Functions to explore first. In Part I, we learned about characteristics of Function As A Service. We also saw some features of Azure's Azure Functions offering as examples of those characteristics. In this post, we will dive into Azure Functions by learning about its key concepts and see some examples.
I will be referencing code snippets from a small web-app that I have worked on while learning Azure Functions. It uses multiple serverless Azure solutions and is implemented in dotnetcore(C# language) and React. If you are not familiar with C#, no worries, I will try to explain C# specific syntactical parts in the examples below.
The app gives real time share market updates to clients. Data is stored in Cosmos DB - an Azure storage solution. There are some Azure Functions written to manipulate and fetch the data. Azure SignalR provides real time updates to connected clients who are accessing the React based app on their browser. If you want to read more about it or see the code, have a look at my github repository:
The building blocks that make Azure Functions so powerful are its triggers and bindings. Trigger specifies when a function is to be executed. We can have a timer trigger, HTTP triggers, and also triggers can be set up for handling events happening in other Azure Services. Bindings allow you to connect to other services seamlessly. Azure needs a function.json file for executing functions, which has trigger and bindings information embedded in it. Let's talk about these concepts one by one:
- A trigger is what causes our function to run. Every function must have exactly one trigger. Suppose you want to add a message to queue when record is inserted in DB or schedule some code to run every 10 minutes, triggers can do that and much more. Azure supports timer trigger, HTTP trigger, Event Hub trigger, Blob trigger, Queue trigger and web-hooks too.
- One of the most important benefits of these triggers is that you don't have to write code to connect to other Azure services, this is done declaratively. You just specify the source trigger information, the data from the trigger event is received in the function parameter.
- To make sense of this, let's look at an example from the share market updates web-app - In below example, we are running the function whenever an update occurs in Cosmos DB. This updated document is reported to the connected SignalR client.
- If you are not familiar with C#, you will find the parameter declaration a bit weird. These are C# attributes used with function parameters, which allow us to decorate parameters with additional metadata. Attributes allow us to declaratively specify metadata about entities(class, methods, parameters, etc.) in the format [AttributeName(AttributeParameter1 = Value, AttributeParameter2 = Value, ...)].
- In case of Azure Functions using C#, attributes are used to specify trigger and bindings information in function parameters. FunctionName attribute specifies entry point method in the class. In above example, we have passed databaseName, collectionName as parameters to attribute class constructor and further ConnectionStringSetting, LeaseCollectionName, CreateLeaseCollectionIfNotExists as attribute parameters. This attribute provides metadata about updatedDocs function parameter specifying collection to monitor for changes. If any records in the collection are updated, this function will be triggered and updated records will be available in updatedDocs function parameter.
- We have specified Cosmos DB connection information with its trigger and SignalR hub information with output binding, inside function parameters. As you can see, there is no code written to open connection to Cosmos DB, fetch the data or close the connection!!! Similarly, there is no code written to open connection to SignalR hub, we have just specified connection information declaratively! These operations are performed behind the scenes by Azure Functions runtime and code written in appropriate SDKs based on triggers/bindings.
- Similar to triggers, bindings allow us to declaratively connect our function to other Azure Services. Unlike triggers, bindings are optional and you can have multiple bindings. Azure supports connecting to variety of services through bindings, e.g. storage services(blob, queue, table, Cosmos), event grids, event hubs, SignalR, twilio, etc. Moreover, you can also add your custom bindings.
- Bindings have direction - in, out and inout. These directions specify if we are loading data into the function from other Azure service or outputting data from function to service. In above example, we have used SignalR output binding, specified with [SignalR(HubName="notifications")]. Note that the direction for bindings is optional and can be inferred by the way we have specified parameters, e.g. using IReadOnlyList, IAsyncCollector in above example. The SignalR output binding will send updated share data to all connected clients. It also allows us to specify user Ids and groups.
- There are many trigger/binding specific attribute parameters supported using which we can do much more than what I have done in the above example.
- function.json is another important part of Azure Functions, along with the code that we write. It holds binding and trigger information in it, which is used by runtime for monitoring events, determining how to pass data to and from a function and for taking scaling decisions.
- This file is auto-generated if you are using a compiled language but for scripting languages, it will be your responsibility to provide one. For compiled languages, it is generated based on annotations(attributes in above example) that you have written. It can be edited directly through Azure Portal as well, but you shouldn't be doing so unless you are just trying things out.
- In case of Azure Functions using C#, you will find function.json generated in bin folder, one file per function in your function app. Function app is a collection of functions, a unit of Azure Functions deployment. Below file was generated for example above -
- Along with this, there are two function app level config files:
- host.json - holds runtime configuration
- local.settings.json - holds secrets such as connection strings and is not meant to be published to Azure.
Azure provides extensions for Visual Studio and VS code for local development of Azure Functions. It also supports Eclipse and IntelliJ. Functions can be deployed in multiple ways - GitHub actions, Azure DevOps, Azure App Service based deployments, etc.
This was my first time trying out serverless things and I must tell you, I have been fascinated by how much productive you can get in so little time. I hope you enjoyed these articles and I added something to your understanding of FaaS with Azure. Do let me know if any queries, will try my best to answer those.