简体   繁体   English

为什么 ASP.NET 长时间运行的页面请求会永远挂起客户端?

[英]Why ASP.NET long running page request hangs forever for the client?

We ran into a problem with an ASP.NET web application written in C# which is built with Web Forms (.ASPX pages).我们遇到了一个用 C# 编写的 ASP.NET Web 应用程序的问题,该应用程序是用 Web 窗体(.ASPX 页面)构建的。 There are some long running requests in this system which take about 10-15 minutes.该系统中有一些长时间运行的请求,大约需要 10-15 分钟。 This is normal due to the heavy SQL queries and calculations that are running at the background.这是正常的,因为在后台运行繁重的 SQL 查询和计算。 This is an old system and this have never been a problem over the years.这是一个旧系统,多年来从未出现过问题。 Some time ago a couple of users started complaining that they cannot download those reports which take more than 10 minutes to run.前段时间,一些用户开始抱怨他们无法下载运行时间超过 10 分钟的报告。 Technically they run the report by hitting a button on the page which performs a post back to the server where the report is generated and when the file is ready the server returns it to the client where the “save file” dialog appears (depending on the browser).从技术上讲,他们通过点击页面上的按钮来运行报告,该按钮执行回发到生成报告的服务器,当文件准备好时,服务器将其返回给出现“保存文件”对话框的客户端(取决于浏览器)。 Unfortunately, those users don't get the “save file” dialog and the file never appears in the browser and the page seems to be loading forever.不幸的是,这些用户没有得到“保存文件”对话框,并且文件永远不会出现在浏览器中,并且页面似乎永远在加载。

As I said, we have never seen this issue before (the system has been hosted on different servers over the years).正如我所说,我们以前从未见过这个问题(系统多年来一直托管在不同的服务器上)。 We are unable to reproduce the problem in our local development environment.我们无法在本地开发环境中重现该问题。 What is most interesting is the fact that we cannot reproduce the problem when we try running the same report from our personal computers which are outside of the client's corporate AD domain network.最有趣的是,当我们尝试从位于客户公司 AD 域网络之外的个人计算机上运行相同的报告时,我们无法重现该问题。 However, we can reproduce the problem when running sample requests in Postman.但是,我们可以在 Postman 中运行示例请求时重现该问题。

Here is a short summary of when the problem can be reproduced and when everything works fine:以下是有关何时可以重现问题以及何时一切正常的简短摘要:

  1. Server = [production server, IIS 10, Windows Server 2019];服务器 = [生产服务器,IIS 10,Windows Server 2019]; Client = [Any browser (either Chrome or Edge) when this is a workstation in the client's corporate AD domain network]; Client = [任何浏览器(Chrome 或 Edge),当这是客户端公司 AD 域网络中的工作站时]; Problem appears => YES出现问题 => 是
  2. Server = [production server, IIS 10, Windows Server 2019];服务器 = [生产服务器,IIS 10,Windows Server 2019]; Client = [Any browser (either Chrome or Edge) when this is a computer OUTSIDE the corporate AD domain network]; Client = [任何浏览器(Chrome 或 Edge),当这是公司 AD 域网络之外的计算机时]; Problem appears => NO出现问题 => 否
  3. Server = [production server, IIS 10, Windows Server 2019];服务器 = [生产服务器,IIS 10,Windows Server 2019]; Client = [Postman OR sample C# console application making HTTP requests OR PowerShell script when the client computer is OUTSIDE the corporate AD domain network]; Client = [当客户端计算机位于公司 AD 域网络之外时发出 HTTP 请求或 PowerShell 脚本的邮递员或示例 C# 控制台应用程序]; Problem appears => YES出现问题 => 是
  4. Server = [development machine, IIS 10, Window 10]; Server = [开发机,IIS 10,Window 10]; Client = [Any browser (either Chrome or Edge) from a computer OUTSIDE the corporate AD domain network];客户端 = [来自公司 AD 域网络之外的计算机的任何浏览器(Chrome 或 Edge)]; Problem appears => NO出现问题 => 否

We did various experiments and it seems that this problem appears in different use cases as follows:我们做了各种实验,似乎这个问题出现在不同的用例中,如下所示:

  • Do a post-back to the .ASPX page and reload some content on the screen回发到 .ASPX 页面并在屏幕上重新加载一些内容
  • Do a post-back and return a file attachment (the “save file” dialog in the browser)回发并返回文件附件(浏览器中的“保存文件”对话框)
  • Sample page which just returns some plain text as a response仅返回一些纯文本作为响应的示例页面

We found that in all those cases the problem appears if the HTTP request takes more than 240-250 seconds.我们发现,在所有这些情况下,如果 HTTP 请求花费的时间超过 240-250 秒,就会出现问题。

The problem is that no error appears and we cannot see any indication neither in browser's DevTools (F12) nor in Postman nor in Fiddler.问题是没有出现错误,我们在浏览器的 DevTools (F12)、Postman 和 Fiddler 中都看不到任何指示。 It just looks like as if the request hangs and remains “pending” forever.它看起来好像请求挂起并且永远保持“待处理”。

All “timeout” settings that we know on the server are long enough.我们在服务器上知道的所有“超时”设置都足够长。 For example, the ASP.NET session timeout is 60 minutes, the httpRuntime is configured with executionTimeout=3600, the Application Pool Idle Time-out is set to 0.例如,ASP.NET 会话超时为 60 分钟,httpRuntime 配置为 executionTimeout=3600,Application Pool Idle Time-out 设置为 0。

It just looks like that if the HTTP request takes more than 240-250 seconds then the connection hangs and it happens only for some clients.看起来如果 HTTP 请求花费的时间超过 240-250 秒,那么连接就会挂起,并且只发生在某些客户端上。 Just to clarify, the process on the server completes every time and it performs all tasks without any problems.澄清一下,服务器上的进程每次都会完成,它执行所有任务都没有任何问题。

Some time ago we did an experiment and it seems the problem doesn't appear if the server periodically sends something to the client by using Response.Write() and Response.Flush() while the long HTTP request is still running but we cannot use this workaround when the server sends a file attachment back to the browser.前段时间我们做了一个实验,如果服务器通过使用 Response.Write() 和 Response.Flush() 定期向客户端发送一些东西,而长 HTTP 请求仍在运行但我们不能使用,似乎问题不会出现当服务器将文件附件发送回浏览器时,此解决方法。 Also, in addition to finding a workaround we are trying to understand what is the cause of this issue and we are looking for some kind of a server setting that we can use to control this behavior.此外,除了找到解决方法外,我们还试图了解导致此问题的原因,并且我们正在寻找某种可用于控制此行为的服务器设置。

Any ideas?有任何想法吗?

Quite sure the time out settings are browser based - not server based.可以肯定的是,超时设置是基于浏览器的,而不是基于服务器的。 So, updates and upgrades to browsers would be quite much why this is occurring.因此,对浏览器的更新和升级将是发生这种情况的主要原因。 And those browser receive many updates - often weekly.这些浏览器会收到许多更新——通常是每周一次。

And in fact, your testing shows and suggests that you get different results based on the kind of client software used - not any changes to the server.事实上,您的测试表明并建议您根据所使用的客户端软件类型获得不同的结果 - 而不是对服务器的任何更改。

So, using post-man etc. gives different results.因此,使用 post-man 等会产生不同的结果。 This is not the server side of things - but the web browsers settings.这不是服务器端的事情 - 而是网络浏览器设置。 I would suggest you consider generating the data or whatever takes a long time as a new thread, and when done, it sets some session value.我建议您考虑生成数据或任何需要很长时间的新线程,完成后,它会设置一些会话值。

And in the browser side, you have a client side timer (or even a asp.net one) - say every 5 or 10 seconds calls a web method, and it returns the status of the data generation.在浏览器端,你有一个客户端定时器(甚至是一个 asp.net 定时器)——比如每 5 或 10 秒调用一个 web 方法,它会返回数据生成的状态。 When data generation and the long parts are done then client side can then click a button, or whatever to actually navigate or display the results.当数据生成和长部分完成后,客户端可以单击一个按钮,或者任何实际导航或显示结果的东西。

So, you are in effect fighting against the browser makers, and their updates to their browsers - something you can't control much.因此,您实际上是在与浏览器制造商以及他们对浏览器的更新作斗争——这是您无法控制的。 You might tweak this, and tweak that - only in some months to see a whole rash of new issues based on say some update to FireFox, or whatever browser(s) they are using.您可能会调整这个,然后调整那个 - 仅在几个月内看到基于 FireFox 的一些更新或他们正在使用的任何浏览器的大量新问题。

So, I would start a new process thread for the long running routine(s), and then the post-back has occurred.所以,我会为长时间运行的例程启动一个新的进程线程,然后回发发生。 As noted, at that point, a simple timer that checks every 10 or 20 seconds could occur.如前所述,此时,可能会出现一个每 10 或 20 秒检查一次的简单计时器。 It would/could as noted, call a web method.如前所述,它会/可以调用网络方法。 Or even as noted, you can use a asp.net timer, and it would trigger every 10 seconds, code behind runs to get the session, and if the data is ready then the code behind can navigate to the new page, or fill out the grid or whatever objects and means you are using to render that data.或者甚至如前所述,您可以使用 asp.net 计时器,它会每 10 秒触发一次,运行后面的代码以获取会话,如果数据准备好,则后面的代码可以导航到新页面,或填写网格或任何您用来呈现该数据的对象和手段。

From the information you posted, it sure looks like you fighting the browser makers, and the various client side settings for time outs - often not under your control.从您发布的信息来看,您确实看起来像是在与浏览器制造商作斗争,以及各种客户端超时设置 - 通常不受您的控制。

Anything that waits for say more then 10-15 seconds?任何等待的时间超过 10-15 秒? Then you ignoring the basic concepts of how a browser works.然后你忽略了浏览器如何工作的基本概念。 (post-back, wait a bit, and get a response). (回发,稍等,然后得到回复)。 Any other design then this type of approach quite much ignores the whole architecture of web based software.任何其他设计,然后这种类型的方法都忽略了基于 Web 的软件的整个架构。 Long waits as a result of a post-back is simple NOT a workable design approach.回发导致的长时间等待很简单,不是一种可行的设计方法。 You might have gotten away doing this approach, but you now clearly paying the price for this bad assumption and design.您可能已经摆脱了这种方法,但您现在显然为这种糟糕的假设和设计付出了代价。

Anything that going to take more then 10 seconds is TOO long of a time frame for a response.任何需要超过 10 秒的响应时间都太长了。

For example, I have some PDF files.例如,我有一些 PDF 文件。 I need to genrate a image (preview) thumb nail.我需要生成图像(预览)缩略图。

So, I load the grid, (show a generic image).所以,我加载了网格,(显示一个通用图像)。 And then start a asp.net timer.然后启动一个asp.net 计时器。

I then check for if this process is done (I used session).然后我检查这个过程是否完成(我使用了会话)。 When done, then I simple refresh the grid, and stop the timer.完成后,我简单地刷新网格,并停止计时器。 So, this approach required a MIN of changes to the existing code, but also afforded the ability to poll and check the status of that long running process, and do so without any fancy ajax, and even having to write client side code.因此,这种方法需要对现有代码进行最少的更改,但也提供了轮询和检查长时间运行进程状态的能力,并且无需任何花哨的 ajax,甚至不必编写客户端代码。

So I have this code:所以我有这个代码:

    If bolUpdate Then
        ' process the images as seperate process
        Dim MyP(1) As String        ' threads only allow one param
        MyP(0) = cPinfo.ProjectHeaderID
        MyP(1) = cPinfo.PortalComp
        Dim mypthread As New Thread(New ParameterizedThreadStart(AddressOf ProcessThumbs))
        mypthread.Start(MyP)
    End If

    If bolUpdate Then
        ' start a timer to update page when image processing done
        Timer1.Enabled = True
    End If

So, I started a separate thread, and then start the timer.所以,我启动了一个单独的线程,然后启动定时器。

the timer code thus waits for the image processing to complete.因此,计时器代码等待图像处理完成。 I trigger it once per second.我每秒触发一次。

Protected Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick

    Dim strSQL As String = "SELECT * FROM myTable WHERE ProjectHeaderID = " &
                            cPinfo.ProjectHeaderID & " AND WebPreviewThumb is null"
    Dim rst As DataTable
    rst = Myrst(strSQL, GetConstr(cPinfo.PortalComp))
    If rst.Rows.Count = 0 Then
        ' no files to process left
        Timer1.Enabled = False
    End If

    LoadProofGrid()

End Sub

So, in above, it pulls the data source for the grid display, and if any null image previews exist - it keeps running.因此,在上面,它为网格显示拉取数据源,如果存在任何空图像预览 - 它会继续运行。 If all image previews are done, then I stop the timer.如果所有图像预览都完成,那么我会停止计时器。

And I did not really have to re-load the gridview for each cycle, but I like the effect (if you have 5 rows, then you see updates occur for each preview image in the grid).而且我真的不必为每个循环重新加载网格视图,但我喜欢这种效果(如果你有 5 行,那么你会看到网格中的每个预览图像都会发生更新)。

Now, I had some specific data rows to test when we are done, but I could have just as well had the process thread set some session() value like session("ReportReady") = true, and check that in the timer event.现在,当我们完成时,我有一些特定的数据行要测试,但我也可以让进程线程设置一些 session() 值,如 session("ReportReady") = true,并在计时器事件中检查它。

So, I would not base your software design on a hope and wing and a prayer, but in fact, add a wee bit of code to cycle around and around waiting for this to occur.所以,我不会将你的软件设计建立在希望、翅膀和祈祷的基础上,但事实上,添加一点代码来循环等待这种情况发生。 The nice part is with a timer event, then you can have some type of progress bar, or even a nice please wait - processing for the user.好的部分是一个计时器事件,然后你可以有某种类型的进度条,甚至是一个不错的请等待 - 为用户处理。

and using a timer event was easy, and nice, since no new markup (except for the timer control), or even introduction of client side JavaScript was required here.并且使用定时器事件很简单,而且很好,因为这里不需要新的标记(定时器控件除外),甚至不需要引入客户端 JavaScript。 However, it is in most cases better to setup such timer code client side, use ajax calls, and get some status as to when the longer running process is done.然而,在大多数情况下,最好设置这样的计时器代码客户端,使用 ajax 调用,并获得一些关于何时完成较长运行过程的状态。

However, the timer approach above tends to mean far less changes to the existing page markup wise.但是,上面的计时器方法往往意味着对现有页面标记的更改要少得多。 And since the asp.net timer DOES cause a post back, then place it in a simple tiny update panel and you not suffer full page post backs and replots during this waiting period.并且由于 asp.net 计时器确实会导致回发,因此将其放置在一个简单的小型更新面板中,并且在此等待期间您不会遭受整页回发和重新绘制。 Once the process is done, then your code behind can now continue to render the report or do whatever.完成该过程后,您的代码现在可以继续呈现报告或执行任何操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM