被踢出去的用戶
0
在還沒有掌握全部證據之前就下結論會犯嚴重的錯誤,會使判斷帶有偏見。——《血字的研究》
“齊識,路老板又來郵件了。”白娜一臉無耐地說。
“一定沒好事吧?”齊識回頭看了一眼,手依舊在快速敲著代碼,并沒有停。
“你剛從泰國回來?”齊識說到。
“你怎么知道?”白娜瞪著眼睛一頭霧水。她清明假期去泰國玩這件事,并沒有告訴任何同事。
“你臉明顯比節前黑了,說明去了熱帶地區玩。你戴的耳環以前沒看到過,說明是假期新買的。耳環上刻著大象……”,齊識停下敲代碼的手,回過頭,“說明你去了視大象為國寶的泰國。”
“你……牛……,不愧是程序員名偵探……”。
“說吧,又出什么事了?”齊識回到顯示器前繼續敲代碼。
“還是用戶被踢出去的那個問題。”
“上次不是已經查清楚了嗎?是低版本IE的鍋。”
“可是,這次是IE 11……”
“哎——”齊識長嘆一聲,“好吧,把郵件轉給我。”
“已經轉給你了。拜托了,我去開會了哈。”
齊識很不情愿地將界面從IDE切換到網頁,打開了郵件。這是一封幾經轉發的郵件,一開始是路老板的用戶向路老板抱怨系統用著用著就自動登出了你們這什么爛系統,路老板回復說可能是低版本IE的鍋爸爸請考慮升級IE或換其他瀏覽器。客戶說老子TM用的就是最新版IE你是不是在玩老子,于是路老板轉發給項目經理白娜說今天必須解決否則炒了你們整個團隊。最后白娜轉發給齊識,只有三個字母:FYI……
很麻煩。這個問題以前也出現過幾次,一直沒有找到具體原因。上一次時發現用戶用的是IE 8,而當時系統支持的IE最低版本是9。當所有人都焦頭爛額束手無策的時候,這位超級英雄背鍋俠IE站了出來。果不其然當用戶換了其他瀏覽器后,就再也沒有出現過類似問題了。
但這次,背鍋俠說這鍋老子不背了。
齊識看了看表,上午11點。不知道要為這個問題加班到幾點。
他先打開了日志分析工具,按照用戶名查詢與該用戶有關的日志。永遠不要相信一個不懂電腦的用戶所描述的事實,所有的信息都要再次確認。但這次事實似乎很無情無義無理取鬧,這個用戶用的確實是IE 11。
通過日志,可以看到這個用戶所有的訪問記錄:他先是登錄系統,打開幾個頁面,然后到了文件管理子系統,找到某個文件夾,上傳了一個文件,然后就登出了。如果用戶描述屬實的話,這次登出請求不是他主動觸發的,是系統自動把他踢出去了。
“用著好好的,怎么就被踢出去了呢?”齊識自言自語道。
“怎么了大偵探,愁眉苦臉的。”老夏端著泡了枸杞的保溫杯路過。現在是11點半,這杯應該是第三泡。老夏以前也是項目上的大牛,后來不知怎的,去了培訓部專門做員工培訓了。不過他時不時還是會到研發部這邊溜達溜達,從背后窺探一下別人的屏幕,指出代碼上的一些問題,然后哈哈一笑揚長而去深藏功與名。
“用戶又被自動踢出去了。”齊識說。
“又是那個問題?不是IE的鍋嗎?”
“看來我們有可能冤枉IE了,這次用戶用的是新版本IE,按理說我們的前端是肯定支持的,也在測試環境驗證過無數遍了。”
“這就怪了。這幾次出問題的用戶,是不是都用的IE?”
“是的。”
“那就還是IE的鍋。”老夏吸溜了一口枸杞茶,走了。
他看似不經意的一句話,反倒提醒了齊識。他立馬繼續查詢日志,找出幾個用不同瀏覽器的用戶,他們的各種操作錯落有致,最后的登出請求也并無破綻。只有一些IE用戶,他們的登出請求是在上傳完文件之后發生的。難道是上傳文件的后臺接口出了問題?但不同瀏覽器產生了不同的行為,這現象又不像是后臺的事兒啊。齊識一看表,已經12點半了。得了,先去吃飯吧。
1
……權衡點滴證據,做出不同的假設,把它們進行對比,最后再確定哪幾點是重要的、哪些是不真實的…… ——《巴斯克維爾的獵犬》
草草吃完,買了杯咖啡,齊識又坐到了電腦前。像這種線上問題,尤其是本地無法復現的線上問題,是他最喜歡的。定位這些問題的過程,就像是偵探在探案,從最初的案件出發,將看似毫無關聯的線索逐一梳理過濾,那最終唯一的真相也會慢慢浮出水面。每到這時,齊識就會感覺像是個超級英雄一樣拯救了世界。這樣的問題解決得多了,同事們給他起了個外號,叫“程序員偵探”。他倒也不介意別人這么叫他,反倒有點自豪。
但這次用戶被踢出的問題,齊識前前后后處理過三次,都無法定位。按照目前系統的實現,用戶自動登出可能是因為網站的登錄cookie過期且SSO上的登錄cookie也過期,這時當某個請求到達后臺時,會清空所有與認證有關的cookie并重定向到SSO的登錄頁面。登錄cookie的有效期是20分鐘,但用戶抱怨的是剛登錄不久就被自動踢出,從日志上看也的確如此。所以“登錄cookie過期”的不在場證明相當完美。
另外的可能就是緩存掛了。每個請求到達后臺時,都會到服務器緩存中取出在用戶登錄時存儲的一個token,將之與請求所攜帶的cookie中的token比較,如果不相符就自動登出。之所以這樣做是考慮用戶的安全,將偽造或竊取cookie登錄的黑客拒之門外。如果存儲或讀取緩存失敗,自然也會自動登出。齊識以前在讀寫緩存的地方加了很詳細的日志,并沒看到任何錯誤發生。“緩存”作案的可能性也不大。
最后一個嫌犯是心跳請求。網站前端每隔2分鐘會自動向后臺發一個心跳請求,如果服務器發現本次心跳與上一次心跳間隔時間超過3分鐘,就認為用戶已處于不活躍狀態,自動將其登出。這么做也是為了用戶安全,比如將所有網站頁面關閉,3分鐘后再次打開,將會自動跳轉到登錄頁面。如果心跳請求沒有發送成功,下次請求到來時很可能已經超過了3分鐘,就會把用戶踢出去。心跳請求可以在Web服務器的日志中查到,每次請求都是成功的。所以,“心跳”的嫌疑似乎也撇清了。
就在上一次處理這個問題時,齊識偶然發現請求日志里顯示的UserAgent是網站不支持的IE 8。使用系統不支持的瀏覽器,任何詭異的事情都可能發生。當用戶升級了IE或使用了其他瀏覽器后,問題不再發生了。但是這一次,為什么新版的IE也不行了呢?
現在,齊識的目光集中在了上傳文件的后臺API上。這是這次發現的新嫌疑人,不,還有IE,可能是團伙作案。現在掌握的最新線索是,用戶使用IE,不管什么版本,在上傳完文件后,被自動登出。對于IE這種慣犯,不容易找出它的破綻,所以齊識決定先從API下手。
2
在沒有事實作為參考以前妄下結論是個很大的錯誤。主觀臆斷的人總是為了套用理論而扭曲事實,而不是用理論來解釋事實。——《波希米亞丑聞》
兩個小時過去了,還是沒有任何進展。上傳文件的API若無其事地待在那里,一臉蔑視地看著齊識。
“有新的線索嗎?”老夏又端著保溫壺過來了。現在是下午3點,壺里應該泡上了新茶。老夏喜歡在午飯后泡一壺釅茶。
“今天的普洱味道如何?”齊識問。
“不是普洱,是正山小種。”
“咦?紅茶應該是周四泡的啊,今天周三應該是普洱才對。”
“這個嘛,突然就想喝紅茶了,呵呵。”老夏說完吐了吐茶沫。“你有不錯的洞察力,總是能發現別人不太在意的事情。但有時候,根據那些事實并不能推理出你的結論。有些事情之間的關聯可能很偶然,并不具備規律性。”老夏微笑地看著齊識,“現在情況如何?”
“我現在在看上傳文件的API。我發現每次用戶調用完這個API之后就會被踢出去。”
“哦?有意思,這個API做了什么見不得人的事了嗎?”
“并沒有。我已經檢查了它和它所調用的所有方法,甚至還看了它本身的filter和全局filter,并沒有任何地方會清空cookie或者token緩存。只有清空這些才會自動登出。”
“對了,你是怎么發現調用完這個API后會被踢出的?”老夏接著問。
齊識調出了某個用戶所有請求日志,定位到上傳文件的請求。“你看,這個POST請求是上傳文件,后面這些就是登出請求了,而前面的都是一些文件夾跳轉的請求。這是其中一個用戶的請求,這里還有其他用戶。”說著,齊識又開了幾個窗口,平鋪在了顯示器上。
“有意思。”老夏瞇縫著眼睛緊盯著屏幕。“雖然他們都是上傳完文件就被踢出了,但能確定就是上傳文件的API導致的嗎?是每次上傳完文件都會登出嗎?”
“還真不是!”
“而且別忘了IE,只有IE才會踢出用戶不是么?那就更能洗清后臺API的嫌疑了。”
“對啊老夏,我怎么沒有想到?”齊識茅塞頓開。
“就像我連續兩周都是周三喝普洱周四喝紅茶,你認為那是某種必然。但這是你的主觀臆斷。主觀判斷一定要有事實作為依據,否則就都是臆斷。”老夏說著,拿起茶壺晃晃悠悠地走了。
“等等,老夏,別走啊,我需要你!”
“我還有事。”老夏回過頭沖齊識眨了眨眼睛說,“等你再需要我的時候我自然會出現的。”
3
他的表情不再那樣淡漠,我看到他炯炯有神的雙眼迸射出智慧和興奮的光芒。——《格蘭奇莊園》
齊識的目光又聚焦在了日志身上。如果用戶真的在上傳文件之后被踢出,一定能從日志中找到什么蛛絲馬跡。齊識又換了一個留下的操作日志較多的用戶,將日志按時間排好序。這個是登錄成功的請求,這個是進入文件管理子系統的請求,這個是進入某個文件夾的請求,這個是心跳請求,然后上傳文件,成功了,然后跳轉到其他文件夾,繼續上傳文件,又發了個心跳,看看離上次心跳的間隔,嗯,2分鐘,沒有任何問題。齊識點擊進入了下一頁日志。
又進入了另一個文件夾,還是上傳文件,成功了,然后……就登出了。這里面一定有什么貓膩。
齊識揉了揉眼睛,將所有注意力都集中到這幾行日志身上,仿佛要看穿這屏幕,看到屏幕那頭的另一個鏡像的宇宙。
用戶在15:32:26進入了第一個文件夾,15:32:45發送了第一個心跳請求,然后上傳了第一個文件,15:33:20跳轉到第二個文件夾,15:34:30上傳第二個文件,15:34:45發送了第二個心跳請求,15:35:07進入第三個文件夾,15:37:48上傳第三個文件,15:37:49,用戶被踢出。
齊識目不轉睛地盯著每個請求的發送時間,突然,他炯炯有神的雙眼迸射出智慧和興奮的光芒。用戶在15:37:48上傳第三個文件之前,丟失了一個心跳請求,這個心跳本應該在15:36:45發出來。
心跳怎么斷了?如果是心跳斷了,是必然會被踢出的,這樣是解釋得通的。但心跳怎么能斷呢?齊識打開發心跳請求的JavaScript文件,就是一個簡單的setInterval,沒有什么特別的。是什么,讓這個2分鐘的輪詢停止了呢?
老夏呢?老夏呢?這老家伙跑哪去了?他說過會在我需要的時候出現的,現在我需要你,可你人呢?我發現了重要的線索,已經鎖定了嫌犯,現在就差證據了,就差證據了。
4
排除所有不可能,剩下的那個不管多不可思議,都是事實真相。——《四簽名》
“老夏?他說今天要去幼兒園接孩子,提前下班了。”培訓部的同事說。
齊識的一臉興奮變成了一臉沮喪,他本想告訴老夏這個重要的發現,然后跟他一起找到問題的根本原因的。但老夏他居然提前下班走人了。
“你要有事,就給他打電話吧。”培訓部的同事看齊識如此低落,就提醒道。
對呀,我怎么忘了這個世界上還有電話這么神奇的存在。齊識撥通了老夏的電話,沒有人接。
“老夏,我有了重要的發現……”齊識把剛才的線索編輯成一條微信,發給了老夏。然后回到座位上接著分析。
用戶前兩次上傳文件都沒有問題,可是第三次就被踢出了。從時間軸來看,前兩次進入文件夾后,都是很快便發出了上傳文件的請求,唯獨第三次,進入文件夾之后,停留了2分多鐘才發出了請求。用戶在干什么?進入文件夾后去喝茶聊天了?那樣的話心跳沒有理由斷掉啊。
齊識啟動IE,打開開發者工具,登錄本地的系統,進入文件管理子系統,打開一個文件夾。接下來該干什么?齊識發呆了幾分鐘,一個心跳請求發送出去了。他回過神來,點擊上傳文件的按鈕,彈出了選擇文件的窗口。上傳哪個文件呢?該死,電腦里沒有PDF文件。這個文件管理系統只能上傳PDF文件,并且做了文件頭校驗,直接改后綴名是不起作用的。于是齊識只好搜了一個TXT轉PDF的在線轉換工具,丟上去一個空的文本文件,得到了一個PDF。然后切回剛剛打開的系統,選擇文件的窗口還開著。他找到轉換好的PDF,點擊按鈕。然后,然后瀏覽器就自動跳轉到了登錄界面……
什么?復現了?這怎么可能?這么輕易就復現了?發生了什么?
齊識又進入系統,上傳剛才轉換好的PDF,一切正常。
鬧鬼了?齊識把剛剛所有操作的日志拿出來看,第一次上傳文件——也就是失敗的那次——的時間,比進入文件夾晚了2分多鐘,比上次心跳請求晚了3分多鐘。按照系統的實現,超過3分鐘沒有心跳請求,后臺會認為用戶已經不活躍,將其自動登出。也就是完全復現了生產系統中用戶的問題。而在此期間,齊識是去轉換文件了。
齊識盯著日志,良久之后,哈哈哈哈地笑出了聲。
5
通過搜索,齊識很快驗證了自己的想法。在IE下,像JavaScript引發的alert窗口或file組件打開的窗口,都屬于模態窗口,它們會阻塞所有主線程中正在執行的JavaScript代碼。至于Chrome、Firefox這樣的瀏覽器,打開的并不是模態窗口,這也就是為什么只有IE頻繁報出類似的問題,其他瀏覽器則一直表現良好。當這種模態窗口一直處于打開狀態時,心跳請求就被迫中斷了,繼而在上傳成功后,被自動登出。
證據確鑿,“兇手”就是你了!
一定要把這個消息告訴老夏。齊識拿起手機,看到老夏半個小時前的一條回復:是不是上傳文件的窗口打開的時間太長了?
齊識不僅震驚,更是欽佩得五體投地。他把剛才的經過,一五一十地發給了老夏。過了一會兒老夏回復道:“哈哈哈,果然,是哪個笨蛋用戶打開了窗口3分鐘都找不到文件?等等,不對,不是3分鐘,是1分鐘!”
“沒錯,所以問題出現得還是很頻繁的。”齊識又和老夏聊了幾句,突然想起來什么,就問:“老夏,你明明不姓夏,可是為什么大家都叫你老夏呢?”
“呵呵,這次解決問題,或者叫探案的過程你感覺怎么樣?”老夏似乎有點顧左右而言他,”我看前兩天你桌上放著一本福爾摩斯探案集,想必也是個Sherlockian,那你一定聽說過那句話吧?”
世界上沒有真正完美的犯罪,其實真相一直就在我們眼前,只不過還沒有被發現。所謂推理,不過就是把重要的細節放大。
當齊識和老夏同時打出這句話時,兩個人都笑了。
6
“那個,我以前做開發的時候,大家都叫我夏洛克”。
留言列表