国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

讓請(qǐng)求處理程序作出響應(yīng)

2018-02-24 15:25 更新

很好。不過現(xiàn)在要是請(qǐng)求處理程序能夠向?yàn)g覽器返回一些有意義的信息而并非全是“Hello World”,那就更好了。

這里要記住的是,瀏覽器發(fā)出請(qǐng)求后獲得并顯示的“Hello World”信息仍是來自于我們_server.js_文件中的_onRequest_函數(shù)。

其實(shí)“處理請(qǐng)求”說白了就是“對(duì)請(qǐng)求作出響應(yīng)”,因此,我們需要讓請(qǐng)求處理程序能夠像_onRequest_函數(shù)那樣可以和瀏覽器進(jìn)行“對(duì)話”。

不好的實(shí)現(xiàn)方式

對(duì)于我們這樣擁有PHP或者Ruby技術(shù)背景的開發(fā)者來說,最直截了當(dāng)?shù)膶?shí)現(xiàn)方式事實(shí)上并不是非常靠譜: 看似有效,實(shí)則未必如此。

這里我指的“直截了當(dāng)?shù)膶?shí)現(xiàn)方式”意思是:讓請(qǐng)求處理程序通過_onRequest_函數(shù)直接返回(return())他們要展示給用戶的信息。

我們先就這樣去實(shí)現(xiàn),然后再來看為什么這不是一種很好的實(shí)現(xiàn)方式。

讓我們從讓請(qǐng)求處理程序返回需要在瀏覽器中顯示的信息開始。我們需要將_requestHandler.js_修改為如下形式:

function start()  {
    console.log("Request handler 'start' was called.");? 
    return  "Hello Start";
}

function upload()  {
    console.log("Request handler 'upload' was called.");? 
    return  "Hello Upload";
}

exports.start = start;
exports.upload = upload;

好的。同樣的,請(qǐng)求路由需要將請(qǐng)求處理程序返回給它的信息返回給服務(wù)器。因此,我們需要將_router.js_修改為如下形式:

function route(handle, pathname)  {
? console.log("About to route a request for "  + pathname);? 
    if  (typeof handle[pathname]  ===  'function')  {? ? 
        return handle[pathname]();? }  
    else  {
? ?     console.log("No request handler found for "  + pathname);? ? 
        return  "404 Not found";? 
    }
}

exports.route = route;

正如上述代碼所示,當(dāng)請(qǐng)求無法路由的時(shí)候,我們也返回了一些相關(guān)的錯(cuò)誤信息。

最后,我們需要對(duì)我們的_server.js_進(jìn)行重構(gòu)以使得它能夠?qū)⒄?qǐng)求處理程序通過請(qǐng)求路由返回的內(nèi)容響應(yīng)給瀏覽器,如下所示:

var http = require("http");
var url = require("url");

function start(route, handle)  {? 
    function onRequest(request, response)  {? ? 
        var pathname = url.parse(request.url).pathname;
    ? ? console.log("Request for "  + pathname +  " received.");

    ? ? response.writeHead(200,  {"Content-Type":  "text/plain"});? ? 
        var content = route(handle, pathname)
    ? ? response.write(content);
    ? ? response.end();? 
    }

? http.createServer(onRequest).listen(8888);
? console.log("Server has started.");
}

exports.start = start;

如果我們運(yùn)行重構(gòu)后的應(yīng)用,一切都會(huì)工作的很好:請(qǐng)求http://localhost:8888/start,瀏覽器會(huì)輸出“Hello Start”,請(qǐng)求http://localhost:8888/upload會(huì)輸出“Hello Upload”,而請(qǐng)求http://localhost:8888/foo?會(huì)輸出“404 Not found”。

好,那么問題在哪里呢?簡(jiǎn)單的說就是: 當(dāng)未來有請(qǐng)求處理程序需要進(jìn)行非阻塞的操作的時(shí)候,我們的應(yīng)用就“掛”了。

沒理解?沒關(guān)系,下面就來詳細(xì)解釋下。

阻塞與非阻塞

正如此前所提到的,當(dāng)在請(qǐng)求處理程序中包括非阻塞操作時(shí)就會(huì)出問題。但是,在說這之前,我們先來看看什么是阻塞操作。

我不想去解釋“阻塞”和“非阻塞”的具體含義,我們直接來看,當(dāng)在請(qǐng)求處理程序中加入阻塞操作時(shí)會(huì)發(fā)生什么。

這里,我們來修改下_start_請(qǐng)求處理程序,我們讓它等待10秒以后再返回“Hello Start”。因?yàn)?,JavaScript中沒有類似_sleep()_這樣的操作,所以這里只能夠來點(diǎn)小Hack來模擬實(shí)現(xiàn)。

讓我們將_requestHandlers.js_修改成如下形式:

function start()  {
? console.log("Request handler 'start' was called.");? 
    function sleep(milliSeconds)  {? ? 
        var startTime =  new  Date().getTime();? ? 
        while  (new  Date().getTime()   startTime + milliSeconds);? 
    }

    sleep(10000);? 
    return  "Hello Start";
}

function upload()  {
    console.log("Request handler 'upload' was called.");? 
    return  "Hello Upload";
}

exports.start = start;
exports.upload = upload;

上述代碼中,當(dāng)函數(shù)_start()_被調(diào)用的時(shí)候,Node.js會(huì)先等待10秒,之后才會(huì)返回“Hello Start”。當(dāng)調(diào)用_upload()_的時(shí)候,會(huì)和此前一樣立即返回。

(當(dāng)然了,這里只是模擬休眠10秒,實(shí)際場(chǎng)景中,這樣的阻塞操作有很多,比方說一些長(zhǎng)時(shí)間的計(jì)算操作等。)

接下來就讓我們來看看,我們的改動(dòng)帶來了哪些變化。

如往常一樣,我們先要重啟下服務(wù)器。為了看到效果,我們要進(jìn)行一些相對(duì)復(fù)雜的操作(跟著我一起做): 首先,打開兩個(gè)瀏覽器窗口或者標(biāo)簽頁(yè)。在第一個(gè)瀏覽器窗口的地址欄中輸入http://localhost:8888/start, 但是先不要打開它!

在第二個(gè)瀏覽器窗口的地址欄中輸入http://localhost:8888/upload, 同樣的,先不要打開它!

接下來,做如下操作:在第一個(gè)窗口中(“/start”)按下回車,然后快速切換到第二個(gè)窗口中(“/upload”)按下回車。

注意,發(fā)生了什么: /start URL加載花了10秒,這和我們預(yù)期的一樣。但是,/upload URL居然_也_花了10秒,而它在對(duì)應(yīng)的請(qǐng)求處理程序中并沒有類似于_sleep()_這樣的操作!

這到底是為什么呢?原因就是_start()_包含了阻塞操作。形象的說就是“它阻塞了所有其他的處理工作”。

這顯然是個(gè)問題,因?yàn)镹ode一向是這樣來標(biāo)榜自己的:“在node中除了代碼,所有一切都是并行執(zhí)行的”。

這句話的意思是說,Node.js可以在不新增額外線程的情況下,依然可以對(duì)任務(wù)進(jìn)行并行處理 —— Node.js是單線程的。它通過事件輪詢(event loop)來實(shí)現(xiàn)并行操作,對(duì)此,我們應(yīng)該要充分利用這一點(diǎn) —— 盡可能的避免阻塞操作,取而代之,多使用非阻塞操作。

然而,要用非阻塞操作,我們需要使用回調(diào),通過將函數(shù)作為參數(shù)傳遞給其他需要花時(shí)間做處理的函數(shù)(比方說,休眠10秒,或者查詢數(shù)據(jù)庫(kù),又或者是進(jìn)行大量的計(jì)算)。

對(duì)于Node.js來說,它是這樣處理的:“嘿,probablyExpensiveFunction()(譯者注:這里指的就是需要花時(shí)間處理的函數(shù)),你繼續(xù)處理你的事情,我(Node.js線程)先不等你了,我繼續(xù)去處理你后面的代碼,請(qǐng)你提供一個(gè)callbackFunction(),等你處理完之后我會(huì)去調(diào)用該回調(diào)函數(shù)的,謝謝!”

(如果想要了解更多關(guān)于事件輪詢細(xì)節(jié),可以閱讀Mixu的博文——理解node.js的事件輪詢。)

接下來,我們會(huì)介紹一種錯(cuò)誤的使用非阻塞操作的方式。

和上次一樣,我們通過修改我們的應(yīng)用來暴露問題。

這次我們還是拿_start_請(qǐng)求處理程序來“開刀”。將其修改成如下形式:

var exec = require("child_process").exec;

function start()  {
? console.log("Request handler 'start' was called.");? 
    var content =  "empty";

    exec("ls -lah",  
        function  (error, stdout, stderr)  {? ? 
        content = stdout;? });? 
        return content;
    }

function upload()  {
    console.log("Request handler 'upload' was called.");? 
    return  "Hello Upload";
}

exports.start = start;
exports.upload = upload;

上述代碼中,我們引入了一個(gè)新的Node.js模塊,_childprocess。之所以用它,是為了實(shí)現(xiàn)一個(gè)既簡(jiǎn)單又實(shí)用的非阻塞操作:exec()

_exec()做了什么呢?它從Node.js來執(zhí)行一個(gè)shell命令。在上述例子中,我們用它來獲取當(dāng)前目錄下所有的文件(“l(fā)s -lah”),然后,當(dāng)/start_URL請(qǐng)求的時(shí)候?qū)⑽募畔⑤敵龅綖g覽器中。

上述代碼是非常直觀的: 創(chuàng)建了一個(gè)新的變量content(初始值為“empty”),執(zhí)行“l(fā)s -lah”命令,將結(jié)果賦值給content,最后將content返回。

和往常一樣,我們啟動(dòng)服務(wù)器,然后訪問“http://localhost:8888/start” 。

之后會(huì)載入一個(gè)漂亮的web頁(yè)面,其內(nèi)容為“empty”。怎么回事?

這個(gè)時(shí)候,你可能大致已經(jīng)猜到了,_exec()_在非阻塞這塊發(fā)揮了神奇的功效。它其實(shí)是個(gè)很好的東西,有了它,我們可以執(zhí)行非常耗時(shí)的shell操作而無需迫使我們的應(yīng)用停下來等待該操作。

(如果想要證明這一點(diǎn),可以將“l(fā)s -lah”換成比如“find /”這樣更耗時(shí)的操作來效果)。

然而,針對(duì)瀏覽器顯示的結(jié)果來看,我們并不滿意我們的非阻塞操作,對(duì)吧?

好,接下來,我們來修正這個(gè)問題。在這過程中,讓我們先來看看為什么當(dāng)前的這種方式不起作用。

問題就在于,為了進(jìn)行非阻塞工作,_exec()_使用了回調(diào)函數(shù)。

在我們的例子中,該回調(diào)函數(shù)就是作為第二個(gè)參數(shù)傳遞給_exec()_的匿名函數(shù):

function  (error, stdout, stderr)  {? 
    content = stdout;
}

現(xiàn)在就到了問題根源所在了:我們的代碼是同步執(zhí)行的,這就意味著在調(diào)用_exec()_之后,Node.js會(huì)立即執(zhí)行?return content?;在這個(gè)時(shí)候,_content_仍然是“empty”,因?yàn)閭鬟f給_exec()_的回調(diào)函數(shù)還未執(zhí)行到——因?yàn)開exec()_的操作是異步的。

我們這里“l(fā)s -lah”的操作其實(shí)是非??斓模ǔ钱?dāng)前目錄下有上百萬(wàn)個(gè)文件)。這也是為什么回調(diào)函數(shù)也會(huì)很快的執(zhí)行到 —— 不過,不管怎么說它還是異步的。

為了讓效果更加明顯,我們想象一個(gè)更耗時(shí)的命令: “find /”,它在我機(jī)器上需要執(zhí)行1分鐘左右的時(shí)間,然而,盡管在請(qǐng)求處理程序中,我把“l(fā)s -lah”換成“find /”,當(dāng)打開/start URL的時(shí)候,依然能夠立即獲得HTTP響應(yīng) —— 很明顯,當(dāng)_exec()_在后臺(tái)執(zhí)行的時(shí)候,Node.js自身會(huì)繼續(xù)執(zhí)行后面的代碼。并且我們這里假設(shè)傳遞給_exec()_的回調(diào)函數(shù),只會(huì)在“find /”命令執(zhí)行完成之后才會(huì)被調(diào)用。

那究竟我們要如何才能實(shí)現(xiàn)將當(dāng)前目錄下的文件列表顯示給用戶呢?

好,了解了這種不好的實(shí)現(xiàn)方式之后,我們接下來來介紹如何以正確的方式讓請(qǐng)求處理程序?qū)g覽器請(qǐng)求作出響應(yīng)。

以非阻塞操作進(jìn)行請(qǐng)求響應(yīng)

我剛剛提到了這樣一個(gè)短語(yǔ) —— “正確的方式”。而事實(shí)上通?!罢_的方式”一般都不簡(jiǎn)單。

不過,用Node.js就有這樣一種實(shí)現(xiàn)方案: 函數(shù)傳遞。下面就讓我們來具體看看如何實(shí)現(xiàn)。

到目前為止,我們的應(yīng)用已經(jīng)可以通過應(yīng)用各層之間傳遞值的方式(請(qǐng)求處理程序 -> 請(qǐng)求路由 -> 服務(wù)器)將請(qǐng)求處理程序返回的內(nèi)容(請(qǐng)求處理程序最終要顯示給用戶的內(nèi)容)傳遞給HTTP服務(wù)器。

現(xiàn)在我們采用如下這種新的實(shí)現(xiàn)方式:相對(duì)采用將內(nèi)容傳遞給服務(wù)器的方式,我們這次采用將服務(wù)器“傳遞”給內(nèi)容的方式。 從實(shí)踐角度來說,就是將_response_對(duì)象(從服務(wù)器的回調(diào)函數(shù)_onRequest()_獲?。┩ㄟ^請(qǐng)求路由傳遞給請(qǐng)求處理程序。 隨后,處理程序就可以采用該對(duì)象上的函數(shù)來對(duì)請(qǐng)求作出響應(yīng)。

原理就是如此,接下來讓我們來一步步實(shí)現(xiàn)這種方案。

先從_server.js_開始:

var http = require("http");
var url = require("url");

function start(route, handle)  {? 
    function onRequest(request, response)  {? ? 
        var pathname = url.parse(request.url).pathname;
    ? ? console.log("Request for "  + pathname +  " received.");

    ? ? route(handle, pathname, response);? 
    }

    http.createServer(onRequest).listen(8888);
    console.log("Server has started.");
}

exports.start = start;

相對(duì)此前從_route()_函數(shù)獲取返回值的做法,這次我們將response對(duì)象作為第三個(gè)參數(shù)傳遞給_route()_函數(shù),并且,我們將_onRequest()_處理程序中所有有關(guān)_response_的函數(shù)調(diào)都移除,因?yàn)槲覀兿M@部分工作讓_route()_函數(shù)來完成。

下面就來看看我們的router.js:

function route(handle, pathname, response)  {
?     console.log("About to route a request for "  + pathname);? 
    if  (typeof handle[pathname]  ===  'function')  {
? ?     handle[pathname](response);? }  
    else  {
    ? ? console.log("No request handler found for "  + pathname);
    ? ? response.writeHead(404,  {"Content-Type":  "text/plain"});
    ? ? response.write("404 Not found");
    ? ? response.end();? 
    }
}

exports.route = route;

同樣的模式:相對(duì)此前從請(qǐng)求處理程序中獲取返回值,這次取而代之的是直接傳遞_response_對(duì)象。

如果沒有對(duì)應(yīng)的請(qǐng)求處理器處理,我們就直接返回“404”錯(cuò)誤。

最后,我們將_requestHandler.js_修改為如下形式:

var exec = require("child_process").exec;

function start(response)  {
? console.log("Request handler 'start' was called.");

? exec("ls -lah",  function  (error, stdout, stderr)  {
? ? response.writeHead(200,  {"Content-Type":  "text/plain"});
? ? response.write(stdout);
? ? response.end();? });
}

function upload(response)  {
? console.log("Request handler 'upload' was called.");
? response.writeHead(200,  {"Content-Type":  "text/plain"});
? response.write("Hello Upload");
? response.end();
}

exports.start = start;
exports.upload = upload;

我們的處理程序函數(shù)需要接收response參數(shù),為了對(duì)請(qǐng)求作出直接的響應(yīng)。

_start_處理程序在_exec()_的匿名回調(diào)函數(shù)中做請(qǐng)求響應(yīng)的操作,而_upload_處理程序仍然是簡(jiǎn)單的回復(fù)“Hello World”,只是這次是使用_response_對(duì)象而已。

這時(shí)再次我們啟動(dòng)應(yīng)用(node index.js),一切都會(huì)工作的很好。

如果想要證明_/start處理程序中耗時(shí)的操作不會(huì)阻塞對(duì)/upload_請(qǐng)求作出立即響應(yīng)的話,可以將_requestHandlers.js_修改為如下形式:

var exec = require("child_process").exec;

function start(response)  {
? console.log("Request handler 'start' was called.");

? exec("find /",? ? { timeout:  10000, maxBuffer:  20000*1024  },? ? function  (error, stdout, stderr)  {
? ? ? response.writeHead(200,  {"Content-Type":  "text/plain"});
? ? ? response.write(stdout);
? ? ? response.end();? ? });
}

function upload(response)  {
? console.log("Request handler 'upload' was called.");
? response.writeHead(200,  {"Content-Type":  "text/plain"});
? response.write("Hello Upload");
? response.end();
}

exports.start = start;
exports.upload = upload;

這樣一來,當(dāng)請(qǐng)求http://localhost:8888/start的時(shí)候,會(huì)花10秒鐘的時(shí)間才載入,而當(dāng)請(qǐng)求http://localhost:8888/upload的時(shí)候,會(huì)立即響應(yīng),縱然這個(gè)時(shí)候/start響應(yīng)還在處理中。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)