我是F#以及Akka.Net的新手并试图通过以下方式实现以下目标:
我想创建一个接收文件位置的actor(Tail),然后使用FileSystemWatcher和一些Observables侦听该位置的事件,将它们作为消息转发给其他actor进行处理。
我遇到的问题是,监听事件的代码一次只能获取一个事件而忽略所有其他事件。例如如果我将20个文件复制到被监视的目录中,它似乎只发送其中1个的事件。
这是我的演员代码:
module Tail
open Akka
open Akka.FSharp
open Akka.Actor
open System
open Model
open ObserveFiles
open ConsoleWriteActor
let handleTailMessages tm =
match tm with
| StartTail (f,r) ->
observeFile f consoleWriteActor |!> consoleWriteActor
|> ignore
let spawnTail =
fun (a : Actor<IMessage> ) ->
let rec l (count : int) = actor{
let! m = a.Receive()
handleTailMessages m
return! l (count + 1)
}
l(0)
这里是侦听事件的代码:
module ObserveFiles
open System
open System.IO
open System.Threading
open Model
open Utils
open Akka
open Akka.FSharp
open Akka.Actor
let rec observeFile (absolutePath : string) (a : IActorRef ) = async{
let fsw = new FileSystemWatcher(
Path = Path.GetDirectoryName(absolutePath),
Filter = "*.*",
EnableRaisingEvents = true,
NotifyFilter = (NotifyFilters.FileName ||| NotifyFilters.LastWrite ||| NotifyFilters.LastAccess ||| NotifyFilters.CreationTime ||| NotifyFilters.DirectoryName)
)
let prepareMessage (args: EventArgs) =
let text =
match box args with
| :? FileSystemEventArgs as fsa ->
match fsa.ChangeType with
| WatcherChangeTypes.Changed -> "Changed " + fsa.Name
| WatcherChangeTypes.Created -> "Created " + fsa.Name
| WatcherChangeTypes.Deleted -> "Deleted " + fsa.Name
| WatcherChangeTypes.Renamed -> "Renamed " + fsa.Name
| _ -> "Some other change " + fsa.ChangeType.ToString()
| :? ErrorEventArgs as ea -> "Error: " + ea.GetException().Message
| o -> "some other unexpected event occurd" + o.GetType().ToString()
WriteMessage text
let sendMessage x = async{ async.Return(prepareMessage x) |!> a
return! observeFile absolutePath a }
let! occurance =
[
fsw.Changed |> Observable.map(fun x -> sendMessage (x :> EventArgs));
fsw.Created |> Observable.map(fun x -> sendMessage (x :> EventArgs));
fsw.Deleted |> Observable.map(fun x -> sendMessage (x :> EventArgs));
fsw.Renamed |> Observable.map(fun x -> sendMessage (x :> EventArgs));
fsw.Error |> Observable.map(fun x -> sendMessage (x :> EventArgs));
]
|> List.reduce Observable.merge
|> Async.AwaitObservable
return! occurance
}
到目前为止需要花费相当多的黑客,关于我如何改变它的任何建议,以便它在演员运行时拾取和处理所有事件将非常感激。
答案 0 :(得分:4)
在设计这样的任务时,我们可以将其拆分为以下组件:
FileSystemWatcher
特定路径。它应该订阅传入事件并将它们作为消息重定向到负责接收更改事件的actor。它应该在关闭时释放一次性资源。示例代码:
open Akka.FSharp
open System
open System.IO
let system = System.create "observer-system" <| Configuration.defaultConfig()
let observer filePath consoleWriter (mailbox: Actor<_>) =
let fsw = new FileSystemWatcher(
Path = filePath,
Filter = "*.*",
EnableRaisingEvents = true,
NotifyFilter = (NotifyFilters.FileName ||| NotifyFilters.LastWrite ||| NotifyFilters.LastAccess ||| NotifyFilters.CreationTime ||| NotifyFilters.DirectoryName)
)
// subscribe to all incoming events - send them to consoleWriter
let subscription =
[fsw.Changed |> Observable.map(fun x -> x.Name + " " + x.ChangeType.ToString());
fsw.Created |> Observable.map(fun x -> x.Name + " " + x.ChangeType.ToString());
fsw.Deleted |> Observable.map(fun x -> x.Name + " " + x.ChangeType.ToString());
fsw.Renamed |> Observable.map(fun x -> x.Name + " " + x.ChangeType.ToString());]
|> List.reduce Observable.merge
|> Observable.subscribe(fun x -> consoleWriter <! x)
// don't forget to free resources at the end
mailbox.Defer <| fun () ->
subscription.Dispose()
fsw.Dispose()
let rec loop () = actor {
let! msg = mailbox.Receive()
return! loop()
}
loop ()
// create actor responsible for printing messages
let writer = spawn system "console-writer" <| actorOf (printfn "%A")
// create manager responsible for serving listeners for provided paths
let manager = spawn system "manager" <| actorOf2 (fun mailbox filePath ->
spawn mailbox ("observer-" + Uri.EscapeDataString(filePath)) (observer filePath writer) |> ignore)
manager <! "testDir"