1. 程式人生 > >Nodejs的運行原理-函數回調篇

Nodejs的運行原理-函數回調篇

csharp listen 客戶端 gpo 結束 war hand 之間 數據流

前言

當客戶端向http server 發起TCP鏈接時,server端會發起一系列的callback調用,這是一個逆向調用的過程;開始於libuv,終止於js代碼裏的callback(promise then)函數。

如下圖所示,http server 正向調用過程,實際大部分的時間花在net.js上,直到最下面的紅框,才調用了關鍵函數createTCP()

function createTCP() {
   //綁定tcp_wrap模塊,調用tcp constructor。   
    var TCP = process.binding(‘tcp_wrap‘).TCP;
    return new TCP();
}

技術分享圖片

tcp_wrap模塊

我們看一下tcpwrap::initialize()的代碼:

          void TCPWrap::Initialize(Handle<Object> target, Handle<Value> unused, Handle<Context> context) {
	  Environment* env = Environment::GetCurrent(context);
	
	  Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate(), New);
	  t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP"));
	  t->InstanceTemplate()->SetInternalFieldCount(1);
	  // Init properties
	  t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "reading"),Boolean::New(env->isolate(), false));
	  t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "owner"),Null(env->isolate()));
	  t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "onread"),Null(env->isolate()));
	  t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(),"onconnection"),Null(env->isolate()));
	
	  NODE_SET_PROTOTYPE_METHOD(t, "open", Open);
	  NODE_SET_PROTOTYPE_METHOD(t, "bind", Bind);
	  NODE_SET_PROTOTYPE_METHOD(t, "listen", Listen);
	  NODE_SET_PROTOTYPE_METHOD(t, "getsockname", GetSockName);
	
	  target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP"), t->GetFunction());
	  }
	
	NODE_MODULE_CONTEXT_AWARE_BUILTIN(tcp_wrap, node::TCPWrap::Initialize)

利用v8 engine的 functionTemplate 創建一個js類。

類的constructor 是 TCP(),

類的prototype是NODE_SET_PROTOTYPE_METHOD()

類的屬性是t->InstanceTemplate()->Set().

在執行函數createTCP()調用process.binding(‘tcp_wrap‘)時,其實會調用到下圖。在紅框內,可以看到參數target被設置成了export對象,也就是說,tcp_wrap模塊真正導出的是TCP函數

技術分享圖片

Server.prototype._listen2

技術分享圖片

結合上圖,我們可以看到函數_listen2中調用createServerHandle() 返回了一個_handle對象

,並且self._handle.onconnection = onconnection,這一步也非常重要。

AsyncWrap準備工作

AsyncWrap 和Env的代碼截圖:

技術分享圖片

準備工作及流程:

技術分享圖片

callBack的逆向調用

前面都是鋪墊,這裏才是正題。

正向調用過程,從createServer()開始,到listen()結束,為了創建一個基於TCP的http server。了解socket流程的都知道,到此為止,創建工作實際已經完成,剩下的就是等待客戶connect。

而所有的callback執行的目的是對應用程序構造出一個socket的對象,並且基於此對象完成面向連接的數據流讀取操作。

下圖為調用流程:

技術分享圖片

First callback

TCP::Listen()通過libuv提供的uv_listen()實現了listen異步調用,並且指定了callback回調函數TCPWrap::OnConnection()。

OnConnection()由libuv的event loop調用。

在看Nodejs Env.h 和 Env-inl.h 中可找到PER_ISOLATE_STRING_PROPERTIES(v)若幹引用,就像上面圖所示,env->onconnection_string()會返回symbole onconnection

MakeCall中,通過object()->Get()獲取symbole的對應函數。

Other callback

net.js中的onconnection()會被調用,如下圖所示:

技術分享圖片

兩個要點,一是創建了socket對象,二是發出了connection時間。

開發者調用createServer()時,其實是在執行new Server(),而類Server中對connection之間有一個監聽者,那就是connectionListener(),也就是第三個callback。

通過前文,我想後面的事情就不在贅述了。

Nodejs的運行原理-函數回調篇