I have the following code which spawns many worker actors. The worker actor will need to send a message to the task dispatcher to ask for more tasks if idle. How the actor identify itself?
let system = System.create "System" <| Configuration.load ()
let taskDispatcher (mailbox: Actor<_>) = .... // Send message to processor to assign tasks
let processor (mailbox: Actor<MyTask>) =
let rec loop () = actor {
// ... task done. send message to the dispatcher for a new task
return! loop ()
}
loop ()
let processors = [
spawn system "System" processor
spawn system "System" processor
spawn system "System" processor
// .... many more
]
The parameter you're calling mailbox (an Actor<'t>) has a property called Self that gives you an IActorRef to the current actor. You can also get the IActorRef of the actor that sent the message you're currently processing by calling the Sender method on the mailbox. You can use these properties to reply to the sender, send messages to yourself, or to send your own actor reference to another actor so they can send you messages.
One thing to note is that you should be giving each actor a unique name when you call spawn. You can't have two actors with the same name in the same actor system.
Here's a simple example of the message loop using these properties:
type Msg = HelloFrom of IActorRef
spawn system "my-actor"
<| fun mailbox ->
let rec loop () =
actor {
let! message = mailbox.Receive()
mailbox.Sender() <! HelloFrom mailbox.Self
return! loop ()
}
loop ()
Related
I have two actors - childActor and parentActor
open System
open Akka
open Akka.FSharp
let systemActor = System.create "systemActor" <| Configuration.defaultConfig()
let childActor (mailbox: Actor<_>) =
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "Message received"
return! loop()
}
loop()
let parentActor (mailbox: Actor<_>) =
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "Message received"
return! loop()
}
loop()
I can create the parent actor reference using spawn function.
let parentRef = spawn systemActor "parentActor" parentActor
Now what I want to do here is to create the child actor reference under parent actor. Something like below.
let childRef = spawn parentRef "childActor" childActor
But this is not working. spawn function requires a parameter of type IActorRefFactory so it is not accepting IActorRef. Is there any way to create a child actor under parent actor in akka.net using F#?
IActorRefFactory is an interface responsible for determining a parent and in case of Akka.FSharp it's implemented by ActorSystem and Actor<_> as well. So in your case just use:
let childRef = spawn mailbox "childActor" childActor
I created the following test code - .Net core 2.1 console application. It prints the following message only
TestActor received message MyTask ("Test1","Test1") from [akka://MySystem/user/Scheduler#1426101451]
But the message Ok 0 cannot be received by the actor scheduler?
open System
open Akka.FSharp
open Akka
type MyTask = MyTask of item1: string * item2: string
let system = System.create "MySystem" <| Configuration.load ()
let scheduler (actors: Actor.IActorRef) (mailbox: Actor<Result<int, string>>) =
let rec loop (list: int list list) = actor {
let! m = mailbox.Receive ()
let sender = mailbox.Sender ()
let akkaName = mailbox.Self.Path.Name
printfn "%s received message %A from %A" akkaName m sender
return! loop []
}
actors <! MyTask("Test1", "Test1")
loop []
let processor (mailbox: Actor<MyTask>) =
let rec loop () = actor {
let! m = mailbox.Receive ()
let sender = mailbox.Sender ()
let akkaName = mailbox.Self.Path.Name
printfn "%s received message %A from %A" akkaName m sender
sender <! Ok 0 // scheduler cannot receive this message?
return! loop ()
}
loop ()
[<EntryPoint>]
let main argv =
let actor = spawn system "TestActor" processor
spawn system "Scheduler" (scheduler actor) |> ignore
system.WhenTerminated.Wait()
0
Update:
It works after I changed the parameter from (mailbox: Actor<Result<int, string>>) to (mailbox: Actor<_>)?
The scheduler isn't the sender of the MyTask message to which the processor is replying, because you are doing the tell (<!) from outside the actor computation. That means it's basically being sent with no sender. You can use the Tell method on IActorRef to send with an explicit sender, since you want to send it from outside the context of your actor computation:
actors.Tell(MyTask("Test1", "Test1"), mailbox.Self)
EDIT
Another issue is that the mailbox parameter to the scheduler is typed as Actor<Result<int, string>>, but since Ok 0 will be inferred as Result<int,obj> in your context, it won't match the actor type signature, and the message will be ignored. When defining an actor with a specific message type, any messages of a different type will be ignored.
How do I implement Failover within an Akka.NET cluster using the Akka.FSharp API?
I have the following cluster node that serves as a seed:
open Akka
open Akka.FSharp
open Akka.Cluster
open System
open System.Configuration
let systemName = "script-cluster"
let nodeName = sprintf "cluster-node-%s" Environment.MachineName
let akkaConfig = Configuration.parse("""akka {
actor {
provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"
}
remote {
log-remote-lifecycle-events = off
helios.tcp {
hostname = "127.0.0.1"
port = 2551
}
}
cluster {
roles = ["seed"] # custom node roles
seed-nodes = ["akka.tcp://script-cluster#127.0.0.1:2551"]
# when node cannot be reached within 10 sec, mark is as down
auto-down-unreachable-after = 10s
}
}""")
let actorSystem = akkaConfig |> System.create systemName
let clusterHostActor =
spawn actorSystem nodeName (fun (inbox: Actor<ClusterEvent.IClusterDomainEvent>) ->
let cluster = Cluster.Get actorSystem
cluster.Subscribe(inbox.Self, [| typeof<ClusterEvent.IClusterDomainEvent> |])
inbox.Defer(fun () -> cluster.Unsubscribe(inbox.Self))
let rec messageLoop () =
actor {
let! message = inbox.Receive()
// TODO: Handle messages
match message with
| :? ClusterEvent.MemberJoined as event -> printfn "Member %s Joined the Cluster at %O" event.Member.Address.Host DateTime.Now
| :? ClusterEvent.MemberLeft as event -> printfn "Member %s Left the Cluster at %O" event.Member.Address.Host DateTime.Now
| other -> printfn "Cluster Received event %O at %O" other DateTime.Now
return! messageLoop()
}
messageLoop())
I then have an arbitrary node that could die:
open Akka
open Akka.FSharp
open Akka.Cluster
open System
open System.Configuration
let systemName = "script-cluster"
let nodeName = sprintf "cluster-node-%s" Environment.MachineName
let akkaConfig = Configuration.parse("""akka {
actor {
provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"
}
remote {
log-remote-lifecycle-events = off
helios.tcp {
hostname = "127.0.0.1"
port = 0
}
}
cluster {
roles = ["role-a"] # custom node roles
seed-nodes = ["akka.tcp://script-cluster#127.0.0.1:2551"]
# when node cannot be reached within 10 sec, mark is as down
auto-down-unreachable-after = 10s
}
}""")
let actorSystem = akkaConfig |> System.create systemName
let listenerRef =
spawn actorSystem "temp2"
<| fun mailbox ->
let cluster = Cluster.Get (mailbox.Context.System)
cluster.Subscribe (mailbox.Self, [| typeof<ClusterEvent.IMemberEvent>|])
mailbox.Defer <| fun () -> cluster.Unsubscribe (mailbox.Self)
printfn "Created an actor on node [%A] with roles [%s]" cluster.SelfAddress (String.Join(",", cluster.SelfRoles))
let rec seed () =
actor {
let! (msg: obj) = mailbox.Receive ()
match msg with
| :? ClusterEvent.MemberRemoved as actor -> printfn "Actor removed %A" msg
| :? ClusterEvent.IMemberEvent -> printfn "Cluster event %A" msg
| _ -> printfn "Received: %A" msg
return! seed () }
seed ()
What is the recommended practice for implementing failover within a cluster?
Specifically, is there a code example of how a cluster should behave when one of its nodes is no longer available?
Should my cluster node spin-up a replacement or is there a different behavior?
Is there a configuration that automatically handles this that I can just set without having to write code?
What code would I have to implement and where?
First of all it's a better idea to rely on MemberUp and MemberRemoved events (both implementing ClusterEvent.IMemberEvent interface, so subscribe for it), as they mark phases, when node joining/leaving procedure has been completed. Joined and left events doesn't necessarily ensure that node is fully operable at signaled point in time.
Regarding failover scenario:
Automatic spinning of the replacements can be done via Akka.Cluster.Sharding plugin (read articles 1 and 2 to get more info about how does it work). There's no equivalent in Akka.FSharp for it, but you may use Akkling.Cluster.Sharding plugin instead: see example code.
Another way is to create replacement actors up-front on each of the nodes. You can route messages to them by using clustered routers or distributed publish/subscribe. This is however more a case in situation, when you have stateless scenarios, so that each actor is perfectly able to pick up work of another actor at any time. This is more general solution for distributing work among many actors living on many different nodes.
You may also set watchers over processing actors. By using monitor function, you may order your actor to watch over another actor (no matter where does it live). In case of node failure, info about dying actor will be send in form of Terminated message to all of its watchers. This way you may implement your own logic i.e. recreating actor on another node. This is actually the most generic way, as it doesn't use any extra plugins or configuration, but the behavior needs to be described by yourself.
I am a little curious about the code example below and what people think.
The idea was to read from a NetworkStream (~20 msg/s) and instead of working in the main, pass things to MainboxProcessor to handle and get things back for bindings when done.
The usual way is to use PostAndReply, but I want to bind to ListView or other control in C#. Must do magic with LastN items and filtering anyway.
Plus, Rx has some error handling.
The example below observes numbers from 2..10 and returns "hello X". On 8 it stops like it was EOF. Made it to ToEnumerable because other thread finishes before otherwise, but it works with Subscribe as well.
What bothers me:
passing Subject(obj) around in recursion. I don't see any problems having around 3-4 of those. Good idea?
Lifetime of Subject.
open System
open System.Threading
open System.Reactive.Subjects
open System.Reactive.Linq // NuGet, take System.Reactive.Core also.
open System.Reactive.Concurrency
type SerializedLogger() =
let _letters = new Subject<string>()
// create the mailbox processor
let agent = MailboxProcessor.Start(fun inbox ->
// the message processing function
let rec messageLoop (letters:Subject<string>) = async{
// read a message
let! msg = inbox.Receive()
printfn "mailbox: %d in Thread: %d" msg Thread.CurrentThread.ManagedThreadId
do! Async.Sleep 100
// write it to the log
match msg with
| 8 -> letters.OnCompleted() // like EOF.
| x -> letters.OnNext(sprintf "hello %d" x)
// loop to top
return! messageLoop letters
}
// start the loop
messageLoop _letters
)
// public interface
member this.Log msg = agent.Post msg
member this.Getletters() = _letters.AsObservable()
/// Print line with prefix 1.
let myPrint1 x = printfn "onNext - %s, Thread: %d" x Thread.CurrentThread.ManagedThreadId
// Actions
let onNext = new Action<string>(myPrint1)
let onCompleted = new Action(fun _ -> printfn "Complete")
[<EntryPoint>]
let main argv =
async{
printfn "Main is on: %d" Thread.CurrentThread.ManagedThreadId
// test
let logger = SerializedLogger()
logger.Log 1 // ignored?
let xObs = logger
.Getletters() //.Where( fun x -> x <> "hello 5")
.SubscribeOn(Scheduler.CurrentThread)
.ObserveOn(Scheduler.CurrentThread)
.ToEnumerable() // this
//.Subscribe(onNext, onCompleted) // or with Dispose()
[2..10] |> Seq.iter (logger.Log)
xObs |> Seq.iter myPrint1
while true
do
printfn "waiting"
System.Threading.Thread.Sleep(1000)
return 0
} |> Async.RunSynchronously // return an integer exit code
I have done similar things, but using the plain F# Event type rather than Subject. It basically lets you create IObservable and trigger its subscribes - much like your use of more complex Subject. The event-based version would be:
type SerializedLogger() =
let letterProduced = new Event<string>()
let lettersEnded = new Event<unit>()
let agent = MailboxProcessor.Start(fun inbox ->
let rec messageLoop (letters:Subject<string>) = async {
// Some code omitted
match msg with
| 8 -> lettersEnded.Trigger()
| x -> letterProduced.Trigger(sprintf "hello %d" x)
// ...
member this.Log msg = agent.Post msg
member this.LetterProduced = letterProduced.Publish
member this.LettersEnded = lettersEnded.Publish
The important differences are:
Event cannot trigger OnCompleted, so I instead exposed two separate events. This is quite unfortunate! Given that Subject is very similar to events in all other aspects, this might be a good reason for using subject instead of plain event.
The nice aspect of using Event is that it is a standard F# type, so you do not need any external dependencies in the agent.
I noticed your comment noting that the first call to Log was ignored. That's because you subscribe to the event handler only after this call happens. I think you could use ReplaySubject variation on the Subject idea here - it replays all events when you subscribe to it, so the one that happened earlier would not be lost (but there is a cost to caching).
In summary, I think using Subject is probably a good idea - it is essentially the same pattern as using Event (which I think is quite standard way of exposing notifications from agents), but it lets you trigger OnCompleted. I would probably not use ReplaySubject, because of the caching cost - you just have to make sure to subscribe before triggering any events.
Suppose I have a stream which only allows one request/response at a time but is used in several threads.
Requests/commands should be throttled such that a new request can only occur once
the previous request has been sent and a reply has been received.
The user would be able to do this
let! res = getResponse("longResp")
let! res2 = getResponse("shortResp")
and not really know or care about the throttle.
I have tried with a modified version of Tomas Petricek's Throttling Agent that allows async with return values, but this requires the user to call getResponse("..") |> Enqueue |> w.Post which is a recipe for disaster (in case they forget to do so).
Is there a good/idiomatic way of doing this in F#?
Then make it explicit in your type system that the returned type needs to be unwrapped with another function. So instead of returning an Async<'T> which as you pointed out can be called directly with Async.Start, rather return something like:
type Queuable<'T> = Queuable of Async<'T>
Then getResponse changes to return a Queueable:
let getResponse (s:string) =
let r =
async{
do! write to your stream
return! read from your stream
}
Queuable r
Provide a function that unwraps the Queuable:
let enqueue (Queuable q) = async{
return! processor.PostAndAsyncReply(fun replyChannel -> replyChannel,q)
}
The processor is an agent that simply runs the Async workflow. Something like this:
let processor = new MailboxProcessor<_>(fun inbox ->
let rec Loop() = async {
let! (r:AsyncReplyChannel<_>,job) = inbox.Receive()
let! res = job
r.Reply res
return! Loop()}
Loop())