成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Node.js:淺析高并發(fā)與分布式集群

Lin_YT / 3473人閱讀

摘要:而在單線程環(huán)境下,繞不過錯(cuò)誤就意味著導(dǎo)致應(yīng)用退出,重啟恢復(fù)的間隙會(huì)導(dǎo)致服務(wù)中斷,這是我們不愿意看到的。這也是支持高并發(fā)的重要原因之一實(shí)際上不光是操作,的絕大多數(shù)操作都是以這種異步的方式進(jìn)行的。

本文首發(fā)于我的個(gè)人博客: kmknkk.xin   
不足之處歡迎斧正!
Node特性:高并發(fā)

在解釋node為什么能夠做到高并發(fā)之前,不妨先了解一下node的其他幾個(gè)特性:

單線程

我們先來明確一個(gè)概念,即:node是單線程的,這一點(diǎn)與JavaScript在瀏覽器中的特性相同,并且在node中JavaScript主線程與其他線程(例如I/O線程)是無法共享狀態(tài)的。

單線程的好處就是:

無需像多線程那樣去關(guān)注線程之間的狀態(tài)同步問題

沒有線程切換所帶來的開銷

沒有死鎖存在

當(dāng)然單線程也有許多壞處:

無法充分利用多核CPU

大量計(jì)算占用CPU會(huì)導(dǎo)致應(yīng)用阻塞(即不適用CPU密集型)

錯(cuò)誤會(huì)引起整個(gè)應(yīng)用的退出

不過在今天看來,這些壞處都已經(jīng)不再是問題或者得到了適當(dāng)?shù)慕鉀Q:

(1) 創(chuàng)建進(jìn)程 or 細(xì)分實(shí)例

關(guān)于第一個(gè)問題,最直白解決方案就是使用child_process核心模塊或者cluster:child_process 和 net 組合應(yīng)用。我們可以通過在一臺多核服務(wù)器上創(chuàng)建多個(gè)進(jìn)程(通常使用fork操作)來充分利用每個(gè)核心,不過要處理好進(jìn)程間通信問題。

另一個(gè)方案是,我們可以將物理機(jī)器劃分為多臺單核的虛擬機(jī),并通過pm2等工具,管理多臺虛擬機(jī)形成一個(gè)集群架構(gòu),高效運(yùn)行所需服務(wù),至于每臺機(jī)器間的通信(狀態(tài)同步)我這里先按下不表,在下文的Node分布式架構(gòu)中再做詳細(xì)說明。

(2) 時(shí)間片輪轉(zhuǎn)

關(guān)于第二點(diǎn),我跟小伙伴討論過后認(rèn)為可以通過時(shí)間片輪轉(zhuǎn)方式,在單線程上模擬多線程,適當(dāng)減少應(yīng)用阻塞的感覺(雖然這種方法不會(huì)真的像多線程那樣節(jié)約時(shí)間)

(3) 負(fù)載均衡、壞點(diǎn)監(jiān)控/隔離

至于第三點(diǎn),我跟小伙伴們也討論過,認(rèn)為主要的痛點(diǎn)就在于node不同于JAVA,它所實(shí)現(xiàn)的邏輯是以異步為主的。

這就導(dǎo)致了node無法像JAVA一樣方便地使用 try/catch 來來捕獲并繞過錯(cuò)誤,因?yàn)闊o法確定異步任務(wù)會(huì)何時(shí)傳回異常。而在單線程環(huán)境下,繞不過錯(cuò)誤就意味著導(dǎo)致應(yīng)用退出,重啟恢復(fù)的間隙會(huì)導(dǎo)致服務(wù)中斷,這是我們不愿意看到的。

當(dāng)然,在服務(wù)器資源豐富的當(dāng)下,我們可以通過 pm2 或 nginx 這些工具,動(dòng)態(tài)的判斷服務(wù)狀態(tài)。在服務(wù)出錯(cuò)時(shí)隔離壞點(diǎn)服務(wù)器,將請求轉(zhuǎn)發(fā)到正常服務(wù)器上,并重啟壞點(diǎn)服務(wù)器以繼續(xù)提供服務(wù)。這也是Node分布式架構(gòu)的一部分。

異步I/O

你可能會(huì)問,既然node是單線程的,事件全部在一個(gè)線程上處理,那不是應(yīng)該效率很低、與高并發(fā)相悖嗎?

恰恰相反,node的性能很高。原因之一就是node具有異步I/O特性,每當(dāng)有I/O請求發(fā)生時(shí),node會(huì)提供給該請求一個(gè)I/O線程。然后node就不管這個(gè)I/O的操作過程了,而是繼續(xù)執(zhí)行主線程上的事件,只需要在該請求返回回調(diào)時(shí)在處理即可。也就是node省去了許多等待請求的時(shí)間。

這也是node支持高并發(fā)的重要原因之一

實(shí)際上不光是I/O操作,node的絕大多數(shù)操作都是以這種異步的方式進(jìn)行的。它就像是一個(gè)組織者,無需事必躬親,只需要告訴成員們?nèi)绾握_的進(jìn)行操作并接受反饋、處理關(guān)鍵步驟,就能使得整個(gè)團(tuán)隊(duì)高效運(yùn)行。

事務(wù)驅(qū)動(dòng)

你可能又要問了,node怎么知道請求返回了回調(diào),又應(yīng)該何時(shí)去處理這些回調(diào)呢?

答案就是node的另一特性:事務(wù)驅(qū)動(dòng),即主線程通過event loop事件循環(huán)觸發(fā)的方式來運(yùn)行程序

這是node支持高并發(fā)的另一重要原因

圖解node環(huán)境下的Event loop:

   ┌───────────────────────┐
┌─>│        timers         │<————— 執(zhí)行 setTimeout()、setInterval() 的回調(diào)
│  └──────────┬────────────┘
|             |<-- 執(zhí)行所有 Next Tick Queue 以及 MicroTask Queue 的回調(diào)
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │<————— 執(zhí)行幾乎所有的回調(diào),除了 close callbacks 以及 timers 調(diào)度的回調(diào)和 setImmediate() 調(diào)度的回調(diào)
│  └──────────┬────────────┘
|             |<-- 執(zhí)行所有 Next Tick Queue 以及 MicroTask Queue 的回調(diào)
│  ┌──────────┴────────────┐
│  │     idle, prepare     │<————— 內(nèi)部調(diào)用,可忽略
│  └──────────┬────────────┘     
|             |<-- 執(zhí)行所有 Next Tick Queue 以及 MicroTask Queue 的回調(diào)
|             |                   ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │ - (retrieve new I/O events; node will block here when appropriate)
│  │         poll          │<─────┤  connections, │ 
│  └──────────┬────────────┘      │   data, etc.  │ 
│             |                   |               | 
|             |                   └───────────────┘
|             |<-- 執(zhí)行所有 Next Tick Queue 以及 MicroTask Queue 的回調(diào)
|  ┌──────────┴────────────┐      
│  │        check          │<————— setImmediate() 的回調(diào)將會(huì)在這個(gè)階段執(zhí)行
│  └──────────┬────────────┘
|             |<-- 執(zhí)行所有 Next Tick Queue 以及 MicroTask Queue 的回調(diào)
│  ┌──────────┴────────────┐
└──┤    close callbacks    │<————— socket.on("close", ...)
   └───────────────────────┘

poll階段:

當(dāng)進(jìn)入到poll階段,并且沒有timers被調(diào)用的時(shí)候,會(huì)發(fā)生下面的情況:

(1)如果poll隊(duì)列不為空:

Event Loop 將同步的執(zhí)行poll queue里的callback(新的I/O事件),直到queue為空或者執(zhí)行的callback到達(dá)上線。

(2)如果poll隊(duì)列為空:

如果腳本調(diào)用了setImmediate(), Event Loop將會(huì)結(jié)束poll階段并且進(jìn)入到check階段執(zhí)行setImmediate()的回調(diào)。

如果腳本沒有setImmediate()調(diào)用,Event Loop將會(huì)等待回調(diào)(新的I/O事件)被添加到隊(duì)列中,然后立即執(zhí)行它們。

當(dāng)進(jìn)入到poll階段,并且調(diào)用了timers的話,會(huì)發(fā)生下面的情況:

一旦poll queue是空的話,Event Loop會(huì)檢查是否timers, 如果有1個(gè)或多個(gè)timers時(shí)間已經(jīng)到達(dá),Event Loop將會(huì)回到timer階段并執(zhí)行那些timer的callback(即進(jìn)入到下一次tick)。

優(yōu)先級:

根據(jù)上面的圖,我們不難得出:

Next Tick Queue > MicroTask Queue

那么setTimeout、setInterval和setImmediate誰快呢?

答案是:不確定

單單從執(zhí)行圖上看,如果兩者都是在mian module里定義的,那么:setTimeout、setInterval > setImmediate

但是有兩個(gè)條件制約了這一結(jié)論:

event loop初始化需要一定時(shí)間

setTimeout有最小毫秒數(shù)(一般認(rèn)為最少1ms)

所以當(dāng) event loop準(zhǔn)備時(shí)間 > setTimeout毫秒數(shù)時(shí),進(jìn)入timers檢查時(shí)已有setTimeout的任務(wù),故timeout先輸出。反之則immediate先輸出。

如果是在poll階段定義的setTimeout和setImmediate,那么immediate先于timeout輸出。原因是在poll階段,會(huì)先進(jìn)入check階段再進(jìn)入timers階段。例如:

const fs = require("fs");

fs.readFile("./test.txt", "utf8", (err, data) => {
    setTimeout( () => {
        console.log("setTimeout");
    }, 0);
    setImmediate( () => {
        console.log("setImmediate");
    })
})

/**
 *
 * console:
 * > setImmediate
 * > setTimeout
 *
 **/

多說一句:
由于timer需要從紅黑樹中取出定時(shí)器來判斷時(shí)間是否到了,時(shí)間復(fù)雜度為O(lg(n)),故如果想立即異步執(zhí)行一個(gè)事件,最好不要用 setTimeout(func, 0)。而是使用 process.nextTick() 來完成。

分布式Node架構(gòu)

我了解到的Node集群架構(gòu)主要分為以下幾個(gè)模塊:

Nginx(負(fù)載均衡、調(diào)度) -> Node集群 -> Redis(同步狀態(tài))

按我的理解整理了一副圖:

當(dāng)然,這應(yīng)該是比較理想狀態(tài)下的架構(gòu)方式。因?yàn)殡m然 Redis 的讀/寫相當(dāng)快,但這是因?yàn)槠鋵?shù)據(jù)存儲(chǔ)在內(nèi)存池里,在內(nèi)存上進(jìn)行相關(guān)操作。

這對于服務(wù)器的內(nèi)存負(fù)荷是相當(dāng)高的,所以通常我們還是會(huì)在架構(gòu)中加入 Mysql,如下圖:

先解釋一下這幅圖:
當(dāng)用戶數(shù)據(jù)到來時(shí),將數(shù)據(jù)先寫入 Mysql,Node 需要數(shù)據(jù)時(shí)再去 Redis 讀取,若沒有找到再去 Mysql 里面查詢想要的數(shù)據(jù),并寫入 Redis,下次使用時(shí)就可以直接去 Redis 里面查詢了。

加入 Mysql 相較于只在 Redis 里讀/寫的好處有:

(1)避免了短期內(nèi)無用的數(shù)據(jù)寫入 Redis,占用內(nèi)存,減輕 Redis 負(fù)擔(dān)

(2)在后期需要對數(shù)據(jù)進(jìn)行特定查詢、分析的時(shí)候(比如分析運(yùn)營活動(dòng)用戶漲幅),SQL關(guān)系查詢能提供很大的幫助

當(dāng)然在應(yīng)對短時(shí)間大流量寫入的時(shí)候,我們也可以直接將數(shù)據(jù)寫入 Redis,以達(dá)到快速存儲(chǔ)數(shù)據(jù)、增加服務(wù)器應(yīng)對流量能力的目的,等流量下去了再多帶帶將數(shù)據(jù)寫入 Mysql。

簡單介紹完了大體的架構(gòu)組成,接下來我們來細(xì)看每個(gè)部分的細(xì)節(jié):

流量接入層

流量接入層所做的就是對所有接受的流量進(jìn)行處理,提供了以下服務(wù):

流量緩沖

分流和轉(zhuǎn)發(fā)

超時(shí)檢測

與用戶建立連接超時(shí)

讀取用戶body超時(shí)

連接后端超時(shí)

讀后端響應(yīng)頭超時(shí)

寫響應(yīng)超時(shí)

與用戶長連接超時(shí)

集群健康檢查/隔離壞點(diǎn)服務(wù)器

隔離壞點(diǎn)服務(wù)器并嘗試修復(fù)/重啟,直到該服務(wù)器恢復(fù)正常

失敗重試機(jī)制

在請求轉(zhuǎn)發(fā)到某集群某機(jī)器上,返回失敗后,將該請求轉(zhuǎn)發(fā)到該集群的別的機(jī)器,或者跨集群的機(jī)器上進(jìn)行重試

連接池/會(huì)話保持機(jī)制

對于延遲敏感用戶使用連接池機(jī)制,減少建立連接的時(shí)間

安全防護(hù)

數(shù)據(jù)分析

當(dāng)轉(zhuǎn)發(fā)到各個(gè)產(chǎn)品線后就到了負(fù)載層工作的時(shí)候了:將請求根據(jù)情況轉(zhuǎn)發(fā)到各地機(jī)房

當(dāng)然,這個(gè)平臺并不止轉(zhuǎn)發(fā)這一個(gè)功能,你可以把它理解為一個(gè)大型的私有云系統(tǒng),提供以下服務(wù):

文件上傳/服務(wù)線上部署

線上配置修改

設(shè)置定時(shí)任務(wù)

線上系統(tǒng)監(jiān)控/日志打印服務(wù)

線上實(shí)例管理

鏡像中心

等等...

Node集群層

這一層主要的工作是:

(1)編寫可靠的 Node 代碼,為需求提供后端服務(wù)

(2)編寫高性能查詢語句,與 Redis、Mysql 交互,提高查詢效率

(3)通過 Redis 同步集群里各個(gè) Node 服務(wù)的狀態(tài)

(4)通過硬件管理平臺,管理/監(jiān)控物理機(jī)器的狀態(tài)、管理IP地址等

(當(dāng)然這部分我只是粗淺地列列條目,還是需要時(shí)間來積累、深入理解)

數(shù)據(jù)庫層

這一層主要的工作是:

(1)創(chuàng)建 Mysql 并設(shè)計(jì)相關(guān)頁、表;建立必要的索引、外鍵,提升查詢便利性

(2)部署 redis 并向 Node 層提供相應(yīng)接口

總結(jié)

雖然 Node 的單線程特性給其提供的服務(wù)帶來了許多問題,但只要我們積極面對這些問題,用合理的方法(如使用 child_process 等模塊或構(gòu)建分布式集群)去解決他們,發(fā)揮 Node 的各種優(yōu)勢,就可以享受到它所帶來的好處!

待更新:

Redis相關(guān)特性

sql查詢性能指標(biāo) & 優(yōu)化策略

Node內(nèi)存監(jiān)控 & 內(nèi)存泄露排查/處理

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/96491.html

相關(guān)文章

  • SegmentFault 技術(shù)周刊 Vol.37 - 布式緩存利器:Redis

    摘要:持久化到中反向代理的負(fù)載均衡基于的集群搭建如何實(shí)現(xiàn)從中訂閱消息轉(zhuǎn)發(fā)到客戶端的擴(kuò)展是阻塞式,使用訂閱發(fā)布模式時(shí),會(huì)導(dǎo)致整個(gè)進(jìn)程進(jìn)入阻塞。緩存是用于解決高并發(fā)場景下系統(tǒng)的性能及穩(wěn)定性問題的銀彈。 showImg(https://segmentfault.com/img/bVYE6k?w=900&h=385); Redis 是由意大利程序員 Salvatore Sanfilippo(昵稱:a...

    binaryTree 評論0 收藏0
  • 淺析云計(jì)算架構(gòu)的服務(wù)管理

    摘要:以上是對云計(jì)算數(shù)據(jù)中心架構(gòu)的一些剖析。因此,云計(jì)算數(shù)據(jù)中心的架構(gòu)也會(huì)隨著社會(huì)的進(jìn)步不斷調(diào)整和優(yōu)化?! ≡朴?jì)算,應(yīng)當(dāng)高度貼合網(wǎng)絡(luò)未來更高層次的發(fā)展趨勢,著力于提高網(wǎng)絡(luò)數(shù)據(jù)處理和存儲(chǔ)能力,致力于低碳高效的利用基礎(chǔ)資源。具體而言,應(yīng)著重從高端服務(wù)器、高密度低成本服務(wù)器、海量存儲(chǔ)設(shè)備和高性能計(jì)算設(shè)備等基礎(chǔ)設(shè)施領(lǐng)域提高云計(jì)算數(shù)據(jù)中心的數(shù)據(jù)處理能力。云計(jì)算要求基礎(chǔ)設(shè)施具有良好的彈性、擴(kuò)展性、自動(dòng)化、數(shù)據(jù)...

    Ajian 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<