轉自 kris 在nodejs使用Redis緩存和查詢數據及Session持久化(Express)html
客戶端與服務會使用一個Sessionid的Cookie值來進行客戶端和服務器端會話的匹配,這個Cookie通常是服務器端讀/寫的,並在Http請求響應的Header中的Set-Cookie屬性設置:node
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 14 Jan 2015 02:29:09 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Content-Encoding: gzip
Set-Cookie: sessionid=i4w3axuzyj4nwwg75y6k5us2; path=/; domain=.ourjs.com; httponlynginx
注* 中間代理人攻擊,便是經過代理服務器(如無線路由)盜取你的會話Cookie(SessionID等),從而訪冒你的身份。所以Google建議網站所有采用HTTPS協議,加密傳輸內容,並提升了純HTTPS網站的權重。git
使用數據庫來集中管理session,存放Session內容,並在各個子域名跨域共享Cookies (SessionID),便可實現爲每個子域分配一個獨立的node.js Web服務器,各個服務程序都可依據sessionid從數據庫中尋找到同一Session,從而實現不一樣Web Server中的會話同步,從而實現必定程度上的負載均衡。github
要想實現徹底意義的負載均衡還須要將Web服務作到徹底狀態無關,不只僅是Session,全部的中間緩存數據都要轉移到與服務器無關的緩存層中,這正是Redis最善長的地方。redis
可是爲何存放在Redis中要比MongoDB中好呢?數據庫
在MongoDB中是這樣存放Session的, 使用 connect-mongo 即用來將Express中的Session持久化到Mongodb的一箇中間件,它也能夠在connect 上使用。express
Express 4.x, 5.0 與 Connect 3.x配合使用:npm
var session = require('express-session'); var MongoStore = require('connect-mongo')(session); app.use(session({ secret: 'foo', store: new MongoStore(options) }));
Express 2.x, 3.x 和 Connect 1.x, 2.x配合使用:跨域
var MongoStore = require('connect-mongo')(express); app.use(express.session({ secret: 'foo', store: new MongoStore(options) }));
對於 Connect 只須要將express替換成connect便可
MongoDB 是一個基於文檔的數據庫,全部數據是從磁盤上進行讀寫的。MongoDB善長的是對無模式JSON數據的查詢。
而Redis是一個基於內存的鍵值數據庫,它由C語言實現的,與Nginx/ NodeJS工做原理近似,一樣以單線程異步的方式工做,先讀寫內存再異步同步到磁盤,讀寫速度上比MongoDB有巨大的提高。所以目前不少超高併發的網站/應用都使用Redis作緩存層,廣泛認爲其性能明顯好於MemoryCache。當併發達到必定程度時,便可考慮使用Redis來緩存數據和持久化Session。
Redis (安裝方法 ) 數據庫採用極簡的設計思想,最新版的源碼包還不到2Mb。其在使用上也有別於通常的數據庫。
redis驅動程序多使用 node_redis 此模塊可搭載官方的 hiredis C 語言庫 - 一樣是非阻塞的,比使用JavaScript內置的解釋器性能稍好。可選擇將hiredis 與 redis 一同安裝。
npm install hiredis redis
若是 hiredis 安裝成功, node_redis 會默認使用 hiredis, 不然會使用JavaScript的解釋器。
Redis的一個Key不只能夠對應一個String類型的值,還支持hashes, lists, sets, sorted sets, bitmaps等。
好比存/取一組Hash值,Redis中有兩個對應的命令
HMSET key field value [field value ...]、
爲一個Key一次設置多個哈希鍵/值, 多用於JSON對象的寫入(序列化的SESSION)。
HGETALL key
讀取一個Key的全部 哈希鍵/值,多用於JSON對象讀取
這兩個命令便是在NodeJS中存取JSON對象的關鍵,
下面是node_reids中對應的例子:
var redis = require("redis"), client = redis.createClient(); //寫入JavaScript(JSON)對象 client.hmset('sessionid', { username: 'kris', password: 'password' }, function(err) { console.log(err) }) //讀取JavaScript(JSON)對象 client.hgetall('sessionid', function(err, object) { console.log(object) })
Redis沒有嚴格意義上的表名和字段名,以 Key-Value 鍵值對的方式存儲,所以通常採用 schema:key 形式作爲鍵值,其中
schema: 可理解爲傳統數據庫中的表名
key: 可理解爲表中的主鍵
所以使用redis存放你的session時,須要一個schema前輟, 好比這個key: sessionid:i4w3axuzyj4nwwg75y6k5us2
Redis 也僅能對Key進行檢索, 尚不支持對Key所存放的Hash Key的檢索。 如要檢索到全部session,只需匹配 sessionid:* 便可,
client.keys('session:*', function (err, keys) { console.log(keys) })
有些第三方庫會支持檢索值中的Hash Key,但這不是一個原子性操做,redis自己並不提供。
所以在採用Redis緩存與檢索數據時,要使用一些獨特的數據類型,如集合(Sets)
> sadd myset 1 2 3 //添加 1 2 3到集合myset (integer) 3 > smembers myset //列出集合的全部成員 1. 3 2. 1 3. 2 > sismember myset 30 //判斷30是否存在 (integer) 0 //不存在
Redis集合不容許添加相同成員。屢次添加同一元素到集合中最終只會包含一個元素。多個集合之間能夠進行鏈接/交集這樣的操做。從而實現相似傳統數據庫中索引、條件和鏈接查詢的效果。
# 添加 3 個用戶和信息 hmset user:1 user_name lee age 21 hmset user:2 user_name david age 25 hmset user:3 user_name chris age 25 # 維護age索引 sadd age:21 1 sadd age:25 2 3 # 維護name索引 sadd name:lee 1 sadd name:david 2 sadd name:chris 3 # 查找 age = 25 和 name = lee 的用戶 sinter age:25 name:lee -> 會返回一個空集合
connect-reids 是一個 Redis 版的 session 存儲器,使用node_redis做爲驅動。藉助它便可在Express中啓用Redis來持久化你的Session.
$ npm install connect-redis
在 Express 3.x 中還須要安裝express-session
$ npm install express-session
將express-session傳給connect-redis來啓用
var session = require('express-session'); var RedisStore = require('connect-redis')(session); app.use(session({ store: new RedisStore(options), secret: 'keyboard cat' }));
檢驗
app.use(function (req, res, next) { if (!req.session) { return next(new Error('oh no')) // handle error } next() // otherwise continue })
這樣你的Session就轉移到了Redis數據庫,這樣作的一個額外好處是,當你的Express服務器忽然重啓後,用戶仍然可使用當前Cookie中的SessionID從數據庫中獲取到他的會話狀態,作到會話不丟失,在必定程度上提升網站的鍵壯性。
若是你的NodeJS網站上的全部緩存數據都轉移到了Redis後,就可作到徹底狀態無關,按需擴展網站的規模。
可水平擴展的NodeJS網站服務器集羣(非 cluster模塊 不一樣,它們是相互獨立的,可分佈在不一樣的物理服務器上),這樣的架構,對於應對超大規模併發也是有好處的。