前面是瞎扯,代碼在后面。
前言
http 請求是無狀態(tài)的,什么是無狀態(tài)呢,就是你先后向服務(wù)器發(fā)送兩個(gè)請求,服務(wù)器那邊比較糊涂,自己是沒有辦法識別這兩個(gè)請求是否是同一個(gè)用戶發(fā)送來的。
也就是說,我上一秒給你五毛錢你下一秒就不認(rèn)賬的意思。
為了避免這種情況發(fā)生,我們告訴服務(wù)器,對岸的朋友來的時(shí)候,你給他一個(gè)簽名,告訴他,下次帶著簽名過來,確認(rèn)一下是不是對的人。
當(dāng)然,這個(gè)簽名也是有 有效期 的。
所以,我們開發(fā)者就在用戶第一個(gè)請求過來的時(shí)候存起來,然后給用戶一個(gè)對應(yīng)的標(biāo)識,這個(gè)標(biāo)識也是有 有效期 的,用戶下一個(gè)請求過來的時(shí)候判斷一下這個(gè)標(biāo)識是否對應(yīng)是否符合條件,if true next else redirect ,就這樣。
當(dāng)然,程序員是無敵的嘛,我們有一百種改變世界的辦法,如今,我已經(jīng)找到了這樣的辦法,本篇簡書就到此結(jié)束,謝謝大家?。?!

session
What!??!看到這里你還問我 session 是什么?WTF
嘿siri,session 是什么東東
Session:在計(jì)算機(jī)中,尤其是在網(wǎng)絡(luò)應(yīng)用中,稱為“會話控制”。Session 對象存儲特定用戶會話所需的屬性及配置信息。這樣,當(dāng)用戶在應(yīng)用程序的 Web 頁之間跳轉(zhuǎn)時(shí),存儲在 Session 對象中的變量將不會丟失,而是在整個(gè)用戶會話中一直存在下去。當(dāng)用戶請求來自應(yīng)用程序的 Web 頁時(shí),如果該用戶還沒有會話,則 Web 服務(wù)器將自動創(chuàng)建一個(gè) Session 對象。當(dāng)會話過期或被放棄后,服務(wù)器將終止該會話。Session 對象最常見的一個(gè)用法就是存儲用戶的首選項(xiàng)。例如,如果用戶指明不喜歡查看圖形,就可以將該信息存儲在 Session 對象中。有關(guān)使用 Session 對象的詳細(xì)信息,請參閱“ASP 應(yīng)用程序”部分的“管理會話”。注意 會話狀態(tài)僅在支持 cookie 的瀏覽器中保留。
嘿siri,自殺
......
簡單的說,session 可以用來維持會話,服務(wù)器每天要接受 XXX 個(gè)請求,上面說了 http 是無狀態(tài)的,哪能分得出誰是誰,不過 session 可以用來幫助服務(wù)器區(qū)分哪些請求對應(yīng)哪些用戶,對的,就這樣,不知道有沒有說清楚~
舉個(gè)例子吧,剛學(xué)編程時(shí)會接觸到登陸功能,登錄成功的時(shí)候一定要存一個(gè) session 然后返回到用戶的 cookie 中,用來保持這個(gè)用戶的會話信息,用戶下個(gè)請求攜帶著 cookie 信息來的時(shí)候判斷一下該用戶是否登錄,然后在進(jìn)行后續(xù)的一頓操作,嗯,就這樣 ~ 還有不懂得可以在網(wǎng)上找一些資料更詳細(xì)的了解一下這一塊 ~
基于內(nèi)存來管理 session
剛開始我們?yōu)榱朔奖憧旖輹?session 存在服務(wù)器的內(nèi)存中,就像下面這樣:
變動的地方會用 start 和 end 標(biāo)識出來
// app.js 文件中
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
// start
// 安裝 express-session 模塊 并引入
var session = require('express-session');
// end
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// start
// session 的一些配置
app.use(session({
secret: 'zz',
cookie:{maxAge:60000},
resave: false,
saveUninitialized: true
}));
// end
app.use('/static',express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
// ./routes/index.js 文件中
var express = require('express');
var router = express.Router();
/* GET home page. */
// 用來為這個(gè)用戶存儲一個(gè) session
router.get('/a', function(req, res, next) {
var a = Math.floor(Math.random()*10)+''
req.session.user = a
res.send(a)
});
// 在頁面上打印自己在 session 中存儲的信息
router.get('/b', function(req, res, next) {
req.session.touch();
res.send(req.session.user)
});
module.exports = router;


上面這種基于內(nèi)存來存儲 session 方法唯一的好處是方便省事,適合初學(xué),但是也存在好多缺點(diǎn)。比如說:
- 如果用戶量比較大,session 會把內(nèi)存占滿
- 服務(wù)器掛了,然后重啟后 session 就不存在了
- 項(xiàng)目部署在多臺服務(wù)器的集群中,就不好使了
......
基于 mongo 來管理 session
不一定要用 mongo 來管理 session,你們可以用 redis 、 memcache 、mysql 等都可以,我這里用 mongo 是因?yàn)殡娔X里面剛裝了個(gè) mongo , 就順手拿來用了。
話不多說,先上代碼為敬~
// app.js 文件中, ./routes/index.js 文件不變
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
// start
// 引入 這兩個(gè) 第三方庫
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
// end
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// start
// 設(shè)置 session 存儲的一些配置選項(xiàng)
app.use(session({
secret: 'zz',
cookie: {
maxAge: 10000
},
resave: false,
saveUninitialized: true,
store: new MongoStore({
url:'mongodb://localhost/ims',
collection:'sessions'
})
}))
// end
app.use('/static',express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;



上圖中可以看到,mongo 中存儲了一條 session 記錄,這樣你如果項(xiàng)目跑在多臺服務(wù)器的話,這條 session 記錄就可以在這多臺服務(wù)器中共享,即使其中某臺服務(wù)器掛了也不影響 session 的存儲,從而來達(dá)到 session 持久化存儲。
總結(jié)
基于內(nèi)存的 session 存儲
0.如果用戶量比較大,session 會把內(nèi)存占滿
1.服務(wù)器掛了,然后重啟后 session 就不存在了
2.項(xiàng)目部署在多臺服務(wù)器的集群中,就不好使了
......
session 持久化存儲
0.提高服務(wù)器內(nèi)存的利用率
1.在多臺服務(wù)器同時(shí)對外提供服務(wù)的集群系統(tǒng)中,可以共享 session
2.當(dāng)某臺服務(wù)器掛掉后,重啟以后還可以使用之前未過期的 session
這么枯燥乏味的文章能看到這里已經(jīng)很棒了,這些東西都太抽象了,剛開始肯定不好理解,建議多查詢一些資料,親自試驗(yàn)一下,感受一下成功的喜悅,嗯哼~
