Apache服務器的缺點: 阻塞
線程是可以獨立運行的最小的CPU單位, 可以在一個進程里并發運行, 共享該進程下的內存地址空間。
我們可以看到同一個進程下的線程是會共享相同的文件和內存的(內存地址空間),所以大家可以想象,當不同的線程需要占用同一個變量時,根據先到先得的原則,先到的線程在運作時,后來的線程只能在旁邊等待,也就是加入到了阻塞排隊序列。所以這就是造成線程阻塞的原因。
因此,雖說進程可以支持多個線程,它們看似同時執行,但互相之間并不同步。一個進程中的多個線程共享相同的內存地址空間,這就意味著它們可以訪問相同的變量和對象,而且它們從同一堆中分配對象。盡管這讓線程之間共享信息變得更容易,因為程序設計者必須小心,確保它們不會妨礙同一進程里的其它線程。
了解了多線程并行的缺陷后,我們就可以更好地理解NodeJS的強大所在了。因為NodeJS是異步單線程的!
服務端取數據實例:
代碼執行到第一行的時候線程會阻塞,等待query返回結果,然后繼續處理。由于數據庫查詢、磁盤讀寫、網絡通信等原因(所謂的I/O)阻塞時間會非常大(相對于CPU始終頻率)。對于高并發的訪問,一方面線程長期阻塞等待,另一方面為了應付新情求而不斷添加新線程,會浪費大量系統資源,同時線程的增加也會也會占用大量的CPU時間來處理內存上下文切換。看看node.js怎么處理。
看到沒,就四個字:異步回調。query的第二個參數是一個回調函數,進程執行到db.query的時候不會等待結果返回,而是直接繼續執行下面的語句,直到進入事件循環。當數據庫執行結果返回的時候會將事件發送到事件隊列,等到線程進入事件循環后才會調用之前的回調函數。更專業的說法是異步I/O。只要單線程就可以。
工作原理:
NodeJS的工作原理其實就是事件循環。可以說每一條NodeJS的邏輯都是寫在回調函數里面的,而回調函數都是有返回之后才異步執行的!
你可以想象一下,NodeJS在寒風中面對著10萬并發大軍,OK,沒問題,上來敵人一個扔到城里,上來一個又扔到城里。城里全民皆兵,可以很好地消化這些敵人。但如果上來一個類似于張飛趙云這樣的人物,老Node心里一慘,和張飛大戰300回合,把他打殘了,再扔到城里。那后面的10萬大軍就得等這300回合。。。
所以這說明什么?說明NodeJS不是沒有阻塞,而是阻塞不發生在后續回調的流程,而會發生在NodeJS本身對邏輯的計算和處理。我們已經知道,NodeJS的分發能力無比強大,可以循環事件進行異步回調。但如果在循環事件時遇到復雜的邏輯運算,那么單薄的單線程怎么支撐得起上百萬的邏輯+并發呢?NodeJS它的所有I/O、網絡通信等比較耗時的操作,都可以交給worker threads執行再回調,所以很快。但CPU的正常操作,它就只能自己抗了。?
說到這里,各位對NodeJS的特性估計也大概有個譜了。所以說適用的場景基本是呼之欲出了~!
解決了什么問題:
1. 并發連接
單線程-->多線程
多線程問題: 服務端與客戶端每建立一個連接,都要為這個連接分配一套配套的資源,主要體現為系統內存資源
以PHP為例,維護一個連接可能需要20M的內存。這就是為什么一般并發量一大,就需要多開服務器
我們同樣是要發起請求,等待服務器端響應;但是與銀行例子不同的是,這次我們點完餐后拿到了一個號碼,拿到號碼,我們往往會在位置上等待,而在我們后面的請求會繼續得到處理,同樣是拿了一個號碼然后到一旁等待,接待員能一直進行處理。
等到飯菜做號了,會喊號碼,我們拿到了自己的飯菜,進行后續的處理(吃飯)。這個喊號碼的動作在NodeJS中叫做回調(Callback),能在事件(燒菜,I/O)處理完成后繼續執行后面的邏輯(吃飯),這體現了NodeJS的顯著特點,異步機制、事件驅動整個過程沒有阻塞新用戶的連接(點餐),也不需要維護已經點餐的用戶與廚師的連接。
基于這樣的機制,理論上陸續有用戶請求連接,NodeJS都可以進行響應,因此NodeJS能支持比Java、PHP程序更高的并發量雖然維護事件隊列也需要成本,再由于NodeJS是單線程,事件隊列越長,得到響應的時間就越長,并發量上去還是會力不從心。
更改連接到服務器的方式,每個連接發射(emit)一個在NodeJS引擎進程中運行的事件(Event),放進事件隊列當中,而不是為每個連接生成一個新的OS線程(并為其分配一些配套內存)。
2. I/O阻塞
Java、PHP也有辦法實現并行請求(子線程),但NodeJS通過回調函數(Callback)和異步機制會做得很自然。
優缺點:
1. 高并發(最重要的優點)
2. 適合I/O密集型應用
缺點:1. 不適合CPU密集型應用;CPU密集型應用給Node帶來的挑戰主要是:
由于JavaScript單線程的原因,如果有長時間運行的計算(比如大循環),
將會導致CPU時間片不能釋放,使得后續I/O無法發起;
解決方案:分解大型運算任務為多個小任務,使得運算能夠適時釋放,不阻塞I/O調用的發起;
2. 只支持單核CPU,不能充分利用CPU
3. 可靠性低,一旦代碼某個環節崩潰,整個系統都崩潰
原因:單進程,單線程
解決方案:(1)Nnigx反向代理,負載均衡,開多個進程,綁定多個端口;
(2)開多個進程監聽同一個端口,使用cluster模塊;
4. 開源組件庫質量參差不齊,更新快,向下不兼容
5. Debug不方便,錯誤沒有stack trace
適合場景
1. RESTful API
這是NodeJS最理想的應用場景,可以處理數萬條連接,本身沒有太多的邏輯,只需要請求API,組織數據進行返回即可。它本質上只是從某個數據庫中查找一些值并將它們組成一個響應。由于響應是少量文本,入站請求也是少量的文本,因此流量不高,一臺機器甚至也可以處理最繁忙的公司的API需求。
2. 統一Web應用的UI層
目前MVC的架構,在某種意義上來說,Web開發有兩個UI層,一個是在瀏覽器里面我們最終看到的,另一個在server端,負責生成和拼接頁面。
不討論這種架構是好是壞,但是有另外一種實踐,面向服務的架構,更好的做前后端的依賴分離。如果所有的關鍵業務邏輯都封裝成REST調用,就意味著在上層只需要考慮如何用這些REST接口構建具體的應用。那些后端程序員們根本不操心具體數據是如何從一個頁面傳遞到另一個頁面的,他們也不用管用戶數據更新是通過Ajax異步獲取的還是通過刷新頁面。
3. 大量Ajax請求的應用
例如個性化應用,每個用戶看到的頁面都不一樣,緩存失效,需要在頁面加載的時候發起Ajax請求,NodeJS能響應大量的并發請求。 總而言之,NodeJS適合運用在高并發、I/O密集、少量業務邏輯的場景。
既然NodeJS處理并發的能力強,但處理計算和邏輯的能力反而很弱,因此,如果我們把復雜的邏輯運算都搬到前端(客戶端)完成,而NodeJS只需要提供異步I/O,這樣就可以實現對高并發的高性能處理。情況就很多啦,比如:RESTFUL API、實時聊天、客戶端邏輯強大的單頁APP,具體的例子比如說:本地化的在線音樂應用,本地化的在線搜索應用,本地化的在線APP等。
順便提一下Apache,打壓了這么多,給顆甜棗。Apache由于其多線程高并發共享內存地址空間的特性,那就意味著如果服務器足夠強大,處理器足夠高核,Apache的運作將會非常良好,所以適用于(并發)異步處理相對較少,后臺計算量大,后臺業務邏輯復雜的應用程序
- 0. 前言
- 1. 基礎篇
- Node.js 入門介紹與安裝
- Node.js運行模式與入門實例
- npm介紹及與Node.js關聯
- Webpack 快速介紹及入門示例
- 模塊
- webpack
- gulp
- 2. 進階篇
- Node.js與Apache比較
- Node.js VS JavaScript
- CommonJS、AMD、CMD與ES6
- 3. 框架篇
- Node.js之Express快速介紹與入門示例
- 4. 實戰篇
- 調試器監聽
- webpack入門示例
- 5. 補充篇
- 語法
- 前端包管理工具:npm、cnpm、yarn
- 6. 問題解決篇
- webpack之options has an unknown property ‘contentBase‘. These properties are valid: