Just ask questions along the way
mail@sjkp.dk
@simped
http://wp.sjkp.dk
Virtual Machines | Container | Serverless | |
---|---|---|---|
Unit of scale | Machine | Application | Function |
Abstraction | Hardware | OS | Language Runtime |
Configure | Machine, Storage, Networking, OS | Run server, configure applications, scaling | Run code on demand |
Execution | Multi-threaded, multi-task | Multi-threaded, single task | Single threaded, single taks |
Execution time | Hours to months | Minutes to days | Microseconds to seconds |
Unit of Cost | Per VM per hour | Per VM per hour | Per memory/second per request |
Amazon | EC2 | ECS | AWS Lambda |
Azure | Azure Virtual Machines | ACS | Azure Functions |
Functions | Web Jobs | Flow | Logic Apps | |
---|---|---|---|---|
Audience | Developers | Developers | End users | IT Pros |
Design Tools | VS, Browser | VS | Browser | VS, Browser |
Scale | Dynamic/App plan | App Plan | ? | App Plan/Dynamic |
Service Plan
Web App
Func App 1
Func App 2
Azure Blob Storage
Central Listener
Function Runtime
|---data
| |---Functions
| |---sampledata
| | QueueTriggerCSharp1.dat
| |
| |---secrets
| host.json
| queuetriggercsharp1.json
|
|---LogFiles
| |---Application
| | |---Functions
| | |---function
| | | ----QueueTriggerCSharp1
| | |---Host
| | debug_sentinel
| |
| |---kudu
| |---deployment
| |---trace
| 2017-03-05T09-50-51_e92a00_001_Startup_GET__0s.xml
| RD000D3AB10CF5-fa094f48-f993-47b4-8b65-8a435c854163.txt
|
|---site
|---deployments
| ----tools
|---diagnostics
| settings.json
|
|---locks
|---tools
|---wwwroot
| host.json
| hostingstart.html
|
|---QueueTriggerCSharp1
function.json
readme.md
run.csx
{
"bindings": [
{
"type": "queueTrigger",
"direction": "in",
"queueName": "image-resize"
},
{
"type": "blob",
"name": "original",
"direction": "in",
"path": "images-original/{name}"
},
{
"type": "blob",
"name": "resized",
"direction": "out",
"path": "images-resized/{name}"
}
]
}
{
"disabled": true,
"scriptFile": "test.js",
"bindings": [
{
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 */5 * * *",
"runOnStartup": true
}
]
}
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.ProjectOxford.Face": "1.1.0"
}
}
}
}
using System;
using Microsoft.ProjectOxford.Face; //Just reference the nuget with using
public static void Run(string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed : {myQueueItem}");
}
#r "Microsoft.WindowsAzure.Storage"
//#r "MyCustomDll.dll"
#load "../shared/models.csx"
using System;
using System.Net.Http;
using Microsoft.ProjectOxford.Face;
using Newtonsoft.Json;
using Microsoft.WindowsAzure.Storage;
public static void Run(string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed : {myQueueItem}");
var client = new HttpClient();
var obj = JsonConvert.SerializeObject(new MyTestClass {Name = "hello world"});
}
public class MyTestClass {
public int Id {get;set;}
public string Name {get;set;}
}
models.csx
{
// The unique ID for this job host. Can be a lower case GUID
// with dashes removed (Required)
"id": "9f4ea53c5136457d883d685e57164f08",
// Value indicating the timeout duration for all functions.
// In Dynamic SKUs, the valid range is from 1 second to 5 minutes and the default value is 5 minutes.
// In Paid SKUs there is no limit and the default value is null (indicating no timeout).
"functionTimeout": "00:05:00",
// Configuration settings for 'http' triggers. (Optional)
"http": {
// Defines the default route prefix that applies to all routes. Default is 'api'.
// Use an empty string to remove the prefix.
"routePrefix": "api"
},
// Set of shared code directories that should be monitored for changes to ensure that
// when code in these directories is changed, it is picked up by your functions
"watchDirectories": [ "Shared" ],
// Array of functions to load. Only functions in this list will be enabled.
"functions": [ "QueueProcessor", "GitHubWebHook" ]
// Configuration settings for 'queue' triggers. (Optional)
"queues": {
// The maximum interval in milliseconds between
// queue polls. The default is 1 minute.
"maxPollingInterval": 2000,
// The number of queue messages to retrieve and process in
// parallel (per job function). The default is 16 and the maximum is 32.
"batchSize": 16,
// The number of times to try processing a message before
// moving it to the poison queue. The default is 5.
"maxDequeueCount": 5,
// The threshold at which a new batch of messages will be fetched.
// The default is batchSize/2.
"newBatchThreshold": 8
},
// Configuration settings for 'serviceBus' triggers. (Optional)
"serviceBus": {
// The maximum number of concurrent calls to the callback the message
// pump should initiate. The default is 16.
"maxConcurrentCalls": 16,
// The default PrefetchCount that will be used by the underlying MessageReceiver.
"prefetchCount": 100,
// the maximum duration within which the message lock will be renewed automatically.
"autoRenewTimeout": "00:05:00"
},
// Configuration settings for 'eventHub' triggers. (Optional)
"eventHub": {
// The maximum event count received per receive loop. The default is 64.
"maxBatchSize": 64,
// The default PrefetchCount that will be used by the underlying EventProcessorHost.
"prefetchCount": 256
},
// Configuration settings for logging/tracing behavior. (Optional)
"tracing": {
// The tracing level used for console logging.
// The default is 'info'. Options are: { off, error, warning, info, verbose }
"consoleLevel": "verbose",
// Value determining what level of file logging is enabled.
// The default is 'debugOnly'. Options are: { never, always, debugOnly }
"fileLoggingMode": "debugOnly"
},
// Configuration settings for Singleton lock behavior. (Optional)
"singleton": {
// The period that function level locks are taken for (they will auto renew)
"lockPeriod": "00:00:15",
// The period that listener locks are taken for
"listenerLockPeriod": "00:01:00",
// The time interval used for listener lock recovery if a listener lock
// couldn't be acquired on startup
"listenerLockRecoveryPollingInterval": "00:01:00",
// The maximum amount of time the runtime will try to acquire a lock
"lockAcquisitionTimeout": "00:01:00",
// The interval between lock acquisition attempts
"lockAcquisitionPollingInterval": "00:00:03"
}
}
{
"disabled": false,
"scriptFile": "bin/SJKP.PrecompiledFunctions.dll",
"entryPoint": "SJKP.PrecompiledFunctions.Functions.Run",
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in"
},
{
"name": "res",
"type": "http",
"direction": "out"
}
]
}
/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.stagingDirectory)"
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_ZRS",
"Premium_LRS"
],
"metadata": {
"description": "Storage Account type"
}
},
"functionAppName": {
"type": "string",
"defaultValue": "sjkptestfunction"
},
"functionAppDomainName": {
"type": "string",
"defaultValue": "myfunc.sjkp.dk"
}
},
"variables": {
"storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]",
"dynamicAppServicePlanName": "[concat('dynamic', uniqueString(resourceGroup().id))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"properties": {
"accountType": "[parameters('storageAccountType')]"
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2015-04-01",
"name": "[variables('dynamicAppServicePlanName')]",
"location": "[resourceGroup().location]",
"properties": {
"name": "[variables('dynamicAppServicePlanName')]",
"computeMode": "Dynamic",
"sku": "Dynamic"
}
},
{
"apiVersion": "2015-08-01",
"type": "Microsoft.Web/sites",
"name": "[parameters('functionAppName')]",
"location": "[resourceGroup().location]",
"kind": "functionapp",
"properties": {
"name": "[parameters('functionAppName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('dynamicAppServicePlanName'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('dynamicAppServicePlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"resources": [
{
"apiVersion": "2015-08-01",
"type": "hostNameBindings",
"name": "[parameters('functionAppDomainName')]",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('functionAppName'))]"
],
"tags": {
"displayName": "hostNameBinding"
},
"properties": {
"domainId": null,
"hostNameType": "Verified",
"siteName": "variables('webAppName')"
}
},
{
"apiVersion": "2016-03-01",
"name": "appsettings",
"type": "config",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-05-01-preview').key1,';')]",
"AzureWebJobsDashboard": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-05-01-preview').key1,';')]",
"FUNCTIONS_EXTENSION_VERSION": "latest"
}
},
{
"apiVersion": "2015-08-01",
"name": "TestFunction",
"type": "functions",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
],
"properties": {
"config": {
"bindings": [
{
"authLevel": "anonymous",
"name": "req",
"type": "httpTrigger",
"direction": "in"
},
{
"name": "res",
"type": "http",
"direction": "out"
}
]
},
"files": {
"run.csx": "using System.Net;\r\npublic static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)\r\n{\r\n log.Info($\"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}\");\r\n\r\n // parse query parameter\r\n string name = req.GetQueryNameValuePairs()\r\n .FirstOrDefault(q => string.Compare(q.Key, \"name\", true) == 0)\r\n .Value;\r\n\r\n // Get request body\r\n dynamic data = await req.Content.ReadAsAsync<object>();\r\n\r\n // Set name to query string or body data\r\n name = name ?? data?.name;\r\n\r\n return name == null\r\n ? req.CreateResponse(HttpStatusCode.BadRequest, \"Please pass a name on the query string or in the request body\")\r\n : req.CreateResponse(HttpStatusCode.OK, \"Hello \" + name);\r\n}"
}
}
}
]
}
],
"outputs": {
}
}
using System;
using System.Threading;
public static async Task Run(string input, TraceWriter log, CancellationToken cancel)
{
log.Info($"C# manually triggered function called with input: {input}");
do {
log.Info($"Output {DateTime.Now}");
await Task.Delay(2500);
} while(!cancel.IsCancellationRequested);
log.Info($"stopping: {cancel.IsCancellationRequested}");
}
"queues": {
// The maximum interval in milliseconds between
// queue polls. The default is 1 minute.
"maxPollingInterval": 2000,
// The number of queue messages to retrieve and process in
// parallel (per job function). The default is 16 and the maximum is 32.
"batchSize": 16,
// The number of times to try processing a message before
// moving it to the poison queue. The default is 5.
"maxDequeueCount": 5,
// The threshold at which a new batch of messages will be fetched.
// The default is batchSize/2.
"newBatchThreshold": 8
},
#r "Microsoft.WindowsAzure.Storage"
using System;
using System.Configuration;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
public static void Run(string input, TraceWriter log)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["AzureWebJobsStorage"]);
var queueClient = storageAccount.CreateCloudQueueClient();
var queue = queueClient.GetQueueReference("binfiles-poison");
var outqueue = queueClient.GetQueueReference("binfiles");
CloudQueueMessage msg = null;
int i = 0;
do
{
msg = queue.GetMessage();
if (msg != null)
{
log.Info($"Processing queue item {i++}");
outqueue.AddMessage(new CloudQueueMessage(msg.AsString));
queue.DeleteMessage(msg);
}
} while (msg != null);
}
https://[function-app-name].scm.azurewebsites.net
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ExecutionContext ctx, TraceWriter log)
{
log.Info($"C# HTTP trigger invocationId: {ctx.InvocationId}");
return req.CreateResponse(HttpStatusCode.OK, "ctx.InvocationId");
}