被自己的行为蠢哭了,意识到原因后真香!
作者 |?零一
來(lái)源 |?前端印象
這兩天在學(xué)習(xí) node 相關(guān)的知識(shí)時(shí),做出了一些錯(cuò)誤的行為~
在做用戶登錄相關(guān)業(yè)務(wù)時(shí)涉及到了 cookie、session 的存取,一搜就找到了 express-session 這個(gè)中間件,真香!配幾個(gè)配置就可以自動(dòng)生成 cookie、sessionId 了
但 express-session 對(duì)于 session 的存儲(chǔ)默認(rèn)是存在內(nèi)存中的
這肯定不合適!
項(xiàng)目一掛 或者 重啟,session信息全丟了,所有用戶都需要重新登錄
多個(gè)進(jìn)程之間也無(wú)法共享 session 數(shù)據(jù)
然后去搜了下有沒有 redis(key - value 形式的數(shù)據(jù)庫(kù)) 相關(guān)的庫(kù),咔,又出來(lái)兩個(gè):
redis(github上叫 node-redis,npm上叫 redis )
connect-redis
前者是提供了 js 可調(diào)用的操作 redis 數(shù)據(jù)庫(kù)的底層方法;后者就是獲取 redis 數(shù)據(jù)庫(kù)實(shí)例,成為 express-session 存取 session 信息的載體
官網(wǎng)的使用說(shuō)明也很清楚:
后來(lái)在我調(diào)試時(shí)發(fā)現(xiàn)出現(xiàn)了很多的問(wèn)題,比如第一次調(diào)用 API(不攜帶cookie),接口迅速響應(yīng),并帶回了 Set-cookies 頭,但是! 我接口響應(yīng)的數(shù)據(jù)呢???我明明返回了
最終 接口 15s 未返回,超時(shí)了
因?yàn)檫@個(gè)問(wèn)題應(yīng)該很明顯了,接口是有響應(yīng)返回的,但返回?cái)?shù)據(jù)這塊兒出了問(wèn)題,先快速定位了問(wèn)題所在,就是 express-session、redis、connect-redis 這三個(gè)庫(kù)其中一個(gè)有問(wèn)題,因?yàn)樵诮尤脒@幾個(gè)庫(kù)之前,session存儲(chǔ)在內(nèi)存中是能正常返回的
于是我就去這幾個(gè)庫(kù)的 issue 里搜了一下是否有類似的問(wèn)題,畢竟都是這么多 star 的庫(kù),明顯的問(wèn)題肯定早就被人提出來(lái)了,然而!沒有
我又開始在搜索引擎搜索,也同樣沒有!
怎么回事... 合著就我一個(gè)人有問(wèn)題,難道說(shuō)...?
我要發(fā)現(xiàn)一個(gè)驚天大bug,然后給他們提pr,從此人生一帆風(fēng)順了?(hhhhh,不知道大家有時(shí)候有沒有這樣的想法)
這就開始了我的 debug 看源碼之路
于是我 "咔",在 express-session 里打了個(gè)斷點(diǎn),同時(shí)也給 redis 服務(wù)開啟了 monitor 監(jiān)控,調(diào)試時(shí)發(fā)現(xiàn) redis 存取值的時(shí)候 key 竟然不一致
啥玩意兒?SET 的 key 是一段 hash + cookie的json字符串,value 是一個(gè)函數(shù)字符串(存?zhèn)€函數(shù)干啥?此時(shí)我非常非常疑惑);GET 的 key 卻只是一小段 hash
我找到源碼里存取key的位置打上了斷點(diǎn),發(fā)現(xiàn)存取值的key確實(shí)不一樣,但又因?yàn)榇藭r(shí)我還沒看多少源碼,所以對(duì)這塊兒的邏輯我暫且表示贊同,因?yàn)榭赡苓@就是別人庫(kù)的 feature 呢?
提前說(shuō)明一下,最終發(fā)現(xiàn)破案也是因?yàn)檫@里,但當(dāng)時(shí)的我是沒法斷定的
沒辦法了,繼續(xù)從頭 debug 吧
res.status(200).send({?name:?'01'?})先是我接口函數(shù)里返回了 http 狀態(tài)碼 200(怪不得我的接口立馬返回了200)
然后 send 函數(shù)調(diào)用走到了 end 函數(shù)中
看了大致的邏輯,發(fā)現(xiàn) express-session 重寫了 res 的 end 函數(shù),在里面判斷 session 信息有無(wú)修改,進(jìn)而判斷需不需要保存 session 到 store 里
//?存儲(chǔ)原始的?end?函數(shù) var?_end?=?res.end; var?_write?=?res.write; var?ended?=?false; //?重寫?end?函數(shù) res.end?=?function?end(chunk,?encoding)?{//?執(zhí)行額外的邏輯//?...//?最終執(zhí)行原始的?end?函數(shù)return?_end.call(res,?chunk,?encoding); };既然 debug 都走到 end 里了,說(shuō)明問(wèn)題快出來(lái)了,能猜到可能是原始的 end 函數(shù)沒有被調(diào)用導(dǎo)致的了
走到了調(diào)用 set 函數(shù)的地方,他第一個(gè)參數(shù)傳了個(gè)數(shù)組,第二個(gè)參數(shù)傳了個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)執(zhí)行就會(huì)走到 原始 end 函數(shù)的調(diào)用,按道理來(lái)說(shuō)就沒問(wèn)題了啊!
我繼續(xù)下一步調(diào)試,發(fā)現(xiàn)根本沒走到回調(diào)函數(shù)里,去查了一下 this.client.set 的 TS 類型
這次又證明了 redis 為什么 SET了key為一長(zhǎng)串字符串,value為一個(gè)函數(shù)
this.client 是 redis 這個(gè)庫(kù)提供的方法,我們是將 redis 生成的實(shí)例對(duì)象傳給 express-session 作為 session 存儲(chǔ)的載體,那現(xiàn)在看起來(lái)是 這兩個(gè)庫(kù)沒互相兼容??
目前內(nèi)心OS:兩個(gè)這么大的庫(kù),更新了都不做互相兼容的么
于是我又跑回去看文檔,gan!
還記得這個(gè)圖么?這是我一開始準(zhǔn)備用這些庫(kù)時(shí)看的,但我沒看到這個(gè)信息:當(dāng) redis 庫(kù)為 v4 版本時(shí),createClient 時(shí)需要加一個(gè) legacyMode: true 的參數(shù),開啟傳統(tǒng)模式?然后再回到我剛才發(fā)現(xiàn)的問(wèn)題,這個(gè)參數(shù)是不是就是為了兼容 express-session 的?
于是我趕緊加上這個(gè)參數(shù),嚯,真的好了!接口也迅速返回內(nèi)容了
所以最終是 redis 做了版本升級(jí),更改了 api 的使用方式
//?舊版?redis client.set(key,?value,?cb)//?新版?redis await?client.set(key,?value)確實(shí)還是新版的api使用起來(lái)舒服,換作舊版的,大家使用前可能還需要封裝一下
但是 express-session 并沒有更改 set 函數(shù)調(diào)用的傳參方式,這也很正常,畢竟這個(gè)庫(kù)只是為了 session 管理用的,而 redis 以及各種 db 庫(kù)又不止是專門服務(wù)于 express-session 的,它們的關(guān)系是這樣的:
然而當(dāng) db 庫(kù)進(jìn)行了更新,就需要中間一層來(lái)連接了,也就是類似我們本文用到的 connect-redis,此時(shí)它們的關(guān)系是這樣的
所以我們?cè)谟?connect-redis 時(shí),傳一個(gè) legacyMode: true 參數(shù)就可以讓 redis 兼容 express-session 的使用了
結(jié)論
該說(shuō)啥呢,被自己蠢哭了,一開始不好好看文檔,導(dǎo)致后面花了一天時(shí)間去排查問(wèn)題,還被迫看了這么多源碼,你要問(wèn)我后悔嗎?我又不后悔:被倒逼著去看了一個(gè)庫(kù)的源碼、梳理了大致的邏輯、學(xué)到了思想、鞏固了 cookie、session 的知識(shí)、學(xué)會(huì)了 redis 庫(kù)的調(diào)試和各種命令 和 長(zhǎng)了個(gè)教訓(xùn)(以后要好好看文檔)
什么時(shí)候比較適合看源碼呢?當(dāng)然是有需要的時(shí)候,儂,比如我這次的經(jīng)歷
大家千萬(wàn)別學(xué)我一樣,粗心大意,文檔還是要好好看,這就跟以前上學(xué)的時(shí)候答題不仔細(xì)看題目一樣,是大忌啊!!!
往期推薦
如果讓你來(lái)設(shè)計(jì)網(wǎng)絡(luò)
用過(guò)留痕,誰(shuí)動(dòng)了我的檔案?
一把王者的時(shí)間,我就學(xué)會(huì)了Nginx
明明還有大量?jī)?nèi)存,為啥報(bào)錯(cuò)“無(wú)法分配內(nèi)存”?
點(diǎn)分享
點(diǎn)收藏
點(diǎn)點(diǎn)贊
點(diǎn)在看
總結(jié)
以上是生活随笔為你收集整理的被自己的行为蠢哭了,意识到原因后真香!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 打破“单点防护”缺陷,山石网科发布“云网
- 下一篇: 立足当下,塑造未来