I have a command running a SFTP check periodically and logging the result to a file.
let logPath = Path.Combine(config.["SharedFolder"],timestamp)
let sw = new StreamWriter(logPath,true)
//...
[<EntryPoint>]
let main argv =
try
sftpExample config.["SharedFolder"] config.["SFTPFolder"] 22 "usr" "pswd" |> ignore
with
| ex ->
ex.Message |> printerAgent.Post
printfn "%s" ex.Message // <- NOTICE THIS LINE
sw.Close()
sw.Dispose()
0
It loops over a MailboxProcessor
let printerAgent = MailboxProcessor.Start(fun inbox->
// the message processing function
let rec messageLoop() = async{
// read a message
let! msg = inbox.Receive()
// process a message
sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), msg)
printfn "%s" msg
// loop to top
return! messageLoop()
}
// start the loop
messageLoop()
)
which is called to write the messages to the log
let sftpExample local host port username (password:string) =
async {
use client = new SftpClient(host, port, username, password)
client.Connect()
sprintf "Connected to %s\nroot dir list" host |> printerAgent.Post
do! downloadDir local client ""
sprintf "Done, disconnecting now" |> printerAgent.Post
client.Disconnect()
} |> Async.RunSynchronously
The file downloads are asynchronous, as well as the corresponding messages, but all appears to work well.
The problem is that - if, for some reasons, the sftp connection immediately fails, the MailboxProcessor
has no time to log the exception message.
What I've tried to do - which is working indeed - was adding a printfn "%s" ex.Message
before the end: I just wanted to know if someone envisions a better solution.
FYI, the full code is in this gist.
答案 0 :(得分:3)
实际上,您要的是程序等待,直到MailboxProcessor在程序退出之前完成对所有消息队列的处理。您的printfn "%s" ex.Message
似乎可以正常工作,但不能保证能正常工作:如果MailboxProcessor队列中有多个项目,则运行printfn
函数的线程可能在MailboxProcessor的线程有时间通过之前完成。它的所有消息。
我建议的设计是将printerAgent
的输入更改为DU,如下所示:
type printerAgentMsg =
| Message of string
| Shutdown
然后,当您希望打印机代理完成其消息的发送时,请在main
函数中使用MailboxProcessor.PostAndReply
(并注意文档中的用法示例)并向其发送Shutdown
消息。请记住,MailboxProcessor消息已排队:在收到Shutdown
消息时,它将已经遍历队列中的其余消息。因此,处理Shutdown
消息所需要做的就是返回unit
答复,而不必再次调用其循环。并且由于使用的是PostAndReply
而不是PostAndReplyAsync
,所以主函数将一直阻塞,直到MailboxProcessor完成所有工作为止。 (为避免永远被阻塞的机会,建议您在PostAndReply
呼叫中将超时设置为10秒;默认超时为-1,表示永远等待。)
编辑:这是我的意思的示例(未经测试,使用后果自负)
type printerAgentMsg =
| Message of string
| Shutdown of AsyncReplyChannel<unit>
let printerAgent = MailboxProcessor.Start(fun inbox->
// the message processing function
let rec messageLoop() = async{
// read a message
let! msg = inbox.Receive()
// process a message
match msg with
| Message text ->
sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), text)
printfn "%s" text
// loop to top
return! messageLoop()
| Shutdown replyChannel ->
replyChannel.Reply()
// We do NOT do return! messageLoop() here
}
// start the loop
messageLoop()
)
let logPath = Path.Combine(config.["SharedFolder"],timestamp)
let sw = new StreamWriter(logPath,true)
//...
[<EntryPoint>]
let main argv =
try
sftpExample config.["SharedFolder"] config.["SFTPFolder"] 22 "usr" "pswd" |> ignore
with
| ex ->
ex.Message |> Message |> printerAgent.Post
printfn "%s" ex.Message // <- NOTICE THIS LINE
printerAgent.PostAndReply( (fun replyChannel -> Shutdown replyChannel), 10000) // Timeout = 10000 ms = 10 seconds
sw.Close()
sw.Dispose()
答案 1 :(得分:1)
最简单的解决方案是使用常规(同步)功能记录而不是MailboxProcessor,或者在主要功能的末尾使用一些记录框架和刷新记录器。如果您想继续使用printingAgent
,则可以实现“同步”模式,如下所示:
type Msg =
| Log of string
| LogAndWait of string * AsyncReplyChannel<unit>
let printerAgent = MailboxProcessor.Start(fun inbox ->
let processLogMessage logMessage =
sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), logMessage)
printfn "%s" logMessage
let rec messageLoop() = async{
let! msg = inbox.Receive()
match msg with
| Log logMessage ->
processLogMessage logMessage
| LogAndWait (logMessage, replyChannel) ->
processLogMessage logMessage
replyChannel.Reply()
return! messageLoop()
}
messageLoop()
)
然后您将异步使用
printerAgent.Post(Log "Message")
或同步
printerAgent.PostAndReply(fun channel -> LogAndWait("Message", channel))
在主功能中记录异常时,应使用同步替代。