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