簡體   English   中英

為什么 ASP.NET 長時間運行的頁面請求會永遠掛起客戶端?

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

我們遇到了一個用 C# 編寫的 ASP.NET Web 應用程序的問題,該應用程序是用 Web 窗體(.ASPX 頁面)構建的。 該系統中有一些長時間運行的請求,大約需要 10-15 分鍾。 這是正常的,因為在后台運行繁重的 SQL 查詢和計算。 這是一個舊系統,多年來從未出現過問題。 前段時間,一些用戶開始抱怨他們無法下載運行時間超過 10 分鍾的報告。 從技術上講,他們通過點擊頁面上的按鈕來運行報告,該按鈕執行回發到生成報告的服務器,當文件准備好時,服務器將其返回給出現“保存文件”對話框的客戶端(取決於瀏覽器)。 不幸的是,這些用戶沒有得到“保存文件”對話框,並且文件永遠不會出現在瀏覽器中,並且頁面似乎永遠在加載。

正如我所說,我們以前從未見過這個問題(系統多年來一直托管在不同的服務器上)。 我們無法在本地開發環境中重現該問題。 最有趣的是,當我們嘗試從位於客戶公司 AD 域網絡之外的個人計算機上運行相同的報告時,我們無法重現該問題。 但是,我們可以在 Postman 中運行示例請求時重現該問題。

以下是有關何時可以重現問題以及何時一切正常的簡短摘要:

  1. 服務器 = [生產服務器,IIS 10,Windows Server 2019]; Client = [任何瀏覽器(Chrome 或 Edge),當這是客戶端公司 AD 域網絡中的工作站時]; 出現問題 => 是
  2. 服務器 = [生產服務器,IIS 10,Windows Server 2019]; Client = [任何瀏覽器(Chrome 或 Edge),當這是公司 AD 域網絡之外的計算機時]; 出現問題 => 否
  3. 服務器 = [生產服務器,IIS 10,Windows Server 2019]; Client = [當客戶端計算機位於公司 AD 域網絡之外時發出 HTTP 請求或 PowerShell 腳本的郵遞員或示例 C# 控制台應用程序]; 出現問題 => 是
  4. Server = [開發機,IIS 10,Window 10]; 客戶端 = [來自公司 AD 域網絡之外的計算機的任何瀏覽器(Chrome 或 Edge)]; 出現問題 => 否

我們做了各種實驗,似乎這個問題出現在不同的用例中,如下所示:

  • 回發到 .ASPX 頁面並在屏幕上重新加載一些內容
  • 回發並返回文件附件(瀏覽器中的“保存文件”對話框)
  • 僅返回一些純文本作為響應的示例頁面

我們發現,在所有這些情況下,如果 HTTP 請求花費的時間超過 240-250 秒,就會出現問題。

問題是沒有出現錯誤,我們在瀏覽器的 DevTools (F12)、Postman 和 Fiddler 中都看不到任何指示。 它看起來好像請求掛起並且永遠保持“待處理”。

我們在服務器上知道的所有“超時”設置都足夠長。 例如,ASP.NET 會話超時為 60 分鍾,httpRuntime 配置為 executionTimeout=3600,Application Pool Idle Time-out 設置為 0。

看起來如果 HTTP 請求花費的時間超過 240-250 秒,那么連接就會掛起,並且只發生在某些客戶端上。 澄清一下,服務器上的進程每次都會完成,它執行所有任務都沒有任何問題。

前段時間我們做了一個實驗,如果服務器通過使用 Response.Write() 和 Response.Flush() 定期向客戶端發送一些東西,而長 HTTP 請求仍在運行但我們不能使用,似乎問題不會出現當服務器將文件附件發送回瀏覽器時,此解決方法。 此外,除了找到解決方法外,我們還試圖了解導致此問題的原因,並且我們正在尋找某種可用於控制此行為的服務器設置。

有任何想法嗎?

可以肯定的是,超時設置是基於瀏覽器的,而不是基於服務器的。 因此,對瀏覽器的更新和升級將是發生這種情況的主要原因。 這些瀏覽器會收到許多更新——通常是每周一次。

事實上,您的測試表明並建議您根據所使用的客戶端軟件類型獲得不同的結果 - 而不是對服務器的任何更改。

因此,使用 post-man 等會產生不同的結果。 這不是服務器端的事情 - 而是網絡瀏覽器設置。 我建議您考慮生成數據或任何需要很長時間的新線程,完成后,它會設置一些會話值。

在瀏覽器端,你有一個客戶端定時器(甚至是一個 asp.net 定時器)——比如每 5 或 10 秒調用一個 web 方法,它會返回數據生成的狀態。 當數據生成和長部分完成后,客戶端可以單擊一個按鈕,或者任何實際導航或顯示結果的東西。

因此,您實際上是在與瀏覽器制造商以及他們對瀏覽器的更新作斗爭——這是您無法控制的。 您可能會調整這個,然后調整那個 - 僅在幾個月內看到基於 FireFox 的一些更新或他們正在使用的任何瀏覽器的大量新問題。

所以,我會為長時間運行的例程啟動一個新的進程線程,然后回發發生。 如前所述,此時,可能會出現一個每 10 或 20 秒檢查一次的簡單計時器。 如前所述,它會/可以調用網絡方法。 或者甚至如前所述,您可以使用 asp.net 計時器,它會每 10 秒觸發一次,運行后面的代碼以獲取會話,如果數據准備好,則后面的代碼可以導航到新頁面,或填寫網格或任何您用來呈現該數據的對象和手段。

從您發布的信息來看,您確實看起來像是在與瀏覽器制造商作斗爭,以及各種客戶端超時設置 - 通常不受您的控制。

任何等待的時間超過 10-15 秒? 然后你忽略了瀏覽器如何工作的基本概念。 (回發,稍等,然后得到回復)。 任何其他設計,然后這種類型的方法都忽略了基於 Web 的軟件的整個架構。 回發導致的長時間等待很簡單,不是一種可行的設計方法。 您可能已經擺脫了這種方法,但您現在顯然為這種糟糕的假設和設計付出了代價。

任何需要超過 10 秒的響應時間都太長了。

例如,我有一些 PDF 文件。 我需要生成圖像(預覽)縮略圖。

所以,我加載了網格,(顯示一個通用圖像)。 然后啟動一個asp.net 計時器。

然后我檢查這個過程是否完成(我使用了會話)。 完成后,我簡單地刷新網格,並停止計時器。 因此,這種方法需要對現有代碼進行最少的更改,但也提供了輪詢和檢查長時間運行進程狀態的能力,並且無需任何花哨的 ajax,甚至不必編寫客戶端代碼。

所以我有這個代碼:

    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

所以,我啟動了一個單獨的線程,然后啟動定時器。

因此,計時器代碼等待圖像處理完成。 我每秒觸發一次。

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

因此,在上面,它為網格顯示拉取數據源,如果存在任何空圖像預覽 - 它會繼續運行。 如果所有圖像預覽都完成,那么我會停止計時器。

而且我真的不必為每個循環重新加載網格視圖,但我喜歡這種效果(如果你有 5 行,那么你會看到網格中的每個預覽圖像都會發生更新)。

現在,當我們完成時,我有一些特定的數據行要測試,但我也可以讓進程線程設置一些 session() 值,如 session("ReportReady") = true,並在計時器事件中檢查它。

所以,我不會將你的軟件設計建立在希望、翅膀和祈禱的基礎上,但事實上,添加一點代碼來循環等待這種情況發生。 好的部分是一個計時器事件,然后你可以有某種類型的進度條,甚至是一個不錯的請等待 - 為用戶處理。

並且使用定時器事件很簡單,而且很好,因為這里不需要新的標記(定時器控件除外),甚至不需要引入客戶端 JavaScript。 然而,在大多數情況下,最好設置這樣的計時器代碼客戶端,使用 ajax 調用,並獲得一些關於何時完成較長運行過程的狀態。

但是,上面的計時器方法往往意味着對現有頁面標記的更改要少得多。 並且由於 asp.net 計時器確實會導致回發,因此將其放置在一個簡單的小型更新面板中,並且在此等待期間您不會遭受整頁回發和重新繪制。 完成該過程后,您的代碼現在可以繼續呈現報告或執行任何操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM