2013-10-29 145 views
0

從我所知道的情況來看,由於Node.js中事件循環的性質,無法輕鬆阻止請求在已被切換到回調後處理功能。由於每個事件回調都不與任何其他特定的流程或事件綁定,因此一旦將回調添加到事件循環的隊列中或正在處理中,即使request.on('超時「)處理程序被觸發。請求超時時停止處理請求

如果超時請求有什麼方法停止處理請求?我能想到部分解決這個問題的唯一方法是在請求期間執行的每個異步回調的頂部添加一個檢查,以查看請求是否超時,如果有,則立即返回並停止連續的回調。

但是,如果已經有一個由超時請求處理的未完成的異步呼叫正在處理,則這並不能解決問題。在這種情況下,除非您銷燬與異步處理(例如執行MySQL查詢)的任何連接,否則它將繼續並最終調用與其關聯的異步回調。

這在PHP + Apache等其他編程模式中不存在問題,因爲apache會爲每個傳入請求創建一個新的PHP進程,所以如果請求超時,進程最終會終止並且整個執行流程停止。

有沒有人遇到這個問題,並找到一個現有的庫或自定義的方式來輕鬆解決這個問題?

編輯:要添加一個具體的例子:

現在,我們有內置用於處理傳入的請求來跟蹤分析節點的API,該請求數據發佈到兔隊列,然後返回一個響應。如果請求進入,並且由於某種原因,請求需要超過30秒的時間才能處理,它將超時並向客戶端發送錯誤響應。此時,客戶將嘗試再次進行相同的呼叫(因此我們不會丟失分析數據)。

但是,由於請求本身和發佈給rabbit的調用之間沒有任何聯繫(除了它是發起發佈請求的事實),因此無法停止發佈該數據。因此,如果兔子服務器最終完成處理髮布請求,現在將會有重複數據,因爲客戶端現在已經發出了2個(或更多)請求。

停止完成發佈的初始請求的唯一方法是在請求超時時銷燬兔子連接。這樣,初始發佈將停止,客戶端的重試請求將不會重複。但是,對於在整個請求中進行的任何異步調用,都需要執行相同類型的手動銷燬連接。如果在超時發生時我有5個優秀的異步調用,我將需要銷燬所有5個異步連接(無論它們是否連接到rabbit,mysql,mongo等)。

然而,問題超出了請求超時時已經建立的連接。即使請求在發佈到rabbit之前超時,被調用來處理該請求的函數已經被執行,並且不會停止,除非在各個位置檢查了是否超時並返回請求立即。

+0

您可以將兔子連接存儲在帶有id的全局對象(可以是客戶端ip或cookie值或任何其他),並且可以在下一個請求中檢索此連接。此外,您可以將兔子連接存儲在插槽本身上,並聽取「關閉」,並在發生這種情況時殺死兔子連接。殺死意味着調用'關閉'或設置一個標誌,以便在你自己的代碼中保存。第一種選擇是,您可以通過某個id將多個套接字/ http連接匯聚到單個服務器的「連接」。 – DDS

+0

@DDS現在我將兔子連接附加到請求上,然後在超時時關閉連接。但是,我希望有一個更通用的解決方案,它包含停止處理整個請求流的能力。現在我想不出一種好的方法,除非我向節點中引入與節點完全相反的東西(爲每個傳入請求創建一個新進程)或冗餘/冗長(在每次異步調用之前添加一個檢查以查看請求已超時)。這兩種方法都不太直觀。 – Sarah

+0

如果您使用節點0.11.x,則可以使用基於生成器的函數([gen-run](https:// github。com/creationix/gen-run),[suspend](https://github.com/jmar777/suspend)),並且您可以修改它以在您要中止函數時將異常拋入生成器函數,並結合忽略原來的下一個回調。然後你有權力在一個'運行函數'上進行真正的「中止」調用(當然這實際上是暫停的)。實質上,這將在每次回調調用後進行檢查,並將「釋放」一直推到函數出口(除非您捕獲並忽略錯誤)。 – DDS

回答

1

對於更具體的問題,您可能會得到更好的答案。像「一個mysql查詢」這樣的技術細節比「一個請求」這樣的模糊的非技術性術語更容易討論。

我能想到部分解決此問題的唯一方法是在請求期間執行的每個異步回調的頂部添加一個檢查,以查看請求是否超時以及是否已超時,然後立即返回並停止連續回調

您現在不需要,也不需要像node.js中那樣的代碼。沒有人會這樣做,因爲這不是問題。即使是這樣,在堆棧中解決問題比在應用程序中解決問題更有意義。

這不是像PHP + Apache的其他編程範式的問題,因爲Apache會爲每個傳入的請求一個新的PHP程序,因此,如果請求超時的過程最終死亡,整個執行流程停止。

這在node.js中甚至沒有問題,因爲對於傳入的HTTP請求,甚至沒有新進程甚至是新線程。什麼發生的結束本質是:

  • 客戶端連接,併發送
  • OS接收
  • 節點接收到GET請求
  • 讓我們在這一點上,客戶端程序(瀏覽器)說,連接的GET請求在客戶端機器上突然死亡
  • 這種情況究竟如何發生有幾種情況。如果電源在瀏覽器所在的房屋中沒有通過,那麼在其他情況下,客戶端會發送數據包來儘早關閉TCP連接。
  • 但是它在節點端並不重要。您處理請求,進行數據庫查詢,呈現您的HTML,最糟糕的情況是一旦完成工作,沒有人關心,但它只是一個GET請求。你的應用程序應該每秒鐘做很多這樣的事情。考慮將該請求的處理完成爲「一個問題」是錯誤的。這不是一個問題,也沒有任何我聽過的東西可以用任何編程語言實際完成。服務器從字面上看不能區分剛剛正確等待來自200毫秒前掉電的客戶端的響應的客戶端。 HTTP沒有「取消」方法。實際上,完成整個HTTP請求可能更符合語義。例如,如果Web瀏覽器向您發送完整的HTTP POST以購買信用卡,但在您回覆之前斷開連接,則該事務應該完成。啓動後中止它將是不正確的。
+0

我不同意。並非所有http請求都在次秒內被提供。有些可能需要數小時(下載)並佔用大量資源(讀大文件)。而且,這些很可能會被客戶拋棄(厭倦了等待)。考慮到這一點,可能有利於早日退出。 AFAICT,莎拉說她需要手動做這件事是對的。也就是說,在每個回調中都沒有必要這樣做。如果有環路就足夠了。如果沒有循環或管道,那麼它可能不值得提供一個早期機制(如你的答案所述)。 – DDS

+0

啊,是的,清楚,具體的例子讓你明確的答案。對於未指定的節點/ php應用程序,下載量是一個很低的比例。基於FUD的一般問題得到了廣泛的答案。 –

+0

我在尋找廣泛的答案,因爲這是一個廣泛且深遠的問題。無論何時您有明確或隱含的超時或已刪除的請求,都會出現此問題。如果請求由於任何原因(由於超時或請求被客戶端或線下的某個地方關閉)而關閉,則無法輕鬆停止處理該請求。查看我的編輯以獲取更多說明。 – Sarah