Skip to content

Instantly share code, notes, and snippets.

@akimboyko
Last active May 28, 2023 13:00
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save akimboyko/e58e4bfbba3e9a551f05 to your computer and use it in GitHub Desktop.
Save akimboyko/e58e4bfbba3e9a551f05 to your computer and use it in GitHub Desktop.
Samples from "Actor-based Concurrency with F# and Akka.NET" http://bit.ly/FSharpAkkaNET
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// #Using Actor
// Actors are one of Akka's concurrent models.
// An Actor is a like a thread instance with a mailbox.
// It can be created with system.ActorOf: use receive to get a message, and <! to send a message.
// This example is an EchoServer which can receive messages then print them.
let system = ActorSystem.Create("FSharp")
type EchoServer =
inherit Actor
override x.OnReceive message =
match message with
| :? string as msg -> printfn "Hello %s" msg
| _ -> failwith "unknown message"
let echoServer = system.ActorOf(Props(typedefof<EchoServer>, Array.empty))
echoServer <! "F#!"
system.Shutdown()
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// #Simplify Actor
// There is a simpler way to define an Actor
let system = ActorSystem.Create("FSharp")
let echoServer =
spawn system "EchoServer"
<| fun mailbox ->
actor {
let! message = mailbox.Receive()
match box message with
| :? string as msg -> printfn "Hello %s" msg
| _ -> failwith "unknown message"
}
echoServer <! "F#!"
system.Shutdown()
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// #Actor Implementation
// An Actor is more lightweight than a thread. Millions of actors can be generated in Akka,
// the secret is that an Actor can reuse a thread.
//
// The mapping relationship between an Actor and a Thread is decided by a Dispatcher.
//
// This example creates 10 Actors, and prints its thread name when invoked.
//
// You will find there is no fixed mapping relationship between Actors and Threads.
// An Actor can use many threads. And a thread can be used by many Actors.
let system = ActorSystem.Create("FSharp")
type EchoServer(name) =
inherit Actor()
override x.OnReceive message =
let tid = Threading.Thread.CurrentThread.ManagedThreadId
match message with
| :? string as msg -> printfn "Hello %s from %s at #%d thread" msg name tid
| _ -> failwith "unknown message"
let echoServers =
[1 .. 10]
|> List.map(fun id -> let properties = [| string(id) :> obj |]
system.ActorOf(Props(typedefof<EchoServer>, properties)))
let rand = Random(1234)
for id in [1 .. 10] do
(rand.Next() % 10) |> List.nth echoServers <! sprintf "F# request %d!" id
system.Shutdown()
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// #Synchronized Return
// Actors are very suitable for long-running operations, like getting resources over a network.
//
// This example creates a Task with the ask function.
//
// In the actor we use 'sender <!' to return the value.
//
// #Asynchronous Return
// Asynchronous operations can provide better performance.
// A Task in F# is very powerful, it can execute asynchronously.
// It can also set a in milliseconds to wait for the result of the computation
// before raising a `TimeoutException`.
let versionUrl = @"https://raw.githubusercontent.com/akkadotnet/akka.net/dev/src/SharedAssemblyInfo.cs"
let system = ActorSystem.Create("FSharp")
let fromUrl (url:string) =
use client = new System.Net.WebClient()
let response = client.DownloadString(url)
response
let echoServer =
spawn system "EchoServer"
<| fun mailbox ->
let rec loop() =
actor {
let! message = mailbox.Receive()
let sender = mailbox.Sender()
match box message with
| :? string as url ->
let response = fromUrl url
printfn "actor: done!"
sender <! response
return! loop()
| _ -> failwith "unknown message"
}
loop()
for timeout in [10; 100; 250; 2500] do
try
let task = (echoServer <? versionUrl)
let response = Async.RunSynchronously (task, timeout)
let responseLength = string(response) |> String.length
printfn "response: result has %d bytes" responseLength
with :? TimeoutException ->
printfn "ask: timeout!"
system.Shutdown()
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// #Remote Actor
// Actor is not only a concurrency model, it can also be used for distributed computing.
// This example builds an EchoServer using an Actor.
// Then it creates a client to access the Akka URL.
// The usage is the same as with a normal Actor.
let configuration =
ConfigurationFactory.ParseString(
@"akka {
log-config-on-start : on
stdout-loglevel : DEBUG
loglevel : ERROR
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
debug : {
receive : on
autoreceive : on
lifecycle : on
event-stream : on
unhandled : on
}
}
remote {
helios.tcp {
port = 8777
hostname = localhost
}
}
}")
let system = ActorSystem.Create("RemoteFSharp", configuration)
let echoServer =
spawn system "EchoServer"
<| fun mailbox ->
let rec loop() =
actor {
let! message = mailbox.Receive()
let sender = mailbox.Sender()
match box message with
| :? string ->
printfn "super!"
sender <! sprintf "Hello %s remote" message
return! loop()
| _ -> failwith "unknown message"
}
loop()
let echoClient = system.ActorSelection(
"akka.tcp://RemoteFSharp@localhost:8777/user/EchoServer")
let task = echoClient <? "F#!"
let response = Async.RunSynchronously (task, 1000)
printfn "Reply from remote %s" (string(response))
system.Shutdown()
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// #Event Bus
// Originally conceived as a way to send messages to groups of actors, the EventBus has been generalized to Event Stream
// #Event Stream
// The event stream is the main event bus of each actor system: it is used for carrying log messages and Dead Letters and may be used by the user code for other purposes as well. It uses Subchannel Classification which enables registering to related sets of channels
let system = ActorSystem.Create("FSharp")
let echoServer =
spawn system "EchoServer"
<| fun mailbox ->
let rec loop() =
actor {
let! message = mailbox.Receive()
match box message with
| :? string ->
printfn "Echo '%s'" message
return! loop()
| _ -> failwith "unknown message"
}
loop()
let eventStream = system.EventStream
eventStream.Subscribe(echoServer, typedefof<string>)
eventStream.Publish("Anybody home?")
eventStream.Publish("Knock knock")
system.Shutdown()
#time "on"
#load "Bootstrap.fsx"
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
open Akka.TestKit
// # Discriminated unions as messages
// Instead of passing primitive type, let's use discriminated union
type ActorMsg =
| Hello of string
| Hi
let system = ActorSystem.Create("FSharp")
let echoServer =
spawn system "EchoServer"
<| fun mailbox ->
let rec replyUa() =
actor {
let! message = mailbox.Receive()
match message with
| Hello name -> printfn "Привіт %s" name
| Hi -> printfn "Привіт!"
return! replySw()
}
and replySw() =
actor {
let! message = mailbox.Receive()
match message with
| Hello name -> printfn "Hallå %s" name
| Hi -> printfn "Hallå!"
return! replyUa()
}
replyUa()
echoServer <! Hello "Kyiv F# group!"
echoServer <! Hello "Akka.NET team!"
system.Shutdown()
#time "on"
#I @"..\packages\Akka.0.7.0\lib\net45"
#I @"..\packages\Akka.FSharp.0.7.0\lib\net45"
#I @"..\packages\Akka.Remote.0.7.0\lib\net45"
#I @"..\packages\Akka.TestKit.0.7.0\lib\net45"
#I @"..\packages\FsPickler.0.9.11\lib\net45"
#I @"..\packages\FSPowerPack.Core.Community.3.0.0.0\Lib\Net40"
#I @"..\packages\FSPowerPack.Linq.Community.3.0.0.0\Lib\Net40"
#I @"..\packages\Newtonsoft.Json.6.0.1\lib\net45"
#I @"..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40"
#I @"..\packages\Helios.1.3.5.0\lib\net45"
#r "Akka.dll"
#r "Akka.FSharp.dll"
#r "Akka.Remote.dll"
#r "Akka.TestKit.dll"
sudo mkdir -p /mnt/samples && sudo mount -r -t vboxsf samples /mnt/samples
cp /mnt/samples/Dockerfile . && cp -r /mnt/samples/bin .
docker build -t samples .
docker run -i samples
docker run -it samples bash
docker images
FROM mono:latest
MAINTAINER Akim Boyko "akim dot boyko at gmail.com" version: 0.1
ENV AKKADIR /akka
RUN mkdir -p $AKKADIR
WORKDIR $AKKADIR
RUN mozroots --import --sync
ADD bin/Debug $AKKADIR
CMD [ "mono", "./Sample.exe" ]
@viliwonka
Copy link

Thanks for examples, gonna study them.

@akimboyko
Copy link
Author

Hi Patrick!
Did not see your comment. There is an updated version of slides (http://bit.ly/FSharpAkkaNET) will be available today afternoon after my talk. There are Docker samples too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment