国产粉嫩无码不卡在线观看,酒店大战丝袜高跟鞋人妻,特级精品毛片免费观看,欧美亚洲日本国产综合在线

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

作者|Virgafox

譯者|姚佳靈

出處丨前端之巔

說明:本文根據(jù)原文作者的系列文章編輯而成,略有刪改。

在這篇文章中,我們將介紹關(guān)于開發(fā) Node.js web 應(yīng)用程序的一些最佳實踐,重點關(guān)注效率和性能,以便用更少的資源獲得最佳結(jié)果。

提高 web 應(yīng)用程序吞吐量的一種方法是對其進行擴展,多次實例化其以平衡在多個實例之間的傳入連接,接來下我們要介紹的是如何在多個內(nèi)核上或多臺機器上對 Node.js 應(yīng)用程序進行水平擴展。

在強制性規(guī)則中,有一些好的實踐可以用來解決這些問題,像拆分 API 和工作進程、采用優(yōu)先級隊列、管理像 cron 進程這樣的周期性作業(yè),在向上擴展到 N 個進程 / 機器時,這不需要運行 N 次。

水平擴展 Node.js 應(yīng)用程序

水平擴展是復(fù)制應(yīng)用程序?qū)嵗怨芾泶罅總魅脒B接。 此操作可以在單個多內(nèi)核機器上執(zhí)行,也可以在不同機器上執(zhí)行。

垂直擴展是提高單機性能,它不涉及代碼方面的特定工作。

在同一臺機器上的多進程

提高應(yīng)用程序吞吐量的一種常用方法是為機器的每個內(nèi)核生成一個進程。 通過這種方式,Node.js 中請求的已經(jīng)有效的“并發(fā)”管理(請參見“事件驅(qū)動,非阻塞 I / O”)可以相乘和并行化。

產(chǎn)生大于內(nèi)核的數(shù)量的大量進程可能并不好,因為在較低級別,操作系統(tǒng)可能會平衡這些進程之間的 CPU 時間。

擴展單機有不同的策略,但常見的概念是,在同一端口上運行多個進程,并使用某種內(nèi)部負載平衡來分配所有進程 / 核上的傳入連接。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

下面所描述的策略是標(biāo)準(zhǔn)的 Node.js 集群模式以及自動的,更高級別的 PM2 集群功能。

原生集群模式

原生 Node.js 群集模塊是在單機上擴展 Node 應(yīng)用程序的基本方法(請參閱 https://Node.js.org/api/cluster.html)。 你的進程的一個實例(稱為“master”)是負責(zé)生成其他子進程(稱為“worker”)的實例,每個進程對應(yīng)一個運行你的應(yīng)用程序的核。 傳入連接按照循環(huán)策略分發(fā)到所有 worker 進程,從而在同一端口上公開服務(wù)。

該方法的主要缺點是必須在代碼內(nèi)部管理 master 進程和 worker 進程之間的差異,通常使用經(jīng)典的 if-else 塊,不能夠輕易地修改進動態(tài)進程數(shù)。

下面的例子來自官方文檔:

const cluster = require(‘cluster’);const http = require(‘http’);const numCPUs = require(‘os’).cpus().length;if (cluster.isMaster) { console.log(`Master ${process.pid} is running`); // Fork workers. for (let i = 0; i < numCPUs; i ) { cluster.fork(); } cluster.on(‘exit’, (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); });} else { // Workers can share any TCP connection // In this case it is an HTTP server http.createServer((req, res) => { res.writeHead(200); res.end(‘hello worldn’); }).listen(8000); console.log(`Worker ${process.pid} started`);}

PM2 集群模式

如果你在使用 PM2 作為你的流程管理器(我也建議你這么做),那么有一個神奇的群集功能可以讓你跨所有內(nèi)核擴展流程,而無需擔(dān)心集群模塊。 PM2 守護程序?qū)⒊袚?dān)“master”進程的角色,它將生成你的應(yīng)用程序的 N 個進程作為 worker 進程, 并進行循環(huán)平衡。

通過這個方法,只需要按你為單內(nèi)核用途一樣地編寫你的應(yīng)用程序(我們稍后再提其中的一些注意事項),而 PM2 將關(guān)注多內(nèi)核部分。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

在集群模式下啟動你的應(yīng)用程序后,你可以使用“pm2 scale”調(diào)整動態(tài)實例數(shù),并執(zhí)行“0-second-downtime”重新加載,進程重新串聯(lián),以便始終至少有一個在線進程。

在生產(chǎn)中運行節(jié)點時,如果你的進程像很多其他你應(yīng)該考慮的有用的東西一樣崩潰了,那么 PM2 作為進程管理器將負責(zé)重新啟動你的進程。

如果你需要進一步擴展,那么你也許需要部署更多的機器。

具有網(wǎng)絡(luò)負載均衡的多臺機器

跨多臺機器進行擴展的主要概念類似于在多內(nèi)核上進行擴展,有多臺機器,每臺機器運行一個或多個進程,以及用于將流量重定向到每臺機器的均衡器。

一旦請求被發(fā)送到特定的節(jié)點,剛才所提到的內(nèi)部均衡器發(fā)送該流量到特定的進程。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

可以以不同方式部署網(wǎng)絡(luò)平衡器。 如果使用 AWS 來配置你的基礎(chǔ)架構(gòu),那么一個不錯的選擇是使用像 ELB(Elastic Load Balancer,彈性負載均衡器)這樣的托管負載均衡器,因為它支持自動擴展等有用功能,并且易于設(shè)置。

但是如果你想按傳統(tǒng)的方式來做,你可以自己部署一臺機器并用 NGINX 設(shè)置一個均衡器。 指向上游的反向代理的配置對于這個任務(wù)來說非常簡單。 下面是配置示例:

http { upstream myapp1 { server srv1.example.com; server srv2.example.com; server srv3.example.com; } server { listen 80; location / { proxy_pass http://myapp1; } }}

通過這種方式,負載均衡器將是你的應(yīng)用程序暴露給外部世界的唯一入口點。 如果擔(dān)心它成為基礎(chǔ)架構(gòu)的單點故障,可以部署多個指向相同服務(wù)器的負載均衡器。

為了在均衡器之間分配流量(每個均衡器都有自己的 IP 地址),可以向主域添加多個 DNS“A”記錄,從而 DNS 解析器將在你的均衡器之間分配流量,每次都解析為不同的 IP 地址。通過這種方式,還可以在負載均衡器上實現(xiàn)冗余。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

我們在這里看到的是如何在不同級別擴展 Node.js 應(yīng)用程序,以便從你的基礎(chǔ)架構(gòu)(從單節(jié)點到多節(jié)點和多均衡器)獲得盡可能高的性能,但要小心:如果想在多進程環(huán)境中使用你的應(yīng)用程序,必須做好準(zhǔn)備,否則會遇到一些問題和不期望的行為。

在向上擴展你的進程時,為了避免出現(xiàn)不期望的行為,現(xiàn)在我們來談?wù)劚仨毧紤]到的一些方面。

讓Node.js 應(yīng)用程序做好擴展準(zhǔn)備

從 DB 中分離應(yīng)用程序?qū)嵗?/p>

首先不是代碼問題,而是你的基礎(chǔ)結(jié)構(gòu)。

如果希望你的應(yīng)用程序能夠跨不同主機進行擴展,則必須把你的數(shù)據(jù)庫部署在獨立的機器上,以便可以根據(jù)需要自由復(fù)制應(yīng)用程序機器。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

在同一臺機器上部署用于開發(fā)目的的應(yīng)用程序和數(shù)據(jù)庫可能很便宜,但絕對不建議用于生產(chǎn)環(huán)境,其中的應(yīng)用程序和數(shù)據(jù)庫必須能夠獨立擴展。 這同樣適用于像 Redis 這樣的內(nèi)存數(shù)據(jù)庫。

無狀態(tài)

如果生成你的應(yīng)用程序的多個實例,則每個進程都有自己的內(nèi)存空間。 這意味著即使在一臺機器上運行,當(dāng)你在全局變量中存儲某些值,或者更常見的是在內(nèi)存中存儲會話時,如果均衡器在下一個請求期間將您重定向到另一個進程,那么你將無法在那里找到它。

這適用于會話數(shù)據(jù)和內(nèi)部值,如任何類型的應(yīng)用程序范圍的設(shè)置。對于可在運行時更改的設(shè)置或配置,解決方案是將它們存儲在外部數(shù)據(jù)庫(存儲或內(nèi)存中)上,以使所有進程都可以訪問它們。

使用 JWT 進行無狀態(tài)身份驗證

身份驗證是開發(fā)無狀態(tài)應(yīng)用程序時要考慮的首要主題之一。 如果將會話存儲在內(nèi)存中,它們將作用于這單個進程。

為了正常工作,應(yīng)該將網(wǎng)絡(luò)負載均衡器配置為,始終將同一用戶重定向到同一臺機器,并將本地用戶重定向到同一用戶始終重定向到同一進程(粘性會話)。

解決此問題的一個簡單方法是將會話的存儲策略設(shè)置為任何形式的持久性,例如,將它們存儲在 DB 而不是 RAM 中。 但是,如果你的應(yīng)用程序檢查每個請求的會話數(shù)據(jù),那么每次 API 調(diào)用都會進行磁盤讀寫操作(I / O),從性能的角度來看,這絕對不是好事。

更好,更快的解決方案(如果你的身份驗證框架支持)是將會話存儲在像 Redis 這樣的內(nèi)存數(shù)據(jù)庫中。 Redis 實例通常位于應(yīng)用程序?qū)嵗獠?,例?DB 實例,但在內(nèi)存中工作會使其更快。 無論如何,在 RAM 中存儲會話會在并發(fā)會話數(shù)增加時需要更多內(nèi)存。

如果想采用更有效的無狀態(tài)身份驗證方法,可以看看 JSON Web Tokens。

JWT 背后的想法很簡單:當(dāng)用戶登錄時,服務(wù)器生成一個令牌,該令牌本質(zhì)上是包含有效負載的 JSON 對象的 base64 編碼,加上簽名獲得的哈希,該負載具有服務(wù)器擁有的密鑰。 有效負載可以包含用于對用戶進行身份驗證和授權(quán)的數(shù)據(jù),例如 userID 及其關(guān)聯(lián)的 ACL 角色。 令牌被發(fā)送回客戶端并由其用于驗證每個 API 請求。

當(dāng)服務(wù)器處理傳入請求時,它會獲取令牌的有效負載并使用其密鑰重新創(chuàng)建簽名。 如果兩個簽名匹配,則可以認為有效載荷有效并且不被改變,并且可以識別用戶。

重要的是要記住 JWT 不提供任何形式的加密。 有效負載僅用 base64 編碼,并以明文形式發(fā)送,因此如果需要隱藏內(nèi)容,則必須使用 SSL。

被 jwt.io 借用的以下模式恢復(fù)了身份驗證過程:

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

在認證過程中,服務(wù)器不需要訪問存儲在某處的會話數(shù)據(jù),因此每個請求都可以由非常有效的方式由不同的進程或機器處理。 RAM 中不保存數(shù)據(jù),也不需要執(zhí)行存儲 I / O,因此在向上擴展時這種方法非常有用。

S3 上的存儲

使用多臺機器時,無法將用戶生成的資產(chǎn)直接保存在文件系統(tǒng)上,因為這些文件只能由該服務(wù)器本地的進程訪問。 解決方案是,將所有內(nèi)容存儲在外部服務(wù)上,可以存儲在像 Amazon S3 這樣的專用服務(wù)上,并在你的數(shù)據(jù)庫中僅保存指向該資源的絕對 URL。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

然后,每個進程 / 機器都可以以相同的方式訪問該資源。

使用 Node.js 的官方 AWS sdk 非常簡單,可以輕松地將服務(wù)集成到你的應(yīng)用程序中。 S3 非常便宜并且針對此目的進行了優(yōu)化。即使你的應(yīng)用程序不是多進程的,它也是一個不錯的選擇。

正確配置 WebSockets

如果你的應(yīng)用程序使用 WebSockets 進行客戶端之間或客戶端與服務(wù)器之間的實時交互,則需要鏈接后端實例,以便在連接到不同節(jié)點的客戶端之間正確傳播廣播消息或消息。

Socket.io 庫為此提供了一個特殊的適配器,稱為 socket.io-redis,它允許你使用 Redis pub-sub 功能鏈接服務(wù)器實例。

為了使用多節(jié)點 socket.io 環(huán)境,還需要強制協(xié)議為“websockets”,因為長輪詢(long-polling)需要粘性會話才能工作。

以上這些對于單節(jié)點環(huán)境來說也是好的實例。

效率和性能的其他良好實踐

接下來,我們將介紹一些可以進一步提高效率和性能的其他實踐。

Web 和 worker 進程

你可能知道,Node.js 實際上是單線程的,因此該進程的單個實例一次只能執(zhí)行一個操作。 在 Web 應(yīng)用程序的生命周期中,執(zhí)行許多不同的任務(wù):管理 API 調(diào)用,讀取 / 寫入 DB,與外部網(wǎng)絡(luò)服務(wù)通信,執(zhí)行某種不可避免的 CPU 密集型工作等。

雖然你使用異步編程,但將所有這些操作委派給響應(yīng) API 調(diào)用的同一進程可能是一種非常低效的方法。

一種常見的模式是基于兩種不同類型的進程之間的職責(zé)分離,這兩種類型的進程組成了你的應(yīng)用程序,通常是 Web 進程和 worker 進程。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

Web 進程主要用于管理傳入的網(wǎng)絡(luò)呼叫,并盡快發(fā)送它們。 每當(dāng)需要執(zhí)行非阻塞任務(wù)時,例如發(fā)送電子郵件 / 通知、編寫日志、執(zhí)行觸發(fā)操作,其結(jié)果是不需要響應(yīng) API 調(diào)用,web 進程將操作委派給 worker 進程。

Web 和 worker 進程之間的通信可以用不同的方式實現(xiàn)。 一種常見且有效的解決方案是優(yōu)先級隊列,如下一段所描述的 Kue 中實現(xiàn)的優(yōu)先級隊列。

這種方法的一大勝利是,可以在相同或不同的機器上獨立擴展 web 和 worker 進程。

例如,如果你的應(yīng)用程序是高流量應(yīng)用程序,幾乎沒有生成的副作用,那么可以部署比 worker 進程更多的 web 進程,而如果很少有網(wǎng)絡(luò)請求為 worker 進程生成大量作業(yè),則可以重新分發(fā)相應(yīng)的資源。

Kue

為了使 web 和 worker 進程相互通信,隊列是一種靈活的方法,可以讓你不必擔(dān)心進程間通信。

Kue 是基于 Redis 的 Node.js 的通用隊列庫,允許你以完全相同的方式放入在相同或不同機器上生成的通信進程。

任何類型的進程都可以創(chuàng)建作業(yè)并將其放入隊列,然后將 worker 進程配置為選擇這些作業(yè)并執(zhí)行它們。 可以為每項工作提供許多選項,如優(yōu)先級、TTL、延遲等。

你生成的 worker 進程越多,執(zhí)行這些作業(yè)所需的并行吞吐量就越多。

Cron

應(yīng)用程序通常需要定期執(zhí)行某些任務(wù)。 通常,這種操作通過操作系統(tǒng)級別的 cron 作業(yè)進行管理,從你的應(yīng)用程序外部調(diào)用單個腳本。

在新機器上部署你的應(yīng)用程序時,用此方法就需要額外的工作,如果要自動部署,這會使進程感到不自在。

實現(xiàn)相同結(jié)果的更自在的方法是使用 NPM 上的可用 cron 模塊。 它允許你在 Node.js 代碼中定義 cron 作業(yè),使其獨立于 OS 配置。

根據(jù)上面描述的 web / worker 模式,worker 進程可以創(chuàng)建 cron,它調(diào)用一個函數(shù),定期將新作業(yè)放入隊列。

使用隊列使其更加干凈,并可以利用 kue 提供的所有功能,如優(yōu)先級,重試等。

當(dāng)你有多個 worker 進程時會出現(xiàn)問題,因為 cron 函數(shù)會同時喚醒每個進程上的應(yīng)用程序,并將多次執(zhí)行的同一作業(yè)放入隊列副本中。

為了解決這個問題,有必要確定將執(zhí)行 cron 操作的單個 worker 進程。

領(lǐng)導(dǎo)者選舉(Leader election)和 cron-cluster(cron 集群)

這種問題被稱為“領(lǐng)導(dǎo)者選舉”,對于這個特定的場景,有一個 NPM 包為我們做了一個叫做 cron-cluster 的技巧。

它暴露了為 cron 模塊提供動力的相同 API,但在設(shè)置過程中,它需要一個 redis 連接,用于與其他進程通信并執(zhí)行領(lǐng)導(dǎo)者選舉算法。

如何創(chuàng)建高性能、可擴展的Node.js應(yīng)用?(如何創(chuàng)建高性能,可擴展的node.js應(yīng)用程序)

使用 redis 作為單一事實來源,所有進程都會同意誰將執(zhí)行 cron,并且只有一份作業(yè)副本將被放入隊列中。 之后,所有 worker 進程都將有資格像往常一樣執(zhí)行作業(yè)。

緩存 API 調(diào)用

服務(wù)器端緩存是提高 API 調(diào)用的性能和反應(yīng)性的常用方法,但它是一個非常廣泛的主題,有很多可能的實現(xiàn)。

在像我們所描述的分布式環(huán)境中,使用 redis 來存儲緩存的值可能是使所有節(jié)點表現(xiàn)相同的最佳方法。

緩存需要考慮的最困難的方面是失效。 快速而簡陋的解決方案只考慮時間,因此緩存中的值在固定的 TTL 之后刷新,缺點是不得不等待下一次刷新以查看響應(yīng)中的更新。

如果你有更多的時間,最好在應(yīng)用程序級別實現(xiàn)失效,在 DB 上值更改時手動刷新 redis 緩存上的記錄。

結(jié) 論

我們在本文中介紹了一些有關(guān)擴展和性能的一些主題。 文中提供的建議可以作為指導(dǎo),可以根據(jù)你的項目的特定需求進行定制。

英文原文:

  • https://medium.com/iquii/good-practices-for-high-performance-and-scalable-node-js-applications-part-1-3-bb06b6204197
  • https://medium.com/iquii/good-practices-for-high-performance-and-scalable-node-js-applications-part-2-3-2a68f875ce79
  • https://medium.com/iquii/good-practices-for-high-performance-and-scalable-node-js-applications-part-3-3-c1a3381e1382

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 舉報,一經(jīng)查實,本站將立刻刪除。