您好,登錄后才能下訂單哦!
這一系列文章主要分析nodejs中的核心庫Libuv。
我的參考書:
樸靈的深入淺出nodejs
Jeffrey Richter的Windows核心編程
Anthony Williams的C++并發編程實戰
暫定為四篇:
1) 征服之初識篇(背景基礎以及重要的概念,圖示,libuv的編譯,例子) 2) 征服之進展篇(內部用到的c語言技巧以及QUEUE的使用) 3) 征服之高潮篇(線程池,iocp,同步,并發,線程間的通信等) 4) 征服之和諧篇(Libuv的初始化,主循環,主線程和線程池之間的和諧交往)
通過征服Libuv系列文章,目的是讓大家了解:
1) C語言之美(語法簡單,功能強大,貼近硬件,適合系統級別編程,有很多技巧) 2) 多線程與線程的同步技術 3) 線程池技術 4) windows IOCP技術 5) 了解與ms PPL, intel TBB以及libdispatch(ios gcd)之間的異同點和優缺點。 6) vs c++中多線程下的debug技巧
之所以稱為了解,而不是掌握,是因為這些技術偏向于底層的系統編程,并不是一撮而就的,需要一定的時間和經歷才能沉淀下來的。因此只能說是讓大家了解。
為了簡單期間,一些限制條件:
僅關注windows下的實現 僅重點分析libuv的通用cpu密集型計算的框架,了解cpu密集計算框架,實際上異步io就比較容易理解了 因為windows下,異步io通過IOCP(完成端口),會使代碼實現非常簡單,而效率非常高。
在寫此篇blog時,突然感覺到,是不是應該將libuv庫進行拆分,將核心部分代碼抽離出來單獨運行,這樣的好處就是要分析的代碼量要少很多,僅關注我們感興趣的代碼,有利于演示。
還有一個目的:
就是在簡化精華版的基礎上升級一下,看看是否能夠實現任務的動態均衡(ms PPL庫和intel TBB庫都有動態均衡,Work Stealing的功能,libuv目前確定沒有此功能,libdispatch目前沒發現,源碼正在閱讀中,還沒看到)。這個實現難度還是很大的,就當練練手吧,哈哈
我嘗試一下吧,看看是否可行!
(本文寫于2016年,經過這段時間研究,于2017-2-10放棄上面的拆分這個想法,實在是牽涉到的代碼以及操作系統太多,還是果斷放棄這個念想吧!)
前段時間,完成了一個微信項目,在后臺選型過程中,花了將近一個月考察了java,php幾個庫,但最終卻選擇了nodejs,原因很簡單:
1、java的配置實在是太麻煩了 2、php實在不熟悉,特別扭 3、nodejs使用js編程,我本人還是比較熟悉js的基礎部分(不算es6.0 es7.0標準),并且npm真好用,實在是資源太豐富了,有時選擇太多也是一種痛苦啊! 4、nodejs基于異步io技術,效率非常高。而且分布式部署簡單。
通過無數次的比較,最終選定了令我非常滿意的組合,感覺最起碼少寫了80%的代碼。
framework: strongloop/loopback(被IBM收購了,只能說強大無比)
樸靈的: wechat / wechat-api / wechat-oauth
supersheep: wechat-pay
數據庫: mongodb用于不需要事務處理的表 mysql用于支付的事務處理
因為沒經驗,在支付時需要事務處理,所以調整為使用mysql。不過從另外一個角度說明loopback的強大,支持多數據源的統一api操作。
angularjs1.x
jquery
總體而言,該前后臺配套系統的開發非常令人愉悅。前臺不好統計,但是微信后臺,至少少寫了80%代碼!!
目前基于異步回調的開發非常盛行,例如ios中的gcd,android中的基于接口回調方式。如果有過這些開發經驗,你會覺得nodejs的異步事件開發模型還是蠻熟悉的。
隨著逐漸熟悉nodejs,深深的喜歡上了nodejs,因此更想深入的了解一下nodejs是如何實現的。
通過了解nodejs的代碼結構,結合深入淺出nodejs第三章的異步io所示流程圖,逐步驗證其正確性。特別是看到libuv中具有深厚c語言功底的老鳥所寫的代碼,忍不住想與大家分享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) 跨平臺:windows linux unix都支持 2) 異步io:windows IOCP 、linux epoll、unix kqueue 3) 主要目的是用于nodejs,實際還有很多庫或程序使用了libuv 例如目前風頭正勁的跨平臺.NetCore庫(就是曾經大名鼎鼎的微軟的asp.net)也是使用libuv作為核心 4) libuv不僅僅支持異步io操作,而且還具有一個強勁的線程池,用于支持多線程并行的cpu密集型操作。 這個才是我們重點要分析的模塊
1) nodejs主要由google v8 javascript引擎和libuv組成 2) v8引擎綁定libuv實現的api,因此,既能使用ecma js標準語言來執行js代碼,又能通過js調用libuv相關接口。 3) 由此可見,libuv本身是獨立的c語言庫,既可以直接使用c/c++來調用,也可以被綁定到c#(.NetCore)或者其他任何語言,例如java ,lua...... c/c++實現的庫最大的好處就是能被各種其他編程語言所綁定和調用。 因為其他各種編程語言基本都是用c/c++來實現的,都留有接口與c/c++互調。
借用深入淺出nodejs(經作者同意)中的兩張圖來了解整個流程:
一定要明確的了解哪些事情是發生在主線程的,哪些事情是發生在線程池中的!!!
nodejs就是數據通過v8引擎(主線程:數據輸入)傳遞給Libuv進行處理(線程池:數據處理—根據數據類型不同,io數據由IOCP[windows]線程池處理,通用計算則由自己實現的線程池處理),libuv處理好后通知v8引擎我已經完成了,你來進行完成處理(主線程:完成回調,信息輸出)。
其實從上面的敘述可以了解到以下幾點:
1) v8 javascript引擎是單線程的,數據的輸入,信息的輸出(完成回調)都是在主線程中處理。這一點以后在源碼分析中我們可以驗證,通過vs強大的debug功能,我們可以很清晰的看到具體代碼到底是運行在哪個線程中。
2) 但是數據處理模塊(libuv)不是單線程的,它根據數據請求類型是否是io請求(socket,文件讀寫或管道等)還是work請求(非io請求)。不同的請求使用不同的處理策略。例如io請求,在windows下用IOCP,在linux下用epool。而work請求,windows和linux下都是使用統一的,自己實現的線程池。而我們的源碼分析就是要證明上面的描述。
5、visual studio編譯及測試Libuv庫:
1) 如何編譯libuv庫 2) 在測試libuv庫的時候,如何解決各種鏈接錯誤 3) 重點是根據鏈接錯誤編號,通過msdn來定位問題、分析問題以及解決問題,掌握方法學是關鍵
編譯:
1) 安裝python2.,6或者2.7版本,并設置好環境變量。千萬別安裝3.0或以上版本。
目前很多跨平臺庫,都通過python腳本進行編譯或引導,因此python是必裝程序。、
2) 在cmd中:
cd到你libuv所在目錄
并輸入 git clone https://chromium.googlesource.com/external/gyp.git build/gyp
將google gyp系統克隆到libuv所在目錄的build/gyp文件夾下面
由于GFW的關系,google網站無法訪問,作為程序員,我想你應該有辦法、
3) 運行libuv所在目錄下的vcbuild.bat,生成visual studio解決方案。雙擊運行,然后F5編譯調試,一切盡在掌握中!
如果你目前可以登陸google網站,則直接運行vcbuild.bat,如果沒安裝過gyp的話,自動會下載gyp構建系統。因此可以省略第二步
因為Libuv在windows中編譯后的結果是一個靜態鏈接庫,那么我們需要重新建個exe工程,并將libuv.lib鏈接入exe這個程序,步驟如下:
1) 新建一個win32/控制臺/空項目,名稱例如:LibuvTest
2) 在源文件中添加main.cpp文件,實現以下簡單代碼并運行,F5,調試運行
3) 如果運行時報無法啟動程序,則將exe設置為啟動項:
4) 使用uv_wort_t進行cpu密集計算的測試代碼,F5編譯調試
F5運行后出現鏈接錯誤
5) LNK2019鏈接錯誤,表示相關的Lib沒有被引入,觀察相關錯誤內容,可以確定Libuv.lib沒有被導入。
1、查找到Libuv.lib被編譯到的目錄
2、在LibuvTest項目上右擊鼠標,選擇屬性菜單,彈出libuvTest Property Pages,然后選擇Linker/input/Additional Dependencies界面
3、相對路徑方式,添加libuv.lib庫
參考
6) 添加好libuv.lib后,繼續F5調試,仍舊有大量鏈接錯誤:
7) 解決LNK4098錯誤,該錯誤是由于LIBCMTD庫引起的,我們忽略該庫,具體如下:
8) 解決LNK2019錯誤,該錯誤前面也提到過,是由于沒有引入相應的lib導致的。
1、查看具體的鏈接錯誤描述,可以看到和socket相關的
2、msdn是法寶,在msdn中查找closesocket函數,看看closesocket屬于哪個lib庫的?
3、按照前面添加libuv.lib方式(),添加Ws2_32.lib。或者也可以參考該文檔使用另外一種方式:#pragma comment(lib,”path”)方式進行鏈接。
9) 周而復始,不斷使用8)的方式,解決所有LNK2019鏈接錯誤
10) 最終需要的所有鏈接庫
11) F5繼續編譯調試,正確運行輸出結果
12) 至此為止,libuv的uv_work_t的Demo能夠編譯并且正確運行。我們也可以休息一下了! 可能太基礎了,但是也是體現了解決問題的方法,一并記錄下來,供大家參考。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。