深入淺出Node.js(七):Connect模塊解析(之一)
作者 田永強 發佈於 2012年6月5日
新浪微博 騰訊微博 豆瓣網 Twitter Facebook linkedin 郵件分享 更多 3
相關廠商內容
12306插件引發GitHub故障,GitHub資深運維工程師確認參加QCon北京2013,現身說法
《程序員必知97件事》合著者Kevlin確認參加QCon北京2013並發表主題演講
阿里巴巴大數據專家鬼厲將在2013北京QCon分享淘寶雲梯Hadoop最佳事件
百度技術沙龍第三十五期:Java新時代——企業級應用架構與物聯網實踐(2013年2月2日 周六)
相關贊助商
QCon全球軟體開發大會2013,北京國際會議中心,4月25~27日,2月28日前8折優惠報名中,詳情請點擊!
Connect模塊背景Node.js的願望是成為一個能構建高速,可伸縮的網路應用的平台,它本身具有基於事件,非同步,非阻塞,回調等特性,這在前幾篇專欄中有過描述。正是基於這樣的一些特性,Node.js平台上的Web框架也具有不同於其他平台的一些特性,其中Connect是眾多Web框架中的佼佼者。Connect在它的官方介紹中,它是Node的一個中間件框架。超過18個捆綁的中間件和一些精選第三方中間件。儘管Connect可能不是性能最好的Node.jsWeb框架,但它卻幾乎是最為流行的Web框架。為何Connect能在眾多框架中勝出,其原因不外乎有如下幾個:
Connect自身十分簡單,其作用是基於Web伺服器做中間件管理。至於如何如何處理網路請求,這些任務通過路由分派給管理的中間件們進行處理。它的處理模型僅僅只是一個中間隊列,進行流式處理而已,流式處理可能性能不是最優,但是卻是最易於被理解和接受。基於中間件可以自由組合和插拔的情況,優化它十分容易。Connect模塊目前在NPM倉庫的MDO(被依賴最多的模塊)排行第八位。但這並沒有真實反映出它的價值,因為排行第五位的Express框架實際上是依賴Connect創建而成的。關於Express的介紹,將會在後續的專欄中一一為你講解。
中間件讓我們回顧一下Node.js最簡單的Web伺服器是如何編寫的:
var http = require("http");http.createServer(function (req, res) { res.writeHead(200, {"Content-Type": "text/plain"}); res.end("Hello World
");}).listen(1337, "127.0.0.1");
我們從最樸素的Web伺服器處理流程開始,可以看到HTTP模塊基於事件處理網路訪問無外乎兩個主要的因素,請求和響應。同理的是Connect的中間件也是扮演這樣一個角色,處理請求,然後響應客戶端或是讓下一個中間件繼續處理。如下是一個中間件最樸素的原型:
function (req, res, next) { // 中間件}
在中間件的上下文中,有著三個變數。分別代表請求對象、響應對象、下一個中間件。如果當前中間件調用了res.end()結束了響應,執行下一個中間件就顯得沒有必要。
流式處理為了演示中間件的流式處理,我們可以看看中間件的使用形式:
var app = connect();// Middlewareapp.use(connect.staticCache());app.use(connect.static(__dirname + "/public"));app.use(connect.cookieParser());app.use(connect.session());app.use(connect.query());app.use(connect.bodyParser());app.use(connect.csrf());app.use(function (req, res, next) { // 中間件});app.listen(3001);
Conncet提供use方法用於註冊中間件到一個Connect對象的隊列中,我們稱該隊列叫做中間件隊列。
Conncet的部分核心代碼如下,它通過use方法來維護一個中間件隊列。然後在請求來臨的時候,依次調用隊列中的中間件,直到某個中間件不再調用下一個中間件為止。
app.stack = [];app.use = function(route, fn){ // … // add the middleware debug("use %s %s", route || "/", fn.name || "anonymous"); this.stack.push({ route: route, handle: fn }); return this;};
值得注意的是,必須要有一個中間件調用res.end()方法來告知客戶端請求已被處理完成,否則客戶端將一直處於等待狀態。流式處理也是Node.js中用於流程式控制制的經典模式,Connect模塊是典型的應用了它。流式處理的好處在於,每一個中間層的職責都是單一的,開發者通過這個模式可以將複雜的業務邏輯進行分解。
路由從前文可以看到其實app.use()方法接受兩個參數,route和fn,既路由信息和中間件函數,一個完整的中間件,其實包含路由信息和中間件函數。路由信息的作用是過濾不匹配的URL。請求在遇見路由信息不匹配時,直接傳遞給下一個中間件處理。通常在調用app.use()註冊中間件時,只需要傳遞一個中間件函數即可。實際上這個過程中,Connect會將/作為該中間件的默認路由,它表示所有的請求都會被該中間件處理。中間件的優勢類似於Java中的過濾器,能夠全局性地處理一些事務,使得業務邏輯保持簡單。任何事物均有兩面性,當你調用app.use()添加中間件的時候,需要考慮的是中間件隊列是否太長,因為每一層中間件的調用都是會降低性能的。為了提高性能,在添加中間件的時候,如非全局需求的,盡量附帶上精確的路由信息。以multipart中間件為例,它用於處理表單提交的文件信息,相對而言較為耗費資源。它存在潛在的問題,那就是有可能被人在客戶端惡意提交文件,造成伺服器資源的浪費。如果不採用路由信息加以限制,那麼任何URL都可以被攻擊。
app.use("/upload", connect.multipart({ uploadDir: path }));
加上精確的路由信息後,可以將問題減小。
MVC目錄
藉助Connect可以自由定製中間件的優勢,可以自行提升性能或是設計出適合自己需要的項目。Connect自身提供了路由功能,在此基礎上,可以輕鬆搭建MVC模式的框架,以達到開發效率和執行效率的平衡。以下是筆者項目中採用的目錄結構,清晰地劃分目錄結構可以幫助劃分代碼的職責,此處僅供參考。
├── Makefile // 構建文件,通常用於啟動單元測試運行等操作├── app.js // 應用文件├── automation // 自動化測試目錄├── bin // 存放啟動應用相關腳本的目錄├── conf // 配置文件目錄├── controllers // 控制層目錄├── helpers // 幫助類庫├── middlewares // 自定義中間件目錄├── models // 數據層目錄├── node_modules // 第三方模塊目錄├── package.json // 項目包描述文件├── public // 靜態文件目錄│ ├── images // 圖片目錄│ ├── libs // 第三方前端JavaScript庫目錄│ ├── scripts // 前端JavaScript腳本目錄│ └── styles // 樣式表目錄├── test // 單元測試目錄└── views // 視圖層目錄
參考:
關於作者
田永強,新浪微博@朴靈,前端工程師,曾就職於SAP,現就職於淘寶,花名朴靈,致力於NodeJS和Mobile Web App方面的研發工作。雙修前後端JavaScript,寄望將NodeJS引薦給更多的工程師。興趣:讀萬卷書,行萬里路。個人Github地址:http://github.com/JacksonTian。
推薦閱讀:
※解析八正道(宋澤萊)
※仁澤易道:解析生命線,生命線不好是否預示著什麼?
※趙孟俯《三門記》《妙嚴寺》解析 下
※解析男女夢境的不同心理含義
※足球技巧:梅西的5大過人技巧及實戰解析