NodeJS緩存機制:暢銷貨,就多囤一點唄


上一篇文章,我們已經實現了客戶端向NodeJS服務器發出請求時,服務器從磁盤讀取文件內容后,向客戶端返回文件的數據。而對於愛蓮(iLinkIT)的1對n的場景,即將文件共享出來之后,讓多個用戶同時下載,如果每個用戶發起請求,我們都重新去磁盤讀一下文件,那樣豈不是效率低下?本文將重點改進一下效率和體驗的問題。

老規矩,先上一個圖:

因為對於一個具體的共享任務,文件是同一個的,我們可以只做一次讀取文件的操作,把讀取進來的文件數據先保存到緩沖區。當有客戶端發送請求時,就從緩沖區讀取數據響應對應的客戶端。

代碼如下:

 1 var http = require( 'http' );
 2 var fs = require('fs');
 3 
 4 var file_path = "D:\\ilinkit_logo.png" ;
 5 var file_stream ;
 6 var buffer_box = [] ;
 7 var file_length = 0 ; 
 8 
 9 fs.stat( file_path , function ( err , stat ){
10             if (err) {
11                 if ('ENOENT' == err.code) {
12                     console.log( 'File does not exist...' );
13                 } else {
14                     console.log( 'Read file exception...' );
15                 }
16             } else {
17                 file_stream = fs.createReadStream( file_path );      
18                 file_stream.on( 'data' , function( chunk ){
19                     buffer_box.push( chunk ) ;
20                     file_length += chunk.length ;
21                 } ); 
22                 file_stream.on( 'end' , function(  ){
23                     console.log( "文件讀取完畢" );
24                 } );
25                 file_stream.on('error', function(err){
26                     console.log( "文件讀取失敗!" );
27                 }); 
28                 
29                 var server =http.createServer( function ( request ,response ){
30                     for( var buffer_index = 0 ; buffer_index<buffer_box.length ; buffer_index++ )
31                     {
32                         response.write( buffer_box[buffer_index] );
33                     }
34                     response.end();
35                 } );
36                 server.listen( 8000 );
37                 console.log( 'HTTP服務器啟動中,端口:8000.....' );
38                 
39             }//end else,讀取文件沒有發生錯誤
40 });                 

先解釋一下整體的框架調整。從第9行~第40行,都是用fs.stat檢查文件的相關信息,如果文件存在不存在,或者其他異常(例如:當前用戶無權限讀取該文件等。),就什么也不做,退出程序。如果讀取文件正確,則創建一個HTTP服務器(第29行~第36行)。

關鍵代碼解析如下:

第6行和第7行,聲明保存文件數據的緩沖區的相關變量。

第19行和第20行,將文件的數據保存到緩沖區中。

第30行~第34行,當有客戶端有發送請求時,從緩沖區讀取數據,通過response對象向客戶端傳送數據。

 

驗證方式如下:

1. 啟動服務器:打開命令行,進入js腳本所在的位置,執行:node f_ilinkit_1.js

2. 打開瀏覽器,輸入:http://localhost:8000,顯示如下:

改進1

建立了緩沖區,客戶端提交請求之后,直接從緩沖區返回數據,貌似整個過程已經很完美了,但是,其實,咱們的程序還存在致命的問題。我們在向客戶端響應數據的時候,是非常簡單地調用 response.write( buffer_box[buffer_index] ); 來響應,但是,知道HTTP協議的同學都知道,如果用Web服務器的標准來看,客戶端和服務器之間的響應,是還需要進行“響應頭”的設置的,這樣,客戶端才知道接收到的數據是什么類型?應該如何處理?

因為我們之前用來測試的文件是D:\ilinkit_logo.png,客戶端(瀏覽器)接收到一個圖片的文件之后,就把它顯示在瀏覽器中了,如果我們共享的文件是zip文件,是rar文件,會發生什么呢?所以,服務器在向客戶端發送數據時,應該設置“響應頭”的內容,讓客戶端把當前的數據當作一個附件來處理,這也符合我們愛蓮(iLinkIT)的業務場景。改進后的代碼如下:

 1 var http = require( 'http' );
 2 var fs = require('fs');
 3 
 4 var file_path = "D:\\ilinkit_logo.rar" ;
 5 var file_stream ;
 6 var buffer_box = [] ;
 7 var file_length = 0 ; 
 8 
 9 var file_name = file_path.substr( file_path.lastIndexOf('\\')+1 );
10 
11 fs.stat( file_path , function ( err , stat ){
12             if (err) {
13                 if ('ENOENT' == err.code) {
14                     console.log( 'File does not exist...' );
15                 } else {
16                     console.log( 'Read file exception...' );
17                 }
18             } else {
19                 file_stream = fs.createReadStream( file_path );      
20                 file_stream.on( 'data' , function( chunk ){
21                     buffer_box.push( chunk ) ;
22                     file_length += chunk.length ;
23                 } ); 
24                 file_stream.on( 'end' , function(  ){
25                     console.log( "文件讀取完畢" );
26                 } );
27                 file_stream.on('error', function(err){
28                     console.log( "文件讀取失敗!" );
29                 }); 
30                 
31                 var server =http.createServer( function ( request ,response ){
32                     response.setHeader( 'Content-Type' , 'application/octet-stream' );
33                     response.setHeader( 'Content-Disposition' , 'attachment; filename=' + encodeURIComponent(file_name) );
34                     
35                     for( var buffer_index = 0 ; buffer_index<buffer_box.length ; buffer_index++ )
36                     {
37                         response.write( buffer_box[buffer_index] );
38                     }
39                     response.end();
40                 } );
41                 server.listen( 8000 );
42                 console.log( 'HTTP服務器啟動中,端口:8000.....' );
43                 
44             }//end else,讀取文件沒有發生錯誤
45 });                 

關鍵的改進點說明如下:

第4行,共享的文件,我們把ilinkit_logo.png修改為ilinkit_logo.rar。當然,在D:下應該放一個ilinkit_logo.rar的文件。

第9行,我們從共享的文件路徑中,解析出文件名(例子中就是:ilinkit_logo.rar),用於向客戶端響應時,告知當前附件的文件名。

第32行和第33行,在向客戶端提供文件數據之前,先設置響應的內容的類型(Content-Type)和內容特點(Content-Disposition),告訴客戶端,要將接收到數據當附件處理,文件名為ilinkit_logo.rar。

驗證方式如下:

1. 先將ilinkit_logo.png壓縮成ilinkit_logo.rar,壓縮后的文件依然放到D:下面。

2. 啟動服務器:打開命令行,進入js腳本所在的位置,執行:node f_ilinkit_2.js

3. 打開瀏覽器,輸入:http://localhost:8000,顯示如下:

我們服務器里提供的共享文件是ilinkit_logo.rar,所以,用客戶端訪問,服務器就向客戶端響應一個附件文件,其實,上面的代碼中,我們如果把ilinkit_logo.rar修改為ilinkit_logo.png,瀏覽器收到文件數據之后,仍然會把它當“附件”處理,而不會顯示在瀏覽器中,因為我們設置了響應頭的內容。

共享文件修改為ilinkit_logo.png的時候,瀏覽器下載效果如下:

【要點回顧】

今天的解說就到這里,我們一起來回顧一下要點:

1. 改進了響應數據的方式,將要共享的文件先讀取到緩沖區,提高響應效率。

2. 通過設置“響應頭”,告知客戶端要將接收到的數據,當“附件”處理。

感謝諸位捧場,歡迎多提寶貴建議,謝謝^_^~~

 

-----------------------愛蓮(iLinkIT)系列文章------------------------------------------

 緣起愛蓮:我要的,現在就要!

愛蓮(iLinkIT)的架構與原理

遇見NodeJS:JavaScript的貴人

NodeJS服務器:一行代碼 = 一個的HTTP服務器

NodeJS文件讀取:感恩常在--抓把糖果,愉悅客人

NodeJS緩存機制:暢銷貨,就多囤一點唄

NodeJS安全設計:好吃的草莓味糖果,只給好朋友小紅

NodeJS服務器退出:完成任務,優雅退出


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM