日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

部署 Node.js 应用以完成服务器端渲染 Server Side Rendering 的性能调优

發(fā)布時間:2023/12/19 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 部署 Node.js 应用以完成服务器端渲染 Server Side Rendering 的性能调优 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文:Operationalizing Node.js for Server Side Rendering

在 Airbnb,我們花了數(shù)年時間將所有前端代碼穩(wěn)定地遷移到一致的架構(gòu)中,在該架構(gòu)中,整個網(wǎng)頁都被編寫為 React 組件的層次結(jié)構(gòu),其中包含來自我們 API 的數(shù)據(jù)。 Ruby on Rails 在將 Web 連接到瀏覽器方面所扮演的角色每天都在減少。事實上,很快我們將過渡到一項新服務,該服務將完全在 Node.js 中提供完全形成的、服務器呈現(xiàn)的網(wǎng)頁。此服務將為所有 Airbnb 產(chǎn)品呈現(xiàn)大部分 HTML。這個渲染引擎不同于我們運行的大多數(shù)后端服務,因為它不是用 Ruby 或 Java 編寫的。但它也不同于我們的心智模型和通用工具所圍繞的那種常見的 I/O 密集型 Node.js 服務。

當您想到 Node.js 時,您會設想您的高度異步應用程序同時高效地為數(shù)百或數(shù)千個連接提供服務。您的服務正在從整個城鎮(zhèn)提取數(shù)據(jù),并進行應用輕量級處理,以使其適合眾多客戶。也許您正在處理一大堆長期存在的 WebSocket 連接。您對非常適合該任務的輕量級并發(fā)模型感到滿意和自信。

服務器端渲染 (SSR) 打破了導致該愿景的假設。它是計算密集型的。 Node.js 中的用戶代碼在單個線程中運行,因此對于計算操作(與 I/O 相對),您可以并發(fā)執(zhí)行它們,但不能并行執(zhí)行。 Node.js 能夠并行處理大量異步 I/O,但會遇到計算限制。隨著請求的計算部分相對于 I/O 的增加,并發(fā)請求將對延遲產(chǎn)生越來越大的影響,因為 CPU 爭用。

考慮 Promise.all([fn1, fn2])。如果 fn1 或 fn2 是由 I/O 解析的承諾,您可以像這樣實現(xiàn)并行性:

如果 fn1 和 fn2 是計算的,它們將改為這樣執(zhí)行:

一個操作必須等待另一個完成才能運行,因為只有一個執(zhí)行線程。

對于服務器端渲染,當服務器進程處理多個并發(fā)請求時會出現(xiàn)這種情況。 并發(fā)請求將被正在處理的其他請求延遲:

在實踐中,請求通常由許多不同的異步階段組成,即使仍然主要是計算。 這可能導致更糟糕的交織。 如果我們的請求由一個像 renderPromise().then(out => formatResponsePromise(out)).then(body => res.send(body)) 這樣的鏈組成,我們可以有像這樣的請求交錯:

在這種情況下,兩個請求最終都會花費兩倍的時間。隨著并發(fā)性的增加,這個問題變得更糟。

此外,SSR 的共同目標之一是能夠在客戶端和服務器上使用相同或相似的代碼。這些環(huán)境之間的一個很大區(qū)別是客戶端上下文本質(zhì)上是單租戶,而服務器上下文是多租戶的。在客戶端輕松工作的技術(shù)(如單例或其他全局狀態(tài))將導致服務器上并發(fā)請求負載下的錯誤、數(shù)據(jù)泄漏和一般混亂。
這兩個問題只會成為并發(fā)問題。在較低的負載水平下或在您的開發(fā)環(huán)境的舒適單一租戶中,一切通常都能正常工作。
這導致了與 Node 應用程序的規(guī)范示例完全不同的情況。我們使用 JavaScript 運行時是因為它的庫支持和瀏覽器特性,而不是它的并發(fā)模型。在這個應用程序中,異步并發(fā)模型強加了它的所有成本,沒有或只有很少的好處。

一些經(jīng)驗分享

用戶發(fā)送請求到我們的主要 Rails 應用程序 Monorail,它將希望在任何給定頁面上呈現(xiàn)的 React 組件的 props 拼湊在一起,并使用這些 props 和組件名稱向 Hypernova 發(fā)出請求。 Hypernova 使用 props 渲染組件以生成 HTML 以返回到 Monorail,然后將其嵌入到頁面模板中并將整個內(nèi)容發(fā)送回客戶端。

在 SSR 渲染失敗(由于錯誤或超時)的情況下,回退是將組件及其道具嵌入頁面而不渲染 HTML,允許它們(希望)被客戶端成功渲染。 這導致我們將 SSR 視為一種可選的依賴項,并且我們能夠容忍一定數(shù)量的超時和失敗。 我們將調(diào)用超時設置為大約在我們調(diào)整值時觀察到的值。不出所料,我們以略低于 5% 的超時基線運行。

在日常流量負載高峰期進行部署時,我們會看到高達 40% 的 SSR 請求發(fā)生超時。類似 BadRequestError: Request aborted on deploys 的這些錯誤,掩蓋了所有其他應用程序/編碼錯誤。

我們曾將延遲歸咎于啟動延遲,而延遲實際上是由并發(fā)請求相互等待以使用 CPU 造成的。 從我們的性能指標來看,由于其他正在運行的請求而等待執(zhí)行所花費的時間與執(zhí)行請求所花費的時間無法區(qū)分。 這也意味著并發(fā)導致的延遲增加看起來與新代碼路徑或功能導致的延遲增加相同——實際上增加了任何單個請求的成本。

BadRequestError: Request aborted 錯誤也變得越來越明顯,不能用一般的慢啟動性能來解釋。 該錯誤來自正文解析器,特別是在客戶端在服務器能夠完全讀取請求正文之前中止請求的情況下發(fā)生。 客戶端放棄并關(guān)閉連接,帶走我們繼續(xù)處理請求所需的寶貴數(shù)據(jù)。 發(fā)生這種情況的可能性要大得多,因為我們開始處理一個請求,然后我們的事件循環(huán)被另一個請求的渲染阻塞,然后從我們被中斷的地方返回完成,卻發(fā)現(xiàn)客戶端已經(jīng)離開了。

我們決定通過使用我們擁有大量現(xiàn)有操作經(jīng)驗的兩個現(xiàn)成組件來解決這個問題:反向代理 (nginx) 和負載均衡器 (haproxy)。

Reverse Proxying and Load Balancing

為了利用我們的 SSR 服務器上存在的多個 CPU 內(nèi)核,我們通過內(nèi)置的 Node.js 集群模塊運行多個 SSR 進程。 由于這些是獨立的進程,我們能夠并行處理并發(fā)請求。

這里的問題是每個節(jié)點進程在請求的整個持續(xù)時間內(nèi)都被有效占用,包括從客戶端讀取請求正文。

雖然我們可以在單個進程中并行讀取多個請求,但這會導致在進行渲染時計算操作的交錯。

節(jié)點進程的使用與客戶端和網(wǎng)絡的速度耦合。

解決方案是使用緩沖反向代理來處理與客戶端的通信。 為此,我們使用 nginx。 Nginx 將來自客戶端的請求讀入緩沖區(qū),并在完全讀取后將完整請求傳遞給節(jié)點服務器。

這種傳輸通過環(huán)回或 unix 域套接字在機器上本地發(fā)生,這比機器之間的通信更快、更可靠。

通過 nginx 處理讀取請求,我們能夠?qū)崿F(xiàn)節(jié)點進程的更高利用率。

總結(jié)

服務器端渲染代表與 Node.js 擅長的規(guī)范的、主要是 I/O 工作負載不同的工作負載。了解異常行為的原因使我們能夠使用我們擁有現(xiàn)有操作經(jīng)驗的現(xiàn)成組件來解決它。

異步渲染仍然存在資源爭用。異步渲染解決進程或瀏覽器的響應問題,但不解決并行性或延遲問題。 這篇翻譯的博文重點介紹的是純計算工作負載的簡單模型。對于 IO 和計算的混合工作負載,請求并發(fā)會增加延遲,但具有允許更高吞吐量的好處。

更多Jerry的原創(chuàng)文章,盡在:“汪子熙”:

總結(jié)

以上是生活随笔為你收集整理的部署 Node.js 应用以完成服务器端渲染 Server Side Rendering 的性能调优的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。