用C/C++來實現 Node.js 的模組(一),node.js模組

來源:互聯網
上載者:User

用C/C++來實現 Node.js 的模組(一),node.js模組

 N久之前的一個坑——用 Node.js 來重構 NBUT 的 Online Judge,包括評測端也得重構一遍。(至於什麼時候完成大家就不要關心了,(/‵Д′)/~ ╧╧

  總之我們現在要做的其實簡而言之就是——用C/C++來實現 Node.js 的模組。

準備工作

  工欲善其事,必先~~耍流氓~~利其器。

node-gyp

  首先你需要一個 node-gyp 模組。

  在任意角落,執行:
複製代碼 代碼如下:
$ npm install node-gyp -g

   在進行一系列的 blahblah 之後,你就安裝好了。

Python

  然後你需要有個 python 環境。

  自己去官網搞一個來。


注意: 根據 node-gyp 的GitHub顯示,請務必保證你的 python 版本介於 2.5.0 和 3.0.0 之間。
 
編譯環境

  嘛嘛,我就偷懶點不細寫了,還請自己移步到 node-gyp 去看編譯器的需求。並且倒騰好。

入門

  我就拿官網的入門 Hello World說事兒了。

Hello World

  請準備一個 C++ 檔案,比如就叫 ~~sb.cc~~ hello.cc。

  然後我們一步步來,先往裡面搞出標頭檔和定義好命名空間:
複製代碼 代碼如下:
#include <node.h>
#include <v8.h>
using namespace v8;

 主要函數

  接下去我們寫一個函數,其傳回值是 Handle<Value>。
複製代碼 代碼如下:
Handle<Value> Hello(const Arguments& args)
{
    //... 嗷嗷待寫
}

   然後我來粗粗解析一下這些東西:

Handle<Value>

  做人要有節操,我事先申明我是從這裡(@fool)參考的。


V8 裡使用 Handle 類型來託管 JavaScript 對象,與 C++ 的 std::sharedpointer 類似,Handle 類型間的賦值均是直接傳遞對象引用,但不同的是,V8 使用自己的 GC 來管理對象生命週期,而不是智能指標常用的引用計數。

JavaScript 類型在 C++ 中均有對應的自訂類型,如 String 、 Integer 、 Object 、 Date 、 Array 等,嚴格遵守在 JavaScript 中的繼承關係。 C++ 中使用這些類型時,必須使用 Handle 託管,以使用 GC 來管理它們的生命週期,而不使用原生棧和堆。
 
  而這個所謂的 Value ,從 V8 引擎的標頭檔 v8.h 中的各種繼承關係中可以看出來,其實就是 JavaScript 中各種對象的基類。

  在瞭解了這件事之後,我們大致能明白上面那段函數的申明的意思就是說,我們寫一個 Hello 函數,其返回的是一個不定類型的值。


注意: 我們只能返回特定的類型,即在 Handle 託管下的 String 啊 Integer 啊等等等等。
 
Arguments

  這個就是傳入這個函數的參數了。我們都知道在 Node.js 中,參數個數是亂來的。而這些參數傳進去到 C++ 中的時候,就轉變成了這個 Arguments 類型的對象了。

  具體的用法我們在後面再說,在這裡只需要明白這個是個什麼東西就好。(為毛要賣關子?因為 Node.js 官方文檔中的例子就是分開來講的,我現在只是講第一個 Hello World 的例子而已( ´థ౪థ)σ

添磚加瓦

  接下去我們就開始添磚加瓦了。就最簡單的兩句話:
複製代碼 代碼如下:
Handle<Value> Hello(const Arguments& args)
{
    HandleScope scope;
    return scope.Close(String::New("world"));
}

   這兩句話是什麼意思呢?大致的意思就是返回一個 Node.js 中的字串 "world"。

HandleScope

  同參考自這裡。


Handle 的生命週期和 C++ 智能指標不同,並不是在 C++ 語義的 scope 內生存(即{} 包圍的部分),而需要通過 HandleScope 手動指定。HandleScope 只能分配在棧上,HandleScope 對象聲明後,其後建立的 Handle 都由 HandleScope 來管理生命週期,HandleScope 對象析構後,其管理的 Handle 將由 GC 判斷是否回收。
 
  所以呢,我們得在需要管理他的生命週期的時候申明這個 Scope 。好的,那麼為什麼我們的代碼不這麼寫呢?
複製代碼 代碼如下:
Handle<Value> Hello(const Arguments& args)
{
    HandleScope scope;
    return String::New("world");
}

   因為當函數返回時,scope 會被析構,其管理的Handle也都將被回收,所以這個 String 就會變得沒有意義。

  所以呢 V8 就想出了個神奇的點子——HandleScope::Close(Handle<T> Value) 函數!這個函數的用處就是關閉這個 Scope 並且把裡面的參數轉交給上一個 Scope 管理,也就是進入這個函數前的 Scope。

  於是就有了我們之前的代碼 scope.Close(String::New("world"));。

String::New

  這個 String 類所對應的就是 Node.js 中原生的字串類。繼承自 Value 類。與此類似,還有:

 •Array
•Integer
•Boolean
•Object
•Date
•Number
•Function
•...
 
  這些東西有些是繼承自 Value,有些是二次繼承。我們這裡就不多做研究,自己可以看看 V8 的代碼(至少是標頭檔)研究研究或者看看這個手冊。

  而這個 New 呢?這裡可以看的。就是建立一個 String 對象。

  至此,這個主要函數我們就解析完畢了。

匯出對象

  我們來溫習一下,如果是在 Node.js 裡面寫的話,我們怎麼匯出函數或者對象什麼的呢?
複製代碼 代碼如下:
exports.hello = function() {}

   那麼,在 C++ 中我們該如何做到這一步呢?

初始化函數

  首先,我們寫個初始化函數:
複製代碼 代碼如下:
void init(Handle<Object> exports)
{
    //... 嗷嗷待寫你妹啊!#゚Å゚)⊂彡☆))゚Д゚)・∵
}

   這是龜腚!函數名什麼的無所謂,但是傳入的參數一定是一個 Handle<Object>,代表我們下面將要在這貨上匯出東西。

  然後,我們就在這裡面寫上匯出的東西了:
複製代碼 代碼如下:
void init(Handle<Object> exports)
{
    exports->Set(String::NewSymbol("hello"),
        FunctionTemplate::New(Hello)->GetFunction());
}

   大致的意思就是說,為這個 exports 對象添加一個欄位叫 hello,所對應的東西是一個函數,而這個函數就是我們親愛的 Hello 函數了。

  用虛擬碼寫直白點就是:
複製代碼 代碼如下:
void init(Handle<Object> exports)
{
    exports.Set("hello", function hello);
}

   大功告成!

  (大功告成你妹啊!閉嘴( ‘д‘⊂彡☆))Д´)

真·匯出

  這才是最後一步,我們最後要申明,這個就是匯出的入口,所以我們在代碼的末尾加上這一行:
NODE_MODULE(hello, init)

   納了個尼?!這又是什麼東西?

  別著急,這個 NODE_MODULE 是一個宏,它的意思呢就是說我們採用 init 這個初始化函數來把要匯出的東西匯出到 hello 中。那麼這個 hello 哪來呢?

  它來自檔案名稱!對,沒錯,它來自檔案名稱。你並不需要事先申明它,你也不必擔心不能用,總之你的這個最終編譯好的二進位檔案名叫什麼,這裡的 hello 你就填什麼,當然要除去尾碼名了。

  詳見官方文檔。


Note that all Node addons must export an initialization function:
複製代碼 代碼如下:
void Initialize (Handle<Object> exports);
NODE_MODULE(module_name, Initialize)

 There is no semi-colon after NODE_MODULE as it's not a function (see node.h).

The module_name needs to match the filename of the final binary (minus the .node suffix).
 
編譯 (๑•́ ₃ •̀๑)

  來吧,讓我們一起編譯吧!

  我們再建立一個類似於 Makefile 的歸檔檔案吧——binding.gyp。

  並且在裡面添加這樣的代碼:
複製代碼 代碼如下:
{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}

   為什麼這麼寫呢?可以參考 node-gyp 的官方文檔。

configure

  在檔案搞好之後,我們要在這個目錄下面執行這個命令了:
複製代碼 代碼如下:
$ node-gyp configure

   如果一切正常的話,應該會產生一個 build 的目錄,然后里面有相關檔案,也許是 M$ Visual Studio 的 vcxproj 檔案等,也許是 Makefile ,視平台而定。

build

  Makefile 也產生好之後,我們就開始構造編譯了:
$ node-gyp build

   等到一切編譯完成,才算是真正的大功告成了!不信你去看看 build/Release 目錄,下面是不是有一個 hello.node 檔案了?沒錯,這個就是 C++ 等下要給 Node.js 撿的肥皂!

搞基吧!Node ヽ(✿゚▽゚)ノ C++

  我們在剛才那個目錄下建立一個檔案 jianfeizao.js:
複製代碼 代碼如下:
var addon = require("./build/Release/hello");
console.log(addon.hello());

   看到沒!看到沒!出來了出來了!Node.js 和 C++ 搞基的結果!這個 addon.hello() 就是我們之前在 C++ 代碼中寫的 Handle<Value> Hello(const Arguments& args) 了,我們現在就已經把它返回的值給輸出了。

洗洗睡吧,下節更深入

  時間不早了,今天就寫到這裡了,至此為止大家都能搞出最基礎的 Hello world 的 C++ 擴充了吧。下一次寫的應該會更深入一點,至於下一次是什麼時候,我也不知道啦其實。
   (喂喂喂,擼主怎麼可以這麼不負責!(o゚ロ゚)┌┛Σ(ノ´ω`)ノ


開發一個網站用php還是nodejs?

我不覺得node.js有什麼不適合新手的,畢竟你從0開始學PHP一樣要對網頁編程有個學習的過程,所以從上手程度來說兩者幾乎等價。一樣要瞭解MVC,資料庫連接等等。
直接比較一下上手時的優劣,你可以自己衡量一下
Node.js
優點:js文法,對會用js的人來說看懂代碼不難。

簡化了設定管理員的過程,內建一個web伺服器,不像php要安裝配置apache
缺點:相比PHP學習資料較少
PHP
優點:學習資料多,架構多,工具包多
缺點:對於文法有個熟悉過程
我覺得這應該是你搭的第一個網站,語言的重要性其實不那麼大,優缺點可能在你學習的過程中也不會體現,而且都是一些外在外因。當然你也可以這麼考慮,現在搞PHP的一抓一大把,而且主要以輕量級公司用的居多。node被很多大公司所推崇,越早接觸機會機會越多。
 
Node是什以及對什有好處?-淺析Nodejs:一個“編碼就緒”伺服器-IT168

實際上,每秒幾乎有數千條 tweets 達到,資料庫不可能及時處理高峰時段需要的寫入數量。Node成為這個問題的解決方案的重要一環。如您所見,Node能處理數萬條入站tweets。它能迅速輕鬆地將它們寫入一個記憶體排隊機制(例如 memcached),另一個單獨進程可以從那裡將它們寫入資料庫。Node在這裡的角色是迅速收集tweet並將這個資訊傳遞給另一個負責寫入的進程。想象一下另一種設計 — 一個常規 PHP 伺服器自己試圖處理對資料庫的寫入 — 每個tweet將在寫入資料庫時導致一個短暫的延遲,這是因為資料庫調用正在阻塞通道。由於資料庫延遲,一台這樣設計的機器每秒可能只能處理2000條入站tweets。每秒100萬條tweets需要500個伺服器。相反,Node能處理每個串連而不會阻塞通道,從而能捕獲儘可能多的tweets。一個能處理50,000 條tweets的Node機器只需要20個伺服器。 映像檔案伺服器 一個擁有大型分布式網站的公司(比如 Facebook 或 Flickr)可能會決定將所有機器只用於服務映像。Node將是這個問題的一個不錯的解決方案,因為該公司能使用它編寫一個簡單的檔案檢索器,然後處理數萬條串連。Node將尋找映像檔案,返迴文件或一個404錯誤,然後什麼也不用做。這種設定將允許這類分布式網站減少它們服務映像、.js和 .css檔案等靜態檔案所需的伺服器數量。 它對什麼有壞處? 當然,在某些情況下,Node並非理想選擇。下面是Node不擅長的領域: 動態建立的頁 目前,Node沒有提供一種預設方法來建立動態網頁。例如,使用JavaServer Pages (JSP) 技術時,可以建立一個在 這樣的JSP程式碼片段中包含迴圈的index.jsp 頁。Node不支援這類動態、HTML驅動的頁面。同樣,Node不太適合作為Apache和Tomcat這樣的網頁伺服器。因此,如果您想在Node中提供這樣一個伺服器端解決方案,必須自己編寫整個解決方案。PHP程式員不想在每次部署web應用程式時都編寫一個針對Apache的PHP轉換器,當目前為止,這正是Node要求您做的。 關聯式資料庫重型應用程式 Node的目的是快速、非同步和非阻塞。資料庫並不一定分享這些目標。它們是同步和阻塞的,因為讀寫時對資料庫的調用在結果產生之前將一直阻塞通道。因此,一個每個請求都需要大量資料庫調用、大量讀取、大量寫入的web應用程式非常不適合Node,這是因為關聯式資料庫本身就能抵銷Node的眾多優勢。(新的NoSQL資料庫更適合Node,不過那完全是另一個主題了。) 結語 問題是“什麼是Node.js?” 應該已經得到解答。閱讀本文之後,您應該能通過幾個清晰簡潔的句子回答這個問題。如果這樣,那麼您已經走到了許多編碼員和程式員的前面。我和許多人都談論過Node,但它們對 Node究竟是什麼一直很迷惑。可以理解,他們具有的是Apache的思維方式 — 伺服器是一個應用程式,將HTML檔案放入其中,一切就會正常運轉。而Node是目的驅動的。它是一個軟體程式,使用JavaScript來允許程式員輕鬆快速地建立快速、可伸縮的web伺服器。Apache是運行就緒的,而ode是編碼就緒的。 Node完成了它提供高度可伸縮伺服器的目標。它並不分配一個 “每個串連一個線程” 模型,而是使用一個 “每個串連一個流程” 模型,只建立每個串連需要的記憶體。它使用Google的一個非常快速的JavaScript引擎:V8引擎。它使用一個事件驅動設計來保持代碼最小且易於閱讀。所有這些因素促成了......餘下全文>>
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.