週三上午九點,孵化部會議室。
長桌旁坐了八個人:楊帆、老陳、小雨、三個後端開發、兩個前端、一個測試。林夕坐在老陳旁邊,麵前的膝上型電腦開著,螢幕上是“星課堂”使用者模組的架構設計檔案。
“開始吧。”楊帆沒看任何人,直接說,“老陳,你負責整體架構,你先講。”
老陳站起來,走到白板前。他畫了一張微服務拆分圖——使用者服務、課程服務、報名服務、證書服務、支付服務,五個服務之間通過REST API和訊息佇列通訊。
“整體采用微服務架構,每個服務獨立部署,資料庫分庫。”老陳講得很流利,“閘道器統一鑒權,服務註冊發現用Nacos,配置中心用Apollo,監控用Prometheus和Grafana。”
楊帆在本子上記著什麽,沒抬頭:“服務拆分原則是什麽?”
“按業務領域。使用者服務管使用者資訊,課程服務管課程內容,報名服務管報名邏輯……”
“報名邏輯為什麽要獨立?”楊帆打斷,“它和課程服務耦合度很高。”
老陳頓了一下:“因為報名有複雜的規則校驗,比如先修課程、名額限製、時間衝突。獨立出來可以保持課程服務的純粹性。”
“純粹性?”楊帆放下筆,“微服務拆分的第一原則不是純粹性,而是獨立部署和迭代的能力。報名規則變化頻率和課程內容變化頻率一樣嗎?”
“不一樣,報名規則可能改得更頻繁……”
“那為什麽要讓一個高頻變化的服務,去依賴一個低頻變化的服務?”楊帆問得很直接,“如果課程服務掛了,報名服務還能工作嗎?”
會議室安靜了。老陳額頭開始冒汗。
“這個……需要重新評估。”他最終說。
“好,下一個。”楊帆看向林夕,“使用者服務,你講。”
林夕站起來,走到白板前。他深吸一口氣,開始講自己的設計。
使用者服務的核心是使用者資訊管理和登入認證。他設計了幾個關鍵點:手機號作為主登入方式,支援微信授權,密碼加鹽雜湊儲存,JWT令牌認證,令牌重新整理機製。
“安全方麵,”林夕調出一頁PPT,“我們采用多層防護:登入介麵限流防爆破,密碼錯誤次數鎖定賬戶,敏感操作需要二次驗證。所有登入和敏感操作都有審計日誌。”
楊帆抬頭:“JWT令牌失效機製怎麽處理?”
“令牌有過期時間,短令牌1小時,長令牌7天。服務端維護一個令牌黑名單,用於主動失效。”
“黑名單在哪裏?規模多大?”
“存在Redis,按月清理。按日活十萬估算,黑名單規模不會超過萬級。”
“如果Redis掛了,怎麽保證新登入使用者不受影響,但已失效令牌依然被拒絕?”楊帆追問。
這個問題很刁鑽。林夕思考了幾秒:“可以用兩級快取:Redis作為主儲存,本地記憶體快取作為降級。Redis掛掉時,新登入可以繼續,但主動失效功能暫時不可用,直到Redis恢複。”
“主動失效的場景有哪些?”
“使用者修改密碼、主動退出、管理員封禁。”
“這些場景發生的頻率?”
“修改密碼和主動退出頻率不高,封禁是管理操作,更少。”
“所以為了低頻功能,增加複雜的兩級快取設計,值得嗎?”楊帆看著他。
林夕心裏一緊。他發現自己掉進了陷阱——過度設計了。
“可能……不值得。”他承認,“也許更好的方案是:修改密碼和封禁時,直接讓使用者重新登入。主動退出可以接受短暫延遲失效。”
楊帆點點頭,在本子上記了一筆:“繼續。”
林夕接著講使用者資訊的分庫分表策略。他設計按使用者ID雜湊分片,每張表500萬資料,預留三年容量。
“分片鍵為什麽用使用者ID,不用手機號?”
“因為手機號可能變更,使用者ID不變。”
“如果業務需求要按手機號查詢使用者呢?”
“建立手機號到使用者ID的對映表,或者用ES做搜尋索引。”
“對映表怎麽分片?”
“也按使用者ID分片,保持一致。”
楊帆沒再追問,示意他繼續。
接下來的二十分鍾,林夕講完了使用者服務的完整設計:資料庫表結構、介麵定義、異常處理、監控指標、灰度發布方案。他盡量把每個細節都想清楚,但楊帆的問題總是能找到他沒考慮到的角落。
“使用者服務和其他服務的呼叫,怎麽保證最終一致性?”
“重要操作用本地事務加訊息佇列,比如使用者註冊成功後發訊息給營銷係統。”
“訊息丟失怎麽處理?”
“傳送方有本地訊息表,定時任務補償。”
“如果訊息重複呢?”
“消費方做冪等。”
“如果消費方處理成功但應答丟失,傳送方重發,消費方已經做了冪等,但業務狀態已經變化——比如使用者已經領過新人禮包,第二次重複訊息會怎麽處理?”
林夕卡住了。這個問題他沒想過。
“會……返回成功,但不做實際處理。”他試著回答。
“那營銷係統怎麽知道使用者已經領過了?”
“需要查詢狀態。”
“如果查詢狀態的介麵也掛了?”
會議室裏鴉雀無聲。所有人都看著林夕,等著他的回答。
林夕感覺後背在冒汗。他知道,這不是在刁難他,而是在逼他思考係統的邊界情況——那些真正會出生產事故的地方。
“可能需要一個更健壯的協同機製。”他最終說,“比如用分散式事務,或者把業務設計成天然冪等——禮包發放時用唯一業務流水號,重複流水號直接忽略。”
“哪個方案好?”
“第二個。分散式事務太重,業務冪等更輕量。”
楊帆放下筆:“正確。”
林夕鬆了口氣。
使用者服務講完,接下來是課程服務、報名服務、證書服務。每個負責人都經曆了類似的拷問。兩個小時的評審會,沒有人能完整回答所有問題。楊帆像個嚴格的考官,把每個設計都翻來覆去地檢查,直到找到薄弱點。
會議結束時,已經十一點半。
“今天的評審,暴露出很多問題。”楊帆做總結,“你們的設計都還停留在‘能工作’的層麵,沒到‘可靠’的層麵。接下來一週,每個人重新完善設計,重點考慮:容錯、降級、監控、可觀測性。下週三第二次評審。”
散會後,人們垂頭喪氣地走出會議室。小雨湊到林夕身邊,小聲說:“太狠了吧?我感覺自己做的是一坨屎。”
“他是對的。”老陳歎了口氣,“咱們確實想得不夠深。我以前在傳統公司,哪有這麽嚴格。”
林夕沒說話。他回到工位,開啟剛才的筆記。楊帆問的每一個問題,他都記下來了。那些問題像一張網,網住了係統的所有脆弱點。
他開始修改設計檔案。按照楊帆的思路,重新思考每一個環節:如果這個依賴掛了怎麽辦?如果這個介麵超時怎麽辦?如果資料不一致怎麽辦?
下午兩點,他正在改檔案,楊帆發來一條釘釘:“來小會議室。”
林夕心裏一緊,以為又要挨批。但走進會議室時,楊帆的表情很平靜。
“坐。”他指了指椅子,“上午的評審,感覺怎麽樣?”
“很有收獲。”林夕實話實說,“很多問題我確實沒考慮到。”
“正常。大部分人做設計,隻考慮正常流程。”楊帆說,“但係統真正考驗人的,是異常流程。99%的時間都走正常路徑,但剩下的1%,決定了係統的可靠性。”
他從包裏拿出一份列印的資料,遞給林夕:“看看這個。”
林夕接過來。是一份事故複盤報告,來自一家知名網際網路公司。標題是:“XX公司支付係統雙十一故障複盤”。
報告詳細記錄了一次重大事故:支付係統因為一個依賴服務的延遲抖動,觸發了熔斷機製,然後雪崩式崩潰,導致半小時內所有支付失敗,直接損失數千萬。
“這個事故的根因,”楊帆說,“不是技術問題,是設計問題。他們設計了熔斷,但沒有設計降級;設計了監控,但沒有設計自愈。當問題發生時,係統沒有逃生通道。”
林夕快速瀏覽報告。裏麵的很多場景,和今天楊帆問的問題很像。
“我給你的任務,”楊帆說,“不隻是做好使用者服務。我要你在一週內,為‘星課堂’設計一套完整的容災方案。包括但不限於:依賴治理、降級策略、流量排程、資料備份、快速回滾。”
“我一個人?”
“老陳會配合你,但他經驗有限。”楊帆看著他,“我看過你在技術部做的優化方案,思路很清晰。這個任務,你能做嗎?”
“能。”林夕沒有猶豫。
“好。”楊帆點頭,“另外,我聽說你在技術部時,發現過積分係統的曆史問題?”
話題轉得太突然。林夕謹慎回答:“是的,一些小問題。”
“不隻是小問題。”楊帆笑了,“周凱昨天跟我吃飯,提了一嘴。他說你很有潛力,但也提醒我,你有時候會‘鑽牛角尖’。”
林夕的心沉了一下。周凱果然在背後說了什麽。
“你怎麽看?”楊帆問。
“我覺得……發現問題就應該解決。”林夕選擇最安全的回答。
“解決的前提是,看清問題全貌。”楊帆站起身,走到窗邊,“林夕,你在公司才一個多月,已經卷進了兩件事:積分係統的事故,現在是我的容災方案設計。你知道這意味著什麽嗎?”
“不知道。”
“意味著你已經被注意到了。”楊帆轉過身,“被注意到是好事,也是壞事。好事是機會多,壞事是容錯率低。從現在開始,你做的每一件事,都會被人放在放大鏡下看。”
他走回桌前:“所以,容災方案必須做好。這不僅是技術任務,也是你的‘投名狀’——證明你有能力,也有分寸。”
林夕明白了。楊帆在給他機會,也在考驗他。
“我會做好。”他說。
“好。”楊帆看了看錶,“我還有會。材料你拿走,有問題隨時問我。”
林夕拿著那份事故報告,回到工位。他沒有立刻開始工作,而是先開啟備忘錄,更新了一條:
“楊帆:嚴格但公正,給我重要任務,也在試探我。周凱曾向他評價我‘鑽牛角尖’。”
寫完,他關掉備忘錄,開啟新的檔案,標題:“星課堂容災方案設計”。
他開始寫。第一頁,他引用了《孫子兵法》裏的一句話,不是刻意為之,而是自然浮現在腦海:
“故用兵之法,無恃其不來,恃吾有以待也;無恃其不攻,恃吾有所不可攻也。”
——不要指望敵人不來,而要依靠自己做好了準備;不要指望敵人不進攻,而要依靠自己擁有讓敵人無法進攻的防禦。
這大概就是容災方案的本質。
他繼續寫。列出所有可能的故障場景:資料庫掛、快取掛、訊息佇列掛、依賴服務掛、網路分割槽、機房故障……
然後,為每個場景設計應對策略。
窗外的天色漸漸暗下來。同事們陸續下班,隻有他還亮著燈。
晚上八點,他完成了第一版大綱。發給了楊帆。
十分鍾後,回複來了:“方向正確。繼續細化,重點在‘如何自動化發現故障’和‘如何自動化恢複’。人工響應太慢。”
林夕回複:“收到。”
他繼續工作。這個任務很重,但也很興奮。這是他第一次設計係統級的容災方案,第一次被賦予這麽大的責任。
晚上十點,手機震動。他以為是楊帆,但一看,是趙磊。
“哥們兒,聽說你調去孵化部了?那邊怎麽樣?”
“挺好的,專案新,學東西。”
“那就好。對了,跟你說個事。”趙磊發來一條語音,聲音壓得很低,“周凱今天開了個會,說要成立‘積分重構專案組’。組長是他,副組長是李工。組員名單裏……沒有你。”
林夕盯著這條訊息。沒有意外,但還是有一點說不出的感覺。
“正常,我已經調走了。”他回複。
“不是這個意思。”趙磊又發來一條,“我是說,他把你之前做的優化方案裏的很多思路,都寫進了專案計劃書。但你的名字……隻在‘參考資料’裏提了一句。”
林夕看著螢幕,很久沒動。
然後他打字:“知道了,謝謝。”
放下手機,他看向窗外。城市的夜景璀璨,無數燈光像繁星。
他想起陳啟明郵件裏的話:“周凱是個聰明人,他知道問題在哪,也知道怎麽最大化自己的利益。”
現在他明白了。周凱用他的方案,申請了預算,啟動了專案,拿走了功勞。而他,這個提出方案的人,在“參考資料”裏被輕輕帶過。
這就是職場。
但奇怪的是,他並沒有很憤怒。反而有一種奇怪的平靜。
因為他知道,自己現在在做的事——設計容災方案,跟著楊帆學技術,在全新的專案裏成長——這些比那個“參考資料”裏的名字更重要。
他重新開啟檔案,繼續寫。
這一次,他寫得更加專注。
因為這不是為了證明給誰看,也不是為了爭什麽功勞。
隻是為了把係統設計得更可靠,更健壯,更經得起考驗。
為了那99%的正常時間,也為了那1%的異常時刻。
鍵盤聲在安靜的辦公區裏回蕩,像某種堅定的心跳。
窗外的夜,很深了。
但林夕知道,有些路,就是要走夜路才能看清方向。
而那些被拿走的功勞,那些被輕輕帶過的名字,都隻是路上的石子。
真正重要的,是往前走。
一直走。