這一系列文章主要分析nodejs中的核心庫(kù)Libuv。
站在用戶(hù)的角度思考問(wèn)題,與客戶(hù)深入溝通,找到河南網(wǎng)站設(shè)計(jì)與河南網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶(hù)體驗(yàn)好的作品,建站類(lèi)型包括:網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名申請(qǐng)、虛擬主機(jī)、企業(yè)郵箱。業(yè)務(wù)覆蓋河南地區(qū)。我的參考書(shū):
樸靈的深入淺出nodejs
Jeffrey Richter的Windows核心編程
Anthony Williams的C++并發(fā)編程實(shí)戰(zhàn)
暫定為四篇:
1) 征服之初識(shí)篇(背景基礎(chǔ)以及重要的概念,圖示,libuv的編譯,例子) 2) 征服之進(jìn)展篇(內(nèi)部用到的c語(yǔ)言技巧以及QUEUE的使用) 3) 征服之高潮篇(線(xiàn)程池,iocp,同步,并發(fā),線(xiàn)程間的通信等) 4) 征服之和諧篇(Libuv的初始化,主循環(huán),主線(xiàn)程和線(xiàn)程池之間的和諧交往)
通過(guò)征服Libuv系列文章,目的是讓大家了解:
1) C語(yǔ)言之美(語(yǔ)法簡(jiǎn)單,功能強(qiáng)大,貼近硬件,適合系統(tǒng)級(jí)別編程,有很多技巧) 2) 多線(xiàn)程與線(xiàn)程的同步技術(shù) 3) 線(xiàn)程池技術(shù) 4) windows IOCP技術(shù) 5) 了解與ms PPL, intel TBB以及l(fā)ibdispatch(ios gcd)之間的異同點(diǎn)和優(yōu)缺點(diǎn)。 6) vs c++中多線(xiàn)程下的debug技巧
之所以稱(chēng)為了解,而不是掌握,是因?yàn)檫@些技術(shù)偏向于底層的系統(tǒng)編程,并不是一撮而就的,需要一定的時(shí)間和經(jīng)歷才能沉淀下來(lái)的。因此只能說(shuō)是讓大家了解。
為了簡(jiǎn)單期間,一些限制條件:
僅關(guān)注windows下的實(shí)現(xiàn) 僅重點(diǎn)分析libuv的通用cpu密集型計(jì)算的框架,了解cpu密集計(jì)算框架,實(shí)際上異步io就比較容易理解了 因?yàn)閣indows下,異步io通過(guò)IOCP(完成端口),會(huì)使代碼實(shí)現(xiàn)非常簡(jiǎn)單,而效率非常高。
在寫(xiě)此篇blog時(shí),突然感覺(jué)到,是不是應(yīng)該將libuv庫(kù)進(jìn)行拆分,將核心部分代碼抽離出來(lái)單獨(dú)運(yùn)行,這樣的好處就是要分析的代碼量要少很多,僅關(guān)注我們感興趣的代碼,有利于演示。
還有一個(gè)目的:
就是在簡(jiǎn)化精華版的基礎(chǔ)上升級(jí)一下,看看是否能夠?qū)崿F(xiàn)任務(wù)的動(dòng)態(tài)均衡(ms PPL庫(kù)和intel TBB庫(kù)都有動(dòng)態(tài)均衡,Work Stealing的功能,libuv目前確定沒(méi)有此功能,libdispatch目前沒(méi)發(fā)現(xiàn),源碼正在閱讀中,還沒(méi)看到)。這個(gè)實(shí)現(xiàn)難度還是很大的,就當(dāng)練練手吧,哈哈
我嘗試一下吧,看看是否可行!
(本文寫(xiě)于2016年,經(jīng)過(guò)這段時(shí)間研究,于2017-2-10放棄上面的拆分這個(gè)想法,實(shí)在是牽涉到的代碼以及操作系統(tǒng)太多,還是果斷放棄這個(gè)念想吧!)
前段時(shí)間,完成了一個(gè)微信項(xiàng)目,在后臺(tái)選型過(guò)程中,花了將近一個(gè)月考察了java,php幾個(gè)庫(kù),但最終卻選擇了nodejs,原因很簡(jiǎn)單:
1、java的配置實(shí)在是太麻煩了 2、php實(shí)在不熟悉,特別扭 3、nodejs使用js編程,我本人還是比較熟悉js的基礎(chǔ)部分(不算es6.0 es7.0標(biāo)準(zhǔn)),并且npm真好用,實(shí)在是資源太豐富了,有時(shí)選擇太多也是一種痛苦?。?4、nodejs基于異步io技術(shù),效率非常高。而且分布式部署簡(jiǎn)單。
通過(guò)無(wú)數(shù)次的比較,最終選定了令我非常滿(mǎn)意的組合,感覺(jué)最起碼少寫(xiě)了80%的代碼。
framework: strongloop/loopback(被IBM收購(gòu)了,只能說(shuō)強(qiáng)大無(wú)比)
樸靈的: wechat / wechat-api / wechat-oauth
supersheep: wechat-pay
數(shù)據(jù)庫(kù): mongodb用于不需要事務(wù)處理的表 mysql用于支付的事務(wù)處理
因?yàn)闆](méi)經(jīng)驗(yàn),在支付時(shí)需要事務(wù)處理,所以調(diào)整為使用mysql。不過(guò)從另外一個(gè)角度說(shuō)明loopback的強(qiáng)大,支持多數(shù)據(jù)源的統(tǒng)一api操作。
angularjs1.x
jquery
總體而言,該前后臺(tái)配套系統(tǒng)的開(kāi)發(fā)非常令人愉悅。前臺(tái)不好統(tǒng)計(jì),但是微信后臺(tái),至少少寫(xiě)了80%代碼??!
目前基于異步回調(diào)的開(kāi)發(fā)非常盛行,例如ios中的gcd,android中的基于接口回調(diào)方式。如果有過(guò)這些開(kāi)發(fā)經(jīng)驗(yàn),你會(huì)覺(jué)得nodejs的異步事件開(kāi)發(fā)模型還是蠻熟悉的。
隨著逐漸熟悉nodejs,深深的喜歡上了nodejs,因此更想深入的了解一下nodejs是如何實(shí)現(xiàn)的。
通過(guò)了解nodejs的代碼結(jié)構(gòu),結(jié)合深入淺出nodejs第三章的異步io所示流程圖,逐步驗(yàn)證其正確性。特別是看到libuv中具有深厚c語(yǔ)言功底的老鳥(niǎo)所寫(xiě)的代碼,忍不住想與大家分享c的代碼之美!!
官方介紹:
libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by Node.js
由上面的介紹,我們可以知道:
1) 跨平臺(tái):windows linux unix都支持 2) 異步io:windows IOCP 、linux epoll、unix kqueue 3) 主要目的是用于nodejs,實(shí)際還有很多庫(kù)或程序使用了libuv 例如目前風(fēng)頭正勁的跨平臺(tái).NetCore庫(kù)(就是曾經(jīng)大名鼎鼎的微軟的asp.net)也是使用libuv作為核心 4) libuv不僅僅支持異步io操作,而且還具有一個(gè)強(qiáng)勁的線(xiàn)程池,用于支持多線(xiàn)程并行的cpu密集型操作。 這個(gè)才是我們重點(diǎn)要分析的模塊
1) nodejs主要由google v8 javascript引擎和libuv組成 2) v8引擎綁定libuv實(shí)現(xiàn)的api,因此,既能使用ecma js標(biāo)準(zhǔn)語(yǔ)言來(lái)執(zhí)行js代碼,又能通過(guò)js調(diào)用libuv相關(guān)接口。 3) 由此可見(jiàn),libuv本身是獨(dú)立的c語(yǔ)言庫(kù),既可以直接使用c/c++來(lái)調(diào)用,也可以被綁定到c#(.NetCore)或者其他任何語(yǔ)言,例如java ,lua...... c/c++實(shí)現(xiàn)的庫(kù)大的好處就是能被各種其他編程語(yǔ)言所綁定和調(diào)用。 因?yàn)槠渌鞣N編程語(yǔ)言基本都是用c/c++來(lái)實(shí)現(xiàn)的,都留有接口與c/c++互調(diào)。
借用深入淺出nodejs(經(jīng)作者同意)中的兩張圖來(lái)了解整個(gè)流程:
一定要明確的了解哪些事情是發(fā)生在主線(xiàn)程的,哪些事情是發(fā)生在線(xiàn)程池中的!!!
nodejs就是數(shù)據(jù)通過(guò)v8引擎(主線(xiàn)程:數(shù)據(jù)輸入)傳遞給Libuv進(jìn)行處理(線(xiàn)程池:數(shù)據(jù)處理—根據(jù)數(shù)據(jù)類(lèi)型不同,io數(shù)據(jù)由IOCP[windows]線(xiàn)程池處理,通用計(jì)算則由自己實(shí)現(xiàn)的線(xiàn)程池處理),libuv處理好后通知v8引擎我已經(jīng)完成了,你來(lái)進(jìn)行完成處理(主線(xiàn)程:完成回調(diào),信息輸出)。
其實(shí)從上面的敘述可以了解到以下幾點(diǎn):
1) v8 javascript引擎是單線(xiàn)程的,數(shù)據(jù)的輸入,信息的輸出(完成回調(diào))都是在主線(xiàn)程中處理。這一點(diǎn)以后在源碼分析中我們可以驗(yàn)證,通過(guò)vs強(qiáng)大的debug功能,我們可以很清晰的看到具體代碼到底是運(yùn)行在哪個(gè)線(xiàn)程中。
2) 但是數(shù)據(jù)處理模塊(libuv)不是單線(xiàn)程的,它根據(jù)數(shù)據(jù)請(qǐng)求類(lèi)型是否是io請(qǐng)求(socket,文件讀寫(xiě)或管道等)還是work請(qǐng)求(非io請(qǐng)求)。不同的請(qǐng)求使用不同的處理策略。例如io請(qǐng)求,在windows下用IOCP,在linux下用epool。而work請(qǐng)求,windows和linux下都是使用統(tǒng)一的,自己實(shí)現(xiàn)的線(xiàn)程池。而我們的源碼分析就是要證明上面的描述。
5、visual studio編譯及測(cè)試Libuv庫(kù):
1) 如何編譯libuv庫(kù) 2) 在測(cè)試libuv庫(kù)的時(shí)候,如何解決各種鏈接錯(cuò)誤 3) 重點(diǎn)是根據(jù)鏈接錯(cuò)誤編號(hào),通過(guò)msdn來(lái)定位問(wèn)題、分析問(wèn)題以及解決問(wèn)題,掌握方法學(xué)是關(guān)鍵
編譯:
1) 安裝python2.,6或者2.7版本,并設(shè)置好環(huán)境變量。千萬(wàn)別安裝3.0或以上版本。
目前很多跨平臺(tái)庫(kù),都通過(guò)python腳本進(jìn)行編譯或引導(dǎo),因此python是必裝程序。、
2) 在cmd中:
cd到你libuv所在目錄
并輸入 git clone https://chromium.googlesource.com/external/gyp.git build/gyp
將google gyp系統(tǒng)克隆到libuv所在目錄的build/gyp文件夾下面
由于GFW的關(guān)系,google網(wǎng)站無(wú)法訪(fǎng)問(wèn),作為程序員,我想你應(yīng)該有辦法、
3) 運(yùn)行l(wèi)ibuv所在目錄下的vcbuild.bat,生成visual studio解決方案。雙擊運(yùn)行,然后F5編譯調(diào)試,一切盡在掌握中!
如果你目前可以登陸google網(wǎng)站,則直接運(yùn)行vcbuild.bat,如果沒(méi)安裝過(guò)gyp的話(huà),自動(dòng)會(huì)下載gyp構(gòu)建系統(tǒng)。因此可以省略第二步
因?yàn)長(zhǎng)ibuv在windows中編譯后的結(jié)果是一個(gè)靜態(tài)鏈接庫(kù),那么我們需要重新建個(gè)exe工程,并將libuv.lib鏈接入exe這個(gè)程序,步驟如下:
1) 新建一個(gè)win32/控制臺(tái)/空項(xiàng)目,名稱(chēng)例如:LibuvTest
2) 在源文件中添加main.cpp文件,實(shí)現(xiàn)以下簡(jiǎn)單代碼并運(yùn)行,F(xiàn)5,調(diào)試運(yùn)行
3) 如果運(yùn)行時(shí)報(bào)無(wú)法啟動(dòng)程序,則將exe設(shè)置為啟動(dòng)項(xiàng):
4) 使用uv_wort_t進(jìn)行cpu密集計(jì)算的測(cè)試代碼,F5編譯調(diào)試
F5運(yùn)行后出現(xiàn)鏈接錯(cuò)誤
5) LNK2019鏈接錯(cuò)誤,表示相關(guān)的Lib沒(méi)有被引入,觀察相關(guān)錯(cuò)誤內(nèi)容,可以確定Libuv.lib沒(méi)有被導(dǎo)入。
1、查找到Libuv.lib被編譯到的目錄
2、在LibuvTest項(xiàng)目上右擊鼠標(biāo),選擇屬性菜單,彈出libuvTest Property Pages,然后選擇Linker/input/Additional Dependencies界面
3、相對(duì)路徑方式,添加libuv.lib庫(kù)
參考
6) 添加好libuv.lib后,繼續(xù)F5調(diào)試,仍舊有大量鏈接錯(cuò)誤:
7) 解決LNK4098錯(cuò)誤,該錯(cuò)誤是由于LIBCMTD庫(kù)引起的,我們忽略該庫(kù),具體如下:
8) 解決LNK2019錯(cuò)誤,該錯(cuò)誤前面也提到過(guò),是由于沒(méi)有引入相應(yīng)的lib導(dǎo)致的。
1、查看具體的鏈接錯(cuò)誤描述,可以看到和socket相關(guān)的
2、msdn是法寶,在msdn中查找closesocket函數(shù),看看closesocket屬于哪個(gè)lib庫(kù)的?
3、按照前面添加libuv.lib方式(),添加Ws2_32.lib。或者也可以參考該文檔使用另外一種方式:#pragma comment(lib,”path”)方式進(jìn)行鏈接。
9) 周而復(fù)始,不斷使用8)的方式,解決所有LNK2019鏈接錯(cuò)誤
10) 最終需要的所有鏈接庫(kù)
11) F5繼續(xù)編譯調(diào)試,正確運(yùn)行輸出結(jié)果
12) 至此為止,libuv的uv_work_t的Demo能夠編譯并且正確運(yùn)行。我們也可以休息一下了! 可能太基礎(chǔ)了,但是也是體現(xiàn)了解決問(wèn)題的方法,一并記錄下來(lái),供大家參考。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。