Nodejs中,我將資料庫連接後的實例對象放在global全局對象中,這樣合理嗎?

如題,這樣就不用根據每個Model將mongoose模型傳遞過去了,可以在Model中直接使用global上面的資料庫模型。請問這樣設計合理嗎?如果不合理,那還有什麼更好的方式使用資料庫嗎?

———————————————————————————————————————————

大家可能沒理解我的意思,我換個問題吧:將某些常用的變數或者方法放到global全局對象中合理嗎?

我是這樣想的:每個路由都會require一些js文件,有很多是相同的,如果把相同的放在global中是否可以節約內存?我對這個不太懂,還望各位賜教。


nodejs需要很嚴謹的編程習慣,最好引入jslint來規範你的代碼。

對於global的使用,為何不推薦,因為global很不可控,對於模塊化開發來說是個惡魔特性。

第一個問題:脫離模塊化初衷:

nodejs開發一般模塊化都非常嚴格,一個人負責幾個模塊,這時候如果你突然創建了一個global,在另一個模塊里調用,這個global其實很不可控,一個是這個調用global的模塊會強依賴你定義global的那個模塊,二者不能脫開使用。

典型的例子,你在入口app.js里定義global.db。在service模塊里調用了global.db。好了,這個service只能在這個應用里調用了,如果你要寫個單測的腳本,如果你要寫個定時任務腳本,你調用了這個service,然後就會報global.db不存在。事實上這是個非常嚴重的問題。

第二個問題:無法避免黑盒操作

如果你在外層定義了global,在你某個模塊使用global.db的時候,你無法確保沒有人修改過這個global.db,因為node里全局變數可以任意修改和使用,這是為什麼js里需要杜絕全局變數的最根本原因,是模塊化思想的根本問題。

其實在js里這種細節還有很多,例如定義方法的時候如果參數需要傳入對象,對對象加工的話。不要暗盒加工傳入的對象,而是copy一個新對象加工後,然後return出來。這個是引用對象的坑,避免這個坑的方法就是盡量避免黑盒操作!當多人維護一段代碼的時候,別人不會關心你的方法的具體實現,如果傳入一個對象,你就修改源對象,這時候就是一個黑盒操作了外部變數,很容易引起一些隱蔽問題。

PS: NodeJS中資料庫連接一定要引入連接池啊,自動重連啊。一般的ORM都提供的,或者單獨引入一個pool管理的庫(例如:generic-pool 可以管理很多種資料庫甚至redis之類的連接池)。


1. 不能節省內存,但是可以節省獲取一個模塊的時間。require() 第一次解析模塊,拿到 module.exports (interface)之後,就會把這個模塊存在內存中了。第二次請求同一個模塊的時候,會直接給內存中的這個模塊,是的,模塊是單例的;放在 global 或者通過 require 獲取,只是從哪裡讀到 exports 的問題,並不會多次 require 並不會造成多分數據;

2. 是否可以放在 global 上?不建議放在全局。但我看到 balderdashy/sails · GitHub 是這麼做的,在 sails 中,配置的 ORM API 是放在全局的,不用 require,直接可以調用;

3. 我的建議還是 require 最好,避免全局變數,標準的 CommonJS,可以很清楚的看到模塊間的依賴。


任何在nodejs中使用global都是刷流氓

其實你可以把伺服器代碼定義一份生成規則 這樣你就不需要Global對象啦


直接使用一個全局的資源對象,是典型的封裝不夠

使用global.db的模塊都假設它是連續可靠的,但如果中途到資料庫的鏈接中斷,你必須重連,重新設置global.db,但是你不知道中斷會發生在哪一次global.db,是不是要在所有調用global.db的地方加判斷呢?當然不想,所以你需要的不是global.db,而是global.getDB(),把重連邏輯和錯誤處理封裝在getDB()里,給各模塊使用。

但是,又假設你有很多模塊要用到一個從用戶數據表獲得信息的調用,暫且叫做getUser(),你是不是也把它掛到global下呢?如果你還有一個getProduct(),是不是弄成global.getProduct()?這個時候你覺得太多了,開始覺察到這種思路只是global.getDB()的慣性,你的品味告訴你,這樣似乎不太好,掛global有你不想要的副作用,你可以容忍20個module用global.getDB(),但恐怕會覺得20個模塊依賴多個global下的成員太「臟」,儘管本質上這並沒有區別;但是你覺得不舒服,因為這違背了你的初衷,你把狼放出來,它就不聽你的了。

你真正需要的是一個db access layer,你需要一完整的封裝,而不是只封裝鏈接管理。

db.js

// module化以後有利於封裝,讓使用代碼更好讀
// module擴展也容易
var db = null;
module.exports = {
init: function () {
if (db === null) {
db = require("...").connect("...");
if (!db) {
// 連接失敗
}
}
return db;
},
// 封裝數據訪問邏輯
doA: function () { ... },
dbB: function () { ... }
};

moduleN.js or app.js

var db = require("./db.js");
db.doA()
db.doB()


不能全局變數,也不能局部變數,包括把變數封裝形式,而是應該基於請求變數。每個請求保存一個資料庫連接,否則一個連接服務多個請求,會衝突。前面的回答都沒考慮衝突問題。

nodejs是單進程模式,非同步處理io請求,但不代表可以共用全局變數,全局變數只能是只讀的。

一個連接發送sql請求之後,他就掛起了,等結果,這時候就去處理其他請求了,另外請求也發個sql請求,那麼數據返回應該是哪個sql的?甚至這個連接被一個請求關閉了,另外的請求還在使用這個關閉了的連接。

我來說幾點關於變數的事情。在web程序中,全局變數不一定是」全局」。局部變數也不一定是臨時的,還有一種變數叫請求變數(暫且這麼叫)。

一般來說,一個主進程,會有n個worker子進程,n一般跟cpu核心數一樣為宜。那麼全局變數在不同worker之間是不共用的,不能通過全局變數來維護所有worker的一些全局狀態。如果要共享,可以用共享內存,如果要誇機器共享,可以用memcache,redis。

另外一個局部變數,不是」臨時」的,可能是永久的。

比如

var cnt=0;

function a(){cnt++}

worker啟動之後編譯運行,首次運行之後,就不會重新編譯(lua是這樣的,js我想應該一樣),這個跟客戶端是不一樣的。

那麼每次a運行之後,cnt值就變了。

第一次http請求,cnt是0,第二次請求,如果是不同worker,還是0,如果是同一個worker處理,可能就是1了。如果要每次請求都變成0,在模塊初始化函數裡面設置cnt=0

還有一種變數是請求相關,就是每次http請求,會有一份相關的數據,同一個請求這個數據是一樣的。比如要存放登錄的用戶名,從session讀取之後,存到這裡,可以保證這個http請求過程中用戶名記錄都是對的。放全局變數,局部變數,都可能衝突。比如當前請求操作一個非同步處理,比如讀文件。nodejs就會掛起當前請求,處理另外的請求,全局變數,局部變數是共用的。

以上我是根據ngx_lua推斷的,nodejs還沒深入研究,有錯誤歡迎指正。還不知道nodejs如何處理請求級數據,類似lua的ngx.ctx變數。


你是個人項目或者項目的領導者,還是開源項目的參與者,公司項目的參與者?

如果是參與者,問你們老大,他說好就好,他說不好就不好。

如果是你自己的項目或者你自己是領導者,你自己覺得哪個最終能提高開發效率,並且不會造成太多可維護性問題,就用哪個。

你具體的例子我不清楚,但我肯定是把封裝過的資料庫連接對象做成全局的。

別聽那些代碼潔癖的人瞎逼逼,工程實現,開發效率第一。

什麼叫做開發效率第一,你是參與者,大家都怎麼干,老大要求怎麼干,你怎麼干。不給團隊添麻煩,大家已經習慣一套方式。

你是組織者,只要想清楚了,怎麼都行。注意,要想清楚,在嚴密死板的結構上打洞是有風險的


(?? . ??)


必要時還是要創建資料庫的全局連接對象,這個針對不同的路由操作方便啊,不用反覆創建連接和釋放連接。另外呢,如果以後針對多用戶創建多連接池,每個池可以用一個全局連接對象啊。面對node-oracledb我是這樣做的,希望大神給予批評指正。


MongoClient or how to connect in a new and better way

MongoClient connection pooling

A Connection Pool is a cache of database connections maintained by the driver so that connections can be re-used when new connections to the database are required. To reduce the number of connection pools created by your application, we recommend calling MongoClient.connect once and reusing the database variable returned by the callback:


不好

如今流行share nothing或者share by communicating,這種赤裸裸的共享全局變數實在太Low了,後患無窮……


推薦閱讀:

Web 前端和後端工程師的具體職責分別是怎樣的?
想深入了解 HTTP 協議,有哪些值得推薦的書籍?
零基礎學習Web前端開發應該先學什麼?
用python+flask搭建一個博客需要學習哪些知識?
IT培訓相對於大學學到的究竟是什麼?

TAG:Web開發 | JavaScript | 後端技術 | MongoDB | Nodejs |