Freya, Hopac, Kestrel:
Building a Concurrent, Functional
Web Service for .NET Core
Marcus Griep
@neoeinstein�https://goo.gl/L77RHj
Marcus Griep
Lead Software Engineer
Vistaprint
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Hopac
@neoeinstein�https://goo.gl/L77RHj
Functional
Web Services
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
OS: Debian 8
GCE 4 vCPU, 4GB
Published self-contained
5.98
@neoeinstein�https://goo.gl/L77RHj
freya.io
@neoeinstein�https://goo.gl/L77RHj
NuGet
Freya
Freya.Hopac
@neoeinstein�https://goo.gl/L77RHj
https://www.myget.org
/F/neoeinstein/api/v3/index.json
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Should I use Freya?
@neoeinstein�https://goo.gl/L77RHj
Should I use Freya?
@neoeinstein�https://goo.gl/L77RHj
Hopac
Concurrent ML-style constructs for F#
@neoeinstein�https://goo.gl/L77RHj
Should I use Hopac?
@neoeinstein�https://goo.gl/L77RHj
CPU
CPU
CPU
CPU
Hopac
Thread Pool
Cooperative
Preemptive
@neoeinstein�https://goo.gl/L77RHj
Should I use Hopac?
@neoeinstein�https://goo.gl/L77RHj
job { … }
@neoeinstein�https://goo.gl/L77RHj
Alt<'a>
Promise<'a>
IVar<'a>
MVar<'a>
>>=
^=>
*<-
*<+->=
Stream<'a>
Mailbox<'a>
@neoeinstein�https://goo.gl/L77RHj
Hopac
Async
@neoeinstein�https://goo.gl/L77RHj
Kestrel
The ASP.NET Core web server
@neoeinstein�https://goo.gl/L77RHj
Should I use Kestrel?
@neoeinstein�https://goo.gl/L77RHj
Should I use Kestrel?
@neoeinstein�https://goo.gl/L77RHj
OWIN
Open Web Interface for .NET
@neoeinstein�https://goo.gl/L77RHj
Should I use Kestrel?
@neoeinstein�https://goo.gl/L77RHj
Suave
@neoeinstein�https://goo.gl/L77RHj
Functional
Web Services
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Kestrel
Freya
Apache / nginx / IIS
OWIN
Suave
Suave
ASP.NET�MVC
@neoeinstein�https://goo.gl/L77RHj
ASP.NET
type HelloHandler = HelloHandler of (string option -> string)
module HelloHandler =
let run (HelloHandler f) x = f x
let impl = HelloHandler <| function
| Some name -> sprintf "Hello %s!" name
| None -> "Hello World!"
[<Route("hello")>]
type HelloController (handler:HelloHandler) =
inherit Controller()
[<HttpGet>]
member x.Get() =
base.Ok(HelloHandler.run handler None)
[<HttpGet("{name}")>]
member x.GetByName(name: string) =
base.Ok(HelloHandler.run handler (Some name))
@neoeinstein�https://goo.gl/L77RHj
Suave
let helloWorld : WebPart =
choose
[ GET >=> path "/hello" >=> (Successful.OK "Hello World!")
GET >=> pathScan "/hello/%s" (sprintf "Hello %s!" >> Successful.OK) ]
@neoeinstein�https://goo.gl/L77RHj
Freya
let sayHello = freya {
let! uO = Freya.Optic.get (Route.atom_ "name")
let helloStr =
uO
|> Option.map (sprintf "Hello, %s!")
|> Option.defaultValue "Hello, World!"
return Represent.text helloStr
}
let helloMachine = freyaMachine {
handleOk sayHello
}
let router = freyaRouter {
resource "/hello{/name}" helloMachine
}
let root = UriTemplateRouter.Freya router
@neoeinstein�https://goo.gl/L77RHj
ASP.NET
[<Route("hello")>]
type HelloController () =
[<HttpGet>]
member x.Get() =
[<HttpGet("{name}")>]
member x.GetByName(name: string) =
@neoeinstein�https://goo.gl/L77RHj
Suave
let helloWorld =
choose
[ GET >=> path "/hello"
GET >=> pathScan "/hello/%s" ]
@neoeinstein�https://goo.gl/L77RHj
Freya
let name_ = Route.atom_ "name"
let router = freyaRouter {
resource "/hello{/name}" helloMachine
}
@neoeinstein�https://goo.gl/L77RHj
ASP.NET
[<Authorize>]
type HelloController () =
type MyRequirement () =
interface IAuthorizationRequirement
type MyAuthHandler () =
inherit AuthorizationHandler<MyRequirement>()
member __.HandleRequirementAsync(ctx:AuthorizationHandlerContext, req:MyRequirement) =
ctx.Succeed(req)
Task.CompletedTask
type Startup () =
member __.ConfigureServices(svc:IServiceCollection) =
svc.AddSingleton<IAuthorizationHandler,MyAuthHandler>()
svc.AddIdentity<IdentityType, IdentityRole>()
.WithSomeBackingStore()
@neoeinstein�https://goo.gl/L77RHj
Suave
let checkAllowed (user,pass) = true
Authentication.authenticateBasic checkAllowed (Successful.OK "You're in!")
@neoeinstein�https://goo.gl/L77RHj
Freya
let checkValidAuth = freya { return true }
let checkAllowed = freya { return true }
let helloMachine = freyaMachine {
authorized checkValidAuth
allowed checkAllowed
handleOk (Represent.text "You're in!")
}
@neoeinstein�https://goo.gl/L77RHj
Freya
Suave
path "/hello" >=> choose
[ GET >=> Successful.OK "hello"
Writers.setHeader "Allow" "GET" >=> RequestErrors.METHOD_NOT_ALLOWED "" ]
let helloMachine = freyaMachine {
methods [GET; HEAD; OPTIONS]
handleOk (Represent.text "You're in!")
}
@neoeinstein�https://goo.gl/L77RHj
Freya
let helloMachine = freyaMachine {
availableMediaTypes [MediaType.Json; MediaType.Text]
availableContentCodings ContentCoding.GZip
availableCharsets Charset.Utf8
handleOk (fun (acpt: Acceptable) -> freya { … })
}
@neoeinstein�https://goo.gl/L77RHj
Freya
let helloMachine = freyaMachine {
methods [GET; HEAD; OPTIONS; POST]
availableMediaTypes [MediaType.Json; MediaType.Text]
availableContentCodings ContentCoding.GZip
availableCharsets Charset.Utf8
acceptableMediaTypes MediaType.Json
handlePost doThePost
handleOk (fun (acpt: Acceptable) -> freya { … })
}
@neoeinstein�https://goo.gl/L77RHj
Freya
let helloMachine = freyaMachine {
cors
corsMaxAge (60*60*24)
corsOrigins (SerializedOrigin.parse "https://example.com")
methods [GET; HEAD; OPTIONS; POST]
availableMediaTypes [MediaType.Json; MediaType.Text]
availableContentCodings ContentCoding.GZip
availableCharsets Charset.Utf8
acceptableMediaTypes MediaType.Json
handlePost doThePost
handleOk (fun (acpt: Acceptable) -> freya { … })
}
@neoeinstein�https://goo.gl/L77RHj
Freya
let core = freyaMachine {
cors
corsMaxAge (60*60*24)
corsOrigins (SerializedOrigin.parse "https://example.com")
methods [GET; HEAD; OPTIONS]
availableContentCodings ContentCoding.GZip
availableCharsets Charset.Utf8
}
let helloMachine = freyaMachine {
including core
methods [GET; HEAD; OPTIONS; POST]
availableMediaTypes [MediaType.Json; MediaType.Text]
acceptableMediaTypes MediaType.Json
handlePost doThePost
handleOk (fun (acpt: Acceptable) -> freya { … })
}
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
@neoeinstein�https://goo.gl/L77RHj
Freya Machines
Decision Trees
Is the server in a state to respond?� true: continue� false: return "503 Service Unavailable"��Is the version of HTTP being requested supported?� true: continue� false: return "505 HTTP Version Not Supported"
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Optimization
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Prototype
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Configuration
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Literal elimination
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Unary elimination
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Subgraph elimination
@neoeinstein�https://goo.gl/L77RHj
Freya Machine
Minimal
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Demo
@neoeinstein�https://goo.gl/L77RHj
Benchmarks
@neoeinstein�https://goo.gl/L77RHj
/hello{/name}
@neoeinstein�https://goo.gl/L77RHj
256 connections
10 sec
Google Compute Engine Instance
4 vCPUs, 4GiB RAM
Google Compute Engine Instance
2 vCPUs, 2GiB RAM
wrk
dotnet helloWorld.dll
/hello{/name}
@neoeinstein�https://goo.gl/L77RHj
wrk -c 256 -t 32 -d 10 --latency http://104.155.85.181:8080/hello
OS: Debian 8
GCE 4 vCPU, 4GB
Published self-contained
@neoeinstein�https://goo.gl/L77RHj
wrk -c 256 -t 32 -d 10 --latency http://104.155.85.181:8080/hello/fred
OS: Debian 8
GCE 4 vCPU, 4GB
Published self-contained
@neoeinstein�https://goo.gl/L77RHj
OS: Debian 8
GCE 4 vCPU, 4GB
Published self-contained
5.98
@neoeinstein�https://goo.gl/L77RHj
OS: Debian 8
GCE 4 vCPU, 4GB
Published self-contained
@neoeinstein�https://goo.gl/L77RHj
Functional
Web Service
@neoeinstein�https://goo.gl/L77RHj
.NET Core
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
Kestrel
OWIN
Hopac
Async
@neoeinstein�https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj
freya.io
https://goo.gl/L77RHj
@neoeinstein�https://goo.gl/L77RHj