簡體   English   中英

Node.js和CPU密集型請求

[英]Node.js and CPU intensive requests

我已經開始修補Node.js HTTP服務器,並且非常喜歡編寫服務器端Javascript,但有些東西阻止我開始使用Node.js作為我的Web應用程序。

我理解整個異步I / O概念,但我有點擔心程序代碼非常CPU密集的邊緣情況,例如圖像處理或排序大數據集。

據我了解,服務器對於簡單的網頁請求非常快,例如查看用戶列表或查看博客帖子。 但是,如果我想編寫非常CPU密集型代碼(例如在管理員后端)生成圖形或調整數千個圖像的大小,請求將非常慢(幾秒鍾)。 由於此代碼不是異步的,因此在幾秒鍾內發送到服務器的每個請求都將被阻止,直到我的慢請求完成為止。

一個建議是使用Web Workers進行CPU密集型任務。 但是,我擔心網絡工作者會很難編寫干凈的代碼,因為它的工作方式是包含一個單獨的JS文件。 如果CPU密集型代碼位於對象的方法中該怎么辦? 為每個CPU密集型方法編寫一個JS文件真是太糟糕了。

另一個建議是生成子進程,但這使得代碼更難以維護。

有什么建議可以克服這個(感知的)障礙嗎? 如何使用Node.js編寫干凈的面向對象代碼,同時確保CPU重任務執行異步?

這是對Web服務器定義的誤解 - 它應該只用於與客戶“交談”。 重載任務應該委托給獨立程序(當然也可以用JS編寫)。
您可能會說它很臟,但我向您保證,調整圖像大小的Web服務器進程更糟糕(即使對於Apache來說,當它不阻止其他查詢時)。 不過,您可以使用公共庫來避免代碼冗余。

編輯:我想出了一個類比; 網絡應用程序應該作為一個餐廳。 你有服務員(網絡服務員)和廚師(工人)。 服務員與客戶保持聯系並完成簡單的任務,例如提供菜單或解釋某些菜是素食的。 另一方面,他們將更艱巨的任務委托給廚房。 因為服務員只做簡單的事情,他們反應迅速,廚師可以集中精力完成工作。

這里的Node.js將是一個單一但非常有才華的服務員,可以一次處理多個請求,而Apache將是一群愚蠢的服務員,每個服務器只處理一個請求。 如果這個Node.js服務員開始做飯,那將是一場直接的災難。 盡管如此,烹飪也可能耗盡大量的Apache服務員,沒有提到廚房里的混亂以及響應能力的逐漸下降。

你需要的是一個任務隊列! 將長時間運行的任務移出Web服務器是一件好事。 將每個任務保存在“單獨的”js文件中可以促進模塊化和代碼重用。 它迫使您考慮如何以一種方式構建程序,從而使程序更容易調試和維護。 任務隊列的另一個好處是工作人員可以用不同的語言編寫。 只需彈出一個任務,完成工作,然后再寫回復。

像這樣的東西https://github.com/resque/resque

這是來自github的一篇文章,關於他們為什么要構建它http://github.com/blog/542-introducing-resque

您不希望CPU密集型代碼執行異步,您希望它並行執行。 您需要從提供HTTP請求的線程中獲取處理工作。 這是解決這個問題的唯一方法。 使用NodeJS,答案就是集群模塊 ,用於生成子進程來完成繁重的工作。 (AFAIK節點沒有任何線程/共享內存的概念;它是進程或什么都沒有)。 您有兩種選擇來構建應用程序。 您可以通過生成8個HTTP服務器並在子進程上同步處理計算密集型任務來獲得80/20解決方案。 這樣做很簡單。 你可以花一個小時在那個鏈接上閱讀它。 事實上,如果您只是刪掉該鏈接頂部的示例代碼,那么您將獲得95%的支持率。

構建此方法的另一種方法是設置作業隊列並通過隊列發送大型計算任務。 請注意,作業隊列的IPC存在大量開銷,因此僅當任務明顯大於開銷時才有用。

我很驚訝這些其他答案都沒有提到集群。

背景:異步代碼是暫停的代碼,直到其他地方發生某些事情 ,此時代碼喚醒並繼續執行。 一個非常常見的情況是I / O必須在其他地方發生緩慢的事情。

如果您的處理器負責執行工作,則異步代碼無用。 這正是“計算密集型”任務的情況。

現在,看起來異步代碼似乎是利基,但事實上它很常見。 它恰好對計算密集型任務沒用。

例如,等待I / O是一種總是在Web服務器中發生的模式。 連接到服務器的每個客戶端都會獲得一個套接字。 大多數情況下插座都是空的。 在套接字收到某些數據之前,您不想做任何事情,此時您要處理請求。 在引擎蓋下,像Node這樣的HTTP服務器正在使用事件庫(libev)來跟蹤數千個打開的套接字。 操作系統通知libev,然后當其中一個套接字獲取數據時,libev會通知NodeJS,然后NodeJS將事件放入事件隊列,此時您的http代碼就會啟動並一個接一個地處理事件。 在套接字有一些數據之前,事件不會被放入隊列,因此事件永遠不會等待數據 - 它已經存在於它們中。

單線程基於事件的Web服務器作為一種范例有意義,當瓶頸等待大量空插槽連接並且您不希望每個空閑連接都有一個完整的線程或進程而您不想輪詢您的250k套接字用於查找下一個包含數據的套接字。

您可以使用幾種方法。

正如@Tim所說,您可以創建一個位於主服務邏輯外部或並行的異步任務。 取決於您的確切要求,但即使是cron也可以作為排隊機制。

WebWorkers可以為您的異步進程工作,但node.js目前不支持它們。 有幾個擴展提供支持,例如: http//github.com/cramforce/node-worker

您仍然可以通過標准的“需求”機制重用模塊和代碼。 您只需確保對工作人員的初始分派傳遞處理結果所需的所有信息。

使用child_process是一種解決方案。 但與Go goroutines相比,每個生成的子進程可能會消耗大量內存

您還可以使用基於隊列的解決方案,例如kue

暫無
暫無

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

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