Azure Functions
Making serverless great again!
What do you need?
- Machine
- OS
- Web Server
- Queue
- Storage
- ...
All you need is...
public static void Run(TimerInfo myTimer, ICollector<string> queueItems, TraceWriter log)
{
List<string> fileNames;
DownloadFilesFromFtp(out fileNames);
foreach(var file in fileNames) {
var content = File.ReadAllLines(fileName);
var serializedData = JsonConvert.SerializeObject(content);
queueItems.Add(serializedData);
}
}public static void Run(TimerInfo myTimer, ICollector<string> queueItems, TraceWriter log)
{
queueItems.Add(...);
}About Me

Blog: http://blog.codenova.pl/
Twitter: @Kamil_Mrzyglod
GitHub: https://github.com/kamil-mrzyglod
StackOverflow: https://stackoverflow.com/users/1874991/kamo
LinkedIn: www.linkedin.com/in/kamil-mrzygłód-31470376
Serverless - what is it?
Source: http://www.commitstrip.com/en/2017/04/26/servers-there-are-no-servers-here/

IaaS, PaaS, SaaS...

http://www.episerver.com/contentassets/c2298831dbc04581ab7a6af1df35dc0d/pizza.jpg
Azure Functions

https://cmatskas.com/content/images/2017/01/functions.jpg
Scenarios



Source: https://azure.microsoft.com/pl-pl/services/functions/
Integrations





Azure CosmosDB
Azure Event Hub
Azure Notification Hub
Azure Storage
Azure Mobile Apps
GitHub
Languages




Hosting Plans
App Service Plan
Consumption Plan
Scaling

Scaling #2
protected virtual async Task<bool> TryAddIfLoadFactorMaxWorker(
string activityId,
IEnumerable<IWorkerInfo> workers,
IWorkerInfo manager)
{
var loadFactorMaxWorker = workers.FirstOrDefault(w => w.LoadFactor == int.MaxValue);
if (loadFactorMaxWorker != null)
{
_tracer.TraceInformation(activityId, loadFactorMaxWorker, "Worker have int.MaxValue loadfactor.");
await RequestAddWorker(activityId, workers, manager, force: false);
return true;
}
return false;
}Pricing
| METER | PRICE | FREE GRANT |
|---|---|---|
| Execution Time | €0.000014/GB-s | 400,000 GB-s |
| Total Executions | €0.169 per million executions | 1 million executions |

Example
Average memory consumption - 128MB(0,125GB)
3 millions of executions(each 1 second)
Consumption cost: 3M-s * 0,125GB = 375 000GB-s(FREE)
So...
Execution cost: 3M * 0,169EUR = 0,507EUR
Total:
0,507EUR
Monitoring
Development
CSX
#r "System.Configuration"
#r "System.Data"
using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
public static async Task Run(TimerInfo myTimer, TraceWriter log)
{
var str = ConfigurationManager.ConnectionStrings["sqldb_connection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(str))
{
conn.Open();
var text = "UPDATE SalesLT.SalesOrderHeader " +
"SET [Status] = 5 WHERE ShipDate < GetDate();";
using (SqlCommand cmd = new SqlCommand(text, conn))
{
// Execute the command and log the # rows affected.
var rows = await cmd.ExecuteNonQueryAsync();
log.Info($"{rows} rows were updated");
}
}
}Precompiled functions
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using LicznikNET.vNext.FtpJob;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;
namespace LicznikNET.vNext.EventAggregator
{
public class EventAggregator
{
private const string FtpServerUri = "ftp://some-server.com";
private const string CurrentDirectory = "D:\\home\\site\\wwwroot\\EventAggregator\\";
private static readonly Regex Regex = new Regex(@"[0-9]{8}.txt");
private static TraceWriter _log;
private static ICollector<string> _queue;
public static void Run(TimerInfo myTimer, ICollector<string> queueItems, TraceWriter log)
{
List<string> fileNames;
_log = log;
_queue = queueItems;
DownloadFilesFromFtp(out fileNames);
RegisterContentInEventProcessor(fileNames);
}
}
}Precompiled functions #2
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;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("HttpTriggerCSharp")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Set name to query string or body data
name = name ?? data?.name;
return name == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}
}
}Configuration
{
"scriptFile": "LicznikNET.vNext.EventAggregator.dll",
"entryPoint": "LicznikNET.vNext.EventAggregator.EventAggregator.Run",
"disabled": false,
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
},
{
"type": "queue",
"name": "queueItems",
"queueName": "eventAggregator",
"connection": "StorageConnectionString",
"direction": "out"
}
]
}OSS
Dessert

Proxies
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxy1": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/api/{test}"
},
"responseOverrides": {
"response.body": "Hello, {test}",
"response.headers.Content-Type": "text/plain"
}
}
}
}
#############
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"proxy1": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/api/{test}"
},
"backendUri": "https://<AnotherApp>.azurewebsites.net/api/<FunctionName>",
"requestOverrides": {
"backend.request.headers.Accept": "application/xml",
"backend.request.headers.x-functions-key": "%ANOTHERAPP_API_KEY%"
}
}
}
}Durable Functions
#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"
public static async Task<long> Run(DurableOrchestrationContext backupContext)
{
string rootDirectory = backupContext.GetInput<string>();
if (string.IsNullOrEmpty(rootDirectory))
{
rootDirectory = Environment.CurrentDirectory;
}
string[] files = await backupContext.CallFunctionAsync<string[]>(
"E2_GetFileList",
rootDirectory);
var tasks = new Task<long>[files.Length];
for (int i = 0; i < files.Length; i++)
{
tasks[i] = backupContext.CallFunctionAsync<long>(
"E2_CopyFileToBlob",
files[i]);
}
await Task.WhenAll(tasks);
long totalBytes = tasks.Sum(t => t.Result);
return totalBytes;
}Resources
https://docs.microsoft.com/en-US/azure/azure-functions/functions-overview
https://martinfowler.com/articles/serverless.html
https://github.com/Azure/Azure-Functions
http://azureserverless.com/blog/
https://github.com/Azure/Azure-Functions/wiki/Samples-and-content
Questions?
https://docs.google.com/forms/d/e/1FAIpQLSfA0nB6xJHq-eGmtnCyec8CiqYk4m97XYz72jxKyJVPiVpfbA/viewform?c=0&w=1&includes_info_params=true
deck
By kamil_mrzyglod
deck
- 517