Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asynchronous 使用F#样式异步工作流的WebRequest超时_Asynchronous_F#_Httpwebrequest - Fatal编程技术网

Asynchronous 使用F#样式异步工作流的WebRequest超时

Asynchronous 使用F#样式异步工作流的WebRequest超时,asynchronous,f#,httpwebrequest,Asynchronous,F#,Httpwebrequest,对于更广泛的上下文,这里是,它下载URL列表 在我看来,当使用use时,在F#中没有处理超时的好方法!response=request.AsyncGetResponse()样式URL获取。我几乎所有的东西都能正常工作(错误处理和异步请求和响应下载),避免了网站需要很长时间才能响应时出现的问题。我当前的代码只是无限期地挂起。我已经在我编写的等待300秒的PHP脚本上尝试过了。它一直在等待 我发现了两种“解决方案”,它们都是不可取的 WaitiaSyncResult+BeginGetResponse

对于更广泛的上下文,这里是,它下载URL列表

在我看来,当使用
use时,在F#中没有处理超时的好方法!response=request.AsyncGetResponse()
样式URL获取。我几乎所有的东西都能正常工作(错误处理和异步请求和响应下载),避免了网站需要很长时间才能响应时出现的问题。我当前的代码只是无限期地挂起。我已经在我编写的等待300秒的PHP脚本上尝试过了。它一直在等待

我发现了两种“解决方案”,它们都是不可取的

WaitiaSyncResult
+
BeginGetResponse
喜欢伊尔贾恩的答案。问题是,如果您已将许多异步请求排入队列,则一些请求会在
awaiiasyncresult
上被人为阻止。换句话说,发出请求的调用已经发出,但是幕后有什么东西阻止了调用。这导致在发出多个并发请求时,
awaitiiasyncresult
上的超时提前触发。我的猜测是对单个域的请求数的限制,或者只是对总请求数的限制

为了支持我的怀疑,我编写了一个小WPF应用程序来绘制请求开始和结束的时间线。在我上面链接的代码中,请注意计时器在第49行和第54行(调用第10行)开始和停止。这是你的电话号码

当我将计时器移动到初始响应之后开始时(因此我只是对内容的下载进行计时)。注意,这是两个独立的运行,但除了计时器的启动位置之外,没有代码更改。不要在
使用前直接测量
开始时间
!response=request.AsyncGetResponse()
,之后我就有了它

为了进一步支持我的主张,我与他制定了一个时间表。这是显然,请求并不是在我告诉他们的时候开始的。

GetResponseStream
在新线程中 换句话说,同步请求和下载调用是在辅助线程中进行的。这是可行的,因为
GetResponseStream
尊重
WebRequest
对象上的
Timeout
属性。但在这个过程中,我们失去了所有的等待时间,因为请求已在传输中,而响应尚未返回。我们不妨用C写它

问题
  • 这是一个已知的问题吗
  • 有什么好的解决方案可以利用F#异步工作流,并且仍然允许超时和错误处理
  • 如果问题真的是我一次发出的请求太多,那么限制请求数量的最佳方法是使用
    信号量(5,5)
    还是类似的东西
  • 附带问题:如果你看过我的代码,你能看到我做过的任何愚蠢的事情并且可以修复吗

如果您有什么不明白的地方,请告诉我。

AsyncGetResponse只需忽略发布的任何超时值。。。这是我们刚刚制定的解决方案:

open System
open System.IO
open System.Net

type Request = Request of WebRequest * AsyncReplyChannel<WebResponse>

let requestAgent =
    MailboxProcessor.Start <| fun inbox -> async {
            while true do
                let! (Request (req, port)) = inbox.Receive ()

                async {
                    try
                        let! resp = req.AsyncGetResponse ()
                        port.Reply resp
                    with
                    | ex -> sprintf "Exception in child %s\n%s" (ex.GetType().Name) ex.Message |> Console.WriteLine
                } |> Async.Start
        }

let getHTML url =
    async {
        try
            let req = "http://" + url |> WebRequest.Create
            try
                use! resp = requestAgent.PostAndAsyncReply ((fun chan -> Request (req, chan)), 1000)
                use str = resp.GetResponseStream ()
                use rdr = new StreamReader (str)
                return Some <| rdr.ReadToEnd ()
            with
            | :? System.TimeoutException ->
                req.Abort()
                Console.WriteLine "RequestAgent call timed out"
                return None
        with
        | ex ->
            sprintf "Exception in request %s\n\n%s" (ex.GetType().Name) ex.Message |> Console.WriteLine
            return None
    } |> Async.RunSynchronously;;

getHTML "www.grogogle.com"
开放系统
开放系统
开放系统.Net
类型请求=WebRequest的请求*AsyncReplyChannel
让请求代理=
MailboxProcessor.Start异步{
尽管如此
let!(请求(请求,端口))=收件箱。接收()
异步的{
尝试
let!resp=req.AsyncGetResponse()
港口回复
具有
|ex->sprintf“子%s\n%s中的异常”(ex.GetType().Name)ex.Message |>Console.WriteLine
}|>异步启动
}
让我们获取HTML url=
异步的{
尝试
让req=“http://”+url |>WebRequest.Create
尝试
使用!resp=requestAgent.PostAndAsyncReply((fun chan->Request(req,chan)),1000)
使用str=resp.GetResponseStream()
使用rdr=新的StreamReader(str)
还一些
请求中止()
Console.WriteLine“RequestAgent调用超时”
一无所获
具有
|ex->
sprintf“请求%s中的异常\n\n%s”(例如GetType().Name)例如Message |>Console.WriteLine
一无所获
}|>Async.RunSynchronously;;
getHTML“www.grogogle.com”

i、 我们正在委托给另一个代理,并调用它来提供异步超时。。。如果我们在指定的时间内没有收到代理的答复,我们将中止请求并继续。

我看到我的其他答案可能无法回答您的特定问题。。。这里是另一个不需要使用信号量的任务限制器实现

open System

type IParallelLimiter =
    abstract GetToken : unit -> Async<IDisposable>

type Message= 
    | GetToken of AsyncReplyChannel<IDisposable>
    | Release

let start count =
    let agent =
        MailboxProcessor.Start(fun inbox ->
            let newToken () =
                { new IDisposable with
                    member x.Dispose () = inbox.Post Release }

            let rec loop n = async {
                    let! msg = inbox.Scan <| function
                        | GetToken _ when n = 0 -> None
                        | msg -> async.Return msg |> Some

                    return!
                        match msg with
                        | Release ->
                            loop (n + 1)
                        | GetToken port ->
                            port.Reply <| newToken ()
                            loop (n - 1)
                }
            loop count)

    { new IParallelLimiter with
        member x.GetToken () =
            agent.PostAndAsyncReply GetToken}

let limiter = start 100;;

for _ in 0..1000 do
    async {
        use! token = limiter.GetToken ()
        Console.WriteLine "Sleeping..."
        do! Async.Sleep 3000
        Console.WriteLine "Releasing..."
    } |> Async.Start
开放系统
类型IParallelLimiter=
抽象GetToken:unit->Async
类型消息=
|AsyncReplyChannel的GetToken
|释放
让我们开始计算吧=
出租代理人=
MailboxProcessor.Start(有趣的收件箱->
让牛顿()=
{新IDisposable与
成员x.Dispose()=inbox.Post Release}
让rec循环n=async{
let!msg=收件箱。无扫描
|msg->async.返回msg |>Some
回来!
配味精
|发布->
循环(n+1)
|GetToken端口->
port.Reply Async.Start

阅读您链接到的关于我的答案的评论——我提供了一些文档链接,这些文档描述了如何否决默认的两个连接限制。@ildjarn我以前看到过这些链接,但我继续探讨这个问题,因为我认为更改连接限制并不能解决一个更深层次的问题,即开发人员如何知道t发出启动请求的调用与请求a之间的不可见空闲时间