- title : Azure Functions
- description : Introduction to Azure Functions with F#
- author : Mathias Brandewinder
- theme : night
- transition : default
- Just started using Azure Functions last week
- General (read "basic") introduction
- Some tips & tricks to get you started
- What are Azure Functions?
- An example: @fsibot
- Hands-on introduction
Process events with serverless code
Azure Functions is a serverless event driven experience that extends the existing Azure App Service platform. These nano-services can scale based on demand and you pay only for the resources you consume.
- A Function is a script which contains, well, a function - F#, C#, or Node.JS
- Triggers define what event trigger the function execution
- Runtime is based on WebJobs SDK, manages execution and scaling
- A Function App is a collection of Functions
This is a function, defined in filefoo.fsx
:
open System.Environment
let Run(timer: TimerInfo, log: TraceWriter) =
log.Info("Function was triggered!")
... where is the timer coming from?
A file function.json
in the same folder defines bindings:
{
"bindings": [
{
"schedule": "0 * * * * *",
"name": "timer",
"type": "timerTrigger",
"direction": "in"
}
],
"disabled": false
}
- uses a CRON expression
{second} {minute} {hour} {day} {month} {day of the week}
- defines the
timer
using as input (in
) in theRun
function. - every binding contains at least
type
,direction
,name
- Timer
- Storage (Blog, Queue, Table)
- HTTP/webhook
- Service Bus
- Notification Hubs, Mobile Apps, Event Hubs, DocumentDB...
Define your dependencies in a project.json
in the same folder:
{
"frameworks": {
"net46":{
"dependencies": {
""FSharp.Compiler.Service": "6.0.1"
}
}
}
}
- Some common dependencies are already there for you (Json.NET for instance)
- App Service Editor
- Continuous deployment
- Kudu (whatever that is)
- FTP
METER | PRICE | FREE GRANT (PER MONTH) |
---|---|---|
Execution Time | $0.000008/GB-s | 400,000 GB-s |
Total Executions | $0.20 per Million Executions | 1 Million Executions |
- GB-s: memory size in Gigabytes of your app by the sum of execution time in seconds
- this is preview price
- Functions are in preview
- Development experience in App Service Editor is currently primitive
- Classic vs. Dynamic
- F# support improving by the day (perf, documentation)
@fsibot is a side-project of mine; you can send F# code to @fsibot on Twitter, and it will evaluate the expression and respond with the result.
Original version used F#, deployed as Windows Service(s) using TopShelf, on a VM Rewrote entirely using Azure Functions last weekend
Application flow:
- did anyone mention @fsibot on Twitter?
- retrieve Tweet, User and Message
- extract code and run it using F# Compiler Service
- respond to the Tweet
3 functions:
- fsibotmentions: probe for mentions every 2 minutes, send them to a Queue "mentionsQueue"
- fsibotcompiler: dequeue message from "mentionsQueue", try to compile, send result to a Queue "responseQueue"
- fsibottweets: dequeue message from "responseQueue", format response, send response
- Twitter mentions: how do I know if a mention has already been processed?
- I cannot keep in memory the ID of the last processed Tweet
- Solution: read / write to a blob
- Heavy? Perhaps. But arguably good (fault-tolerant) design
So far I have found the easiest approach is to
- create types for messages
- serialize/deserialize using JSON
- represent messages as strings in the function signature
- note: outgoing messages use byref
Azure Functions
F# documentation
Greg Shackles
Michał Niegrzybowski
The great @fsibot caper
... let's create a simple demo function!
- Go to the Azure Portal: https://portal.azure.com
- Select + New > Function App > Create
- Select a name (ex: mathias-sfsharp-09-2016), subscription, new Resource Group (ex: mathias-sfsharp-09-2016), Classic App Service Plan, Pin to dashboard
- Be patient... (2 minutes?)
- Select New Function > Language F# > Empty, name it "TimerFunction"
- You should now have in Code:
open System
let Run(log: TraceWriter ) =
log.Verbose("F# function executed.");
- We need to add some bindings. Go to Integrate > New Trigger > Timer > Select. Leave the timer named "myTimer", the Schedule as is, and Save.
- Go back to Develop. Under Code, click "View Files". Select "function.json", you should see the following bindings:
{
"bindings": [
{
"type": "timerTrigger",
"name": "myTimer",
"schedule": "0 * * * * *",
"direction": "in"
}
],
"disabled": false
}
- Select run.fsx, and modify the code:
open System
let Run(myTimer: TimerInfo, log: TraceWriter ) =
log.Info("F# function executed.")
- Hit Save (which should now be red). In the Logs window, you should see the following:
2016-09-18T00:54:39.233 Script for function 'TimerFunction' changed. Reloading.
...
2016-09-18T00:54:40.201 Compilation succeeded.
2016-09-18T00:55:00.002 Function started (Id=cebd8264-49f4-4f28-9e72-abac866f05b3)
2016-09-18T00:55:00.002 F# function executed.
2016-09-18T00:55:00.002 Function completed (Success, Id=cebd8264-49f4-4f28-9e72-abac866f05b3)
- If you give it a minute, you should see a second message
2016-09-18T00:56:00.003 F# function executed.
2016-09-18T00:56:00.003 Function completed (Success, Id=9a3a6acc-78bc-4802-8c53-d83521be58b0)
... your timer function is now up-and-running :)
-
Let's use some configuration. Under "Search my functions", below "Monitor", click "Function app settings" > Configure app settings. In the app settings section, add a Key and Value, for instance "Hello" and "World", and save on the top-left corner.
-
Close by clicking [X] in the top-right corner
-
Go back to TimerFunction, and edit the code in run.fsx:
open System
open System.Configuration
let Run(myTimer: TimerInfo, log: TraceWriter ) =
let appSettings = ConfigurationManager.AppSettings
let value = appSettings.["Hello"]
log.Info("Hello, " + value)
log.Info("F# function executed.")
You should now see in the log:
2016-09-18T01:02:33.796 Script for function 'TimerFunction' changed. Reloading.
...
2016-09-18T01:02:35.280 Compilation succeeded.
2016-09-18T01:03:00.002 Function started (Id=3cf142c4-8993-4de2-b81d-ee54e54e5c8f)
2016-09-18T01:03:00.002 Hello, World
2016-09-18T01:03:00.002 F# function executed.
2016-09-18T01:03:00.002 Function completed (Success, Id=3cf142c4-8993-4de2-b81d-ee54e54e5c8f)
- Let's add a nuget package, say, FSharp.Data version 2.3.2. Go to Show Files, click +, add a file called project.json, paste in the following:
{
"frameworks": {
"net46":{
"dependencies": {
"fsharp.data": "2.3.2"
}
}
}
}
You should see in the logs something like this:
2016-09-18T01:13:27.926 Restoring packages.
2016-09-18T01:13:27.926 Starting NuGet restore
2016-09-18T01:13:28.941 Restoring packages for D:\home\site\wwwroot\TimerFunction\project.json...
2016-09-18T01:13:29.316 GET https://api.nuget.org/v3-flatcontainer/fsharp.data/index.json
2016-09-18T01:13:29.582 OK https://api.nuget.org/v3-flatcontainer/fsharp.data/index.json 268ms
...
2016-09-18T01:13:37.267 Packages restored.
2016-09-18T01:13:37.579 Script for function 'TimerFunction' changed. Reloading.
2016-09-18T01:13:39.938 D:\home\site\wwwroot\TimerFunction\run.fsx(4,9): warning FS1182: The value 'myTimer' is unused
2016-09-18T01:13:39.938 D:\local\Temp\tmp4047.fsx(9,38): warning FS988: Main module of program is empty: nothing will happen when it is run
2016-09-18T01:13:39.938 Compilation succeeded.
- We can now use fsharp.data in run.fsx:
open System
open System.Configuration
open FSharp.Data
let Run(myTimer: TimerInfo, log: TraceWriter ) =
let appSettings = ConfigurationManager.AppSettings
let value = appSettings.["Hello"]
log.Info("Hello, " + value)
let data = WorldBankData.GetDataContext()
let capital = data.Countries.France.CapitalCity
log.Info("Capital of France is " + capital)
log.Info("F# function executed.")
- Storage: how to create and use a Queue? Go to Integrate > Outputs > Azure Storage Queue > Select, and in the Storage Account Connection, select new; you should see here the storage account that was created for you as you created the function.
- You can Disable or Delete the function in Manage, and look at some more logs under Monitor.
... thank you!