編程到系統(tǒng)設(shè)計(jì))
1. 面試流程與整體定位途游游戲在北京的游戲圈里算比較務(wù)實(shí)的那一類做的是棋牌和休閑游戲賽道技術(shù)上對(duì)后端的實(shí)時(shí)性、穩(wěn)定性和高并發(fā)要求都不低。我這次投的是后端開發(fā)崗整體面試走下來(lái)最大的感受是他們不怎么看八股文的記憶能力更在意你面對(duì)真實(shí)業(yè)務(wù)場(chǎng)景時(shí)能不能把技術(shù)用對(duì)地方。先說(shuō)下面試流程總共四輪加一輪HR溝通。第一輪是電話初篩大概二十分鐘主要確認(rèn)你目前在哪個(gè)城市、離職狀態(tài)、工作年限、做過什么類型的項(xiàng)目問了一個(gè)簡(jiǎn)單的技術(shù)問題——大概是Java里HashMap在并發(fā)場(chǎng)景下會(huì)有什么問題屬于熱身級(jí)別回答清楚就能過。第二輪是技術(shù)面一個(gè)多小時(shí)面試官是后端組的核心開發(fā)。這一輪問得最細(xì)從Java并發(fā)、網(wǎng)絡(luò)編程到Redis、MySQL都有覆蓋中間穿插了兩個(gè)代碼題一個(gè)偏數(shù)據(jù)結(jié)構(gòu)一個(gè)偏游戲業(yè)務(wù)場(chǎng)景設(shè)計(jì)。第三輪是技術(shù)終面面試官應(yīng)該是技術(shù)負(fù)責(zé)人級(jí)別問題更宏觀比如如果讓你設(shè)計(jì)一個(gè)跨服排行榜系統(tǒng)你會(huì)怎么做同時(shí)會(huì)對(duì)簡(jiǎn)歷上的項(xiàng)目做非常深度的追問尤其是線上故障排查和性能優(yōu)化這塊。第四輪是HR面聊薪資、到崗時(shí)間、之前的工作經(jīng)歷和離職原因沒有太多技術(shù)內(nèi)容。從我準(zhǔn)備的感受來(lái)說(shuō)游戲后端面試和互聯(lián)網(wǎng)業(yè)務(wù)后端面試有一個(gè)明顯的區(qū)別業(yè)務(wù)后端重點(diǎn)在看你對(duì)Spring生態(tài)的熟悉程度而游戲后端更看重并發(fā)編程、網(wǎng)絡(luò)通信、數(shù)據(jù)結(jié)構(gòu)和架構(gòu)設(shè)計(jì)。途游這家公司技術(shù)棧里Java占比很高Netty用得比較重Redis和MySQL是標(biāo)配所以要重點(diǎn)準(zhǔn)備的方向一目了然。如果你也想投這家公司我的建議是要在簡(jiǎn)歷里主動(dòng)把游戲相關(guān)的項(xiàng)目經(jīng)驗(yàn)放前面哪怕只是個(gè)人練手項(xiàng)目比如用Netty寫過一個(gè)簡(jiǎn)單的游戲服務(wù)器框架或者做過一個(gè)排行榜的Redis方案這類內(nèi)容在面試官眼里比一堆CRUD接口值錢得多。2. 為什么游戲后端面試和互聯(lián)網(wǎng)后端不一樣很多從業(yè)務(wù)后端轉(zhuǎn)游戲后端的同學(xué)上來(lái)最懵的一件事是面試官根本不按SpringBoot那套聊天。網(wǎng)上有個(gè)很熱的問題叫Java SpringBoot項(xiàng)目后端可以直接上手改代碼嗎放在游戲后端這個(gè)場(chǎng)景里答案往往是能改但主心骨不在SpringBoot上。游戲后端的技術(shù)重心和業(yè)務(wù)后端差異很大。業(yè)務(wù)后端核心是接口設(shè)計(jì)、權(quán)限控制、事務(wù)管理和數(shù)據(jù)流轉(zhuǎn)框架是骨架SpringBoot選對(duì)了業(yè)務(wù)代碼往里面填就行。游戲后端就不一樣核心在實(shí)時(shí)交互玩家操作要毫秒級(jí)響應(yīng)服務(wù)器要同時(shí)扛住幾萬(wàn)甚至幾十萬(wàn)人的并發(fā)在線。所以面試官聊的都是Netty的線程模型、自定義協(xié)議怎么設(shè)計(jì)、消息怎么廣播、狀態(tài)同步還是幀同步、Redis在排行榜和緩存里怎么用不踩坑。后端開發(fā)除了增刪改查還有什么這個(gè)問題放在游戲后端里答案特別豐富。我在面試中被問到過一個(gè)很典型的場(chǎng)景題設(shè)計(jì)一個(gè)全區(qū)服排行榜要能實(shí)時(shí)更新、要支持海量玩家、還要能查排名區(qū)間。這個(gè)需求核心不在數(shù)據(jù)庫(kù)的CRUD而在數(shù)據(jù)結(jié)構(gòu)選型和存儲(chǔ)方案設(shè)計(jì)。你用什么結(jié)構(gòu)存儲(chǔ)分?jǐn)?shù)ZSet當(dāng)然可以但ZSet單節(jié)點(diǎn)內(nèi)存夠不夠要不要分片排行榜是按天重置還是跨天累計(jì)同分怎么排名這些問題的深度已經(jīng)遠(yuǎn)超SQL語(yǔ)句本身了。所以準(zhǔn)備游戲后端面試思維要先轉(zhuǎn)換過來(lái)。不要一上來(lái)就刷Spring的源碼要把時(shí)間花在并發(fā)編程、網(wǎng)絡(luò)協(xié)議、數(shù)據(jù)結(jié)構(gòu)和系統(tǒng)設(shè)計(jì)這些更底層的硬功夫上。不是SpringBoot沒用而是游戲服務(wù)器往往是長(zhǎng)連接通信業(yè)務(wù)邏輯在自定義協(xié)議層就開始處理了SpringBoot更多是負(fù)責(zé)管理后臺(tái)和日志監(jiān)控這類輔助模塊。這里還要提一個(gè)容易踩的坑簡(jiǎn)歷上千萬(wàn)不要把游戲后端經(jīng)驗(yàn)寫成基于SpringBoot的某某系統(tǒng)面試官一看就覺得你做的不是真正意義上的游戲服務(wù)器。哪怕你確實(shí)用了SpringBoot管理后臺(tái)也要把通信層、邏輯層、存儲(chǔ)層分開寫清楚重點(diǎn)突出Netty和自研協(xié)議的部分。3. 技術(shù)知識(shí)考察全拆解3.1 Java并發(fā)編程是重頭戲途游的第二輪技術(shù)面Java并發(fā)這塊問得非常細(xì)不是問你synchronized和ReentrantLock有什么區(qū)別這種表面題而是直接丟給你一個(gè)場(chǎng)景一臺(tái)游戲服務(wù)器要支持兩萬(wàn)玩家同時(shí)在線玩家之間會(huì)發(fā)生大量的異步消息交互你怎么設(shè)計(jì)線程模型這個(gè)問題背后考的是線程池參數(shù)設(shè)置、任務(wù)隊(duì)列的選擇和線程安全的數(shù)據(jù)結(jié)構(gòu)。我當(dāng)時(shí)回答的思路是先分析場(chǎng)景特點(diǎn)游戲消息的特點(diǎn)是短、頻、快每條任務(wù)的執(zhí)行時(shí)間可能只有幾毫秒但數(shù)量非常大而且不同玩家之間的消息不允許互相阻塞。基于這個(gè)特點(diǎn)線程池的核心線程數(shù)和最大線程數(shù)不適合設(shè)置得太大隊(duì)列要選擇有界隊(duì)列拒絕策略要配合消息重發(fā)機(jī)制而不是簡(jiǎn)單丟棄。面試官追問道如果某個(gè)玩家的消息處理邏輯里不小心寫了一塊耗時(shí)很長(zhǎng)的數(shù)據(jù)庫(kù)操作導(dǎo)致線程池隊(duì)列堆積你怎么辦這個(gè)問題問得好因?yàn)檎鎸?shí)業(yè)務(wù)里這種問題太常見了。我的回答是把耗時(shí)操作異步化用CompletableFuture或者消息隊(duì)列把DB操作扔到單獨(dú)的線程池里執(zhí)行游戲邏輯線程只做內(nèi)存操作和狀態(tài)變更DB落盤走異步路徑。他還問了ConcurrentHashMap在什么場(chǎng)景下會(huì)出現(xiàn)線程安全問題這個(gè)很多人會(huì)答錯(cuò)。ConcurrentHashMap的單個(gè)操作是線程安全的但復(fù)合操作不是比如先get再put這種讀改寫操作在并發(fā)場(chǎng)景下結(jié)果可能不符合預(yù)期。游戲場(chǎng)景里典型的例子是玩家充值后發(fā)放道具先查余額、再扣款、再發(fā)道具這三個(gè)操作必須保證原子性否則并發(fā)下可能發(fā)兩次道具。這個(gè)問題我答到了用CAS循環(huán)或者分布式鎖來(lái)解決面試官明顯比較滿意。3.2 Netty與網(wǎng)絡(luò)協(xié)議設(shè)計(jì)游戲后端的地基第二輪面試有一半時(shí)間花在Netty上這也是游戲后端和業(yè)務(wù)后端最大的分水嶺。面試官先讓我講Netty的Reactor線程模型這個(gè)屬于基礎(chǔ)但一定要講出層次。我回答了Boss Group和Worker Group的分工Boss線程負(fù)責(zé)Accept連接Worker線程負(fù)責(zé)處理讀寫事件每個(gè)Worker線程綁定一個(gè)Selector多個(gè)Channel注冊(cè)到同一個(gè)Selector上。接下來(lái)問的是自定義協(xié)議設(shè)計(jì)。游戲里客戶端和服務(wù)器之間的通信不能像HTTP那樣一個(gè)請(qǐng)求一個(gè)響應(yīng)通常是一條TCP長(zhǎng)連接上跑很多消息所以必須有自己的一套消息格式。我當(dāng)時(shí)設(shè)計(jì)的是類似這樣的結(jié)構(gòu)消息頭四個(gè)字節(jié)表示包體長(zhǎng)度兩個(gè)字節(jié)表示消息ID一個(gè)字節(jié)表示協(xié)議版本后面跟著用Protobuf序列化的包體數(shù)據(jù)。面試官接著問如果客戶端發(fā)過來(lái)的消息長(zhǎng)度字段和實(shí)際包體不一致你怎么處理這就是經(jīng)典的粘包拆包問題答案是長(zhǎng)度字段在解碼時(shí)做合法性校驗(yàn)超出合理范圍直接斷開連接防止惡意報(bào)文攻擊服務(wù)器。他還追問了半包問題即一條消息被拆成了多個(gè)TCP分片怎么辦。這個(gè)就是Netty的ByteToMessageDecoder做的事情通過累積緩沖等讀夠一個(gè)完整包再交給業(yè)務(wù)邏輯處理。我補(bǔ)充了一個(gè)真實(shí)場(chǎng)景里的經(jīng)驗(yàn)不能用InputStream直接read因?yàn)閞ead一個(gè)字節(jié)就返回一個(gè)字節(jié)的話函數(shù)調(diào)用開銷太大必須要用批量累積再拆包的方式。Netty這關(guān)考完之后我能明顯感覺到面試官是在驗(yàn)證你到底是用過Netty還是理解了Netty。兩者的區(qū)別在于你遇到真正的高并發(fā)連接時(shí)能不能解釋清楚為什么Netty比傳統(tǒng)的BIO模型性能高一個(gè)數(shù)量級(jí)。3.3 Redis在游戲業(yè)務(wù)里的深度應(yīng)用Redis在游戲后端里扮演的角色比一般業(yè)務(wù)系統(tǒng)要重得多因?yàn)榕判邪?、抽?jiǎng)、簽到、活動(dòng)配置、熱點(diǎn)數(shù)據(jù)緩存全都要靠它。面試官針對(duì)Redis的提問非常貼合游戲場(chǎng)景。最典型的問題是排行榜。我問到的是ZSet的應(yīng)用比如斗地主玩家的積分排行。ZSet能保證分?jǐn)?shù)有序但是有一個(gè)坑如果積分相同是按照成員名稱的字典序排列的這不一定符合業(yè)務(wù)需求。我自己踩過這個(gè)坑所以主動(dòng)提了解決方案把積分一個(gè)足夠隨機(jī)的序列號(hào)拼成一個(gè)復(fù)合分值保證同分時(shí)順序也不至于太死板或者直接用時(shí)間戳參與排序邏輯。面試官聽完點(diǎn)頭認(rèn)可還問了一個(gè)延伸場(chǎng)景如果排行榜要按賽季重置怎么辦方案是用兩個(gè)Key當(dāng)前賽季的Key和上賽季的Key賽季結(jié)束時(shí)先把當(dāng)前Key的數(shù)據(jù)歸檔再啟用一個(gè)新的Key。另一個(gè)問得比較多的是緩存一致性。游戲里玩家信息、背包物品是高頻訪問的數(shù)據(jù)不能每次都查數(shù)據(jù)庫(kù)但是緩存和數(shù)據(jù)庫(kù)之間怎么保證一致性是經(jīng)典難題。我給出的方案是Cache Aside模式讀請(qǐng)求先查緩存緩存不存在再查DB并回填寫請(qǐng)求先更新DB再刪除緩存。面試官問了一個(gè)很尖銳的問題如果你更新了DB還沒來(lái)得及刪緩存這時(shí)候有讀請(qǐng)求過來(lái)讀到的還是舊數(shù)據(jù)怎么辦我的回答是加一個(gè)短TTL做兜底比如緩存設(shè)置8秒過期極端情況下最多有8秒的臟讀窗口在游戲業(yè)務(wù)里可以接受。Redis持久化策略也被問了游戲里掉數(shù)據(jù)是重大事故。RDB適合做冷備和快速恢復(fù)AOF則能保證更強(qiáng)的數(shù)據(jù)可靠性建議同時(shí)開啟AOF刷盤策略用everysec平衡性能和可靠性。這個(gè)回答比較常規(guī)但面試官額外問了一句AOF文件越來(lái)越大會(huì)不會(huì)影響性能我知道他想要的是AOF重寫機(jī)制所以補(bǔ)充了Rewrite相關(guān)思路。3.4 數(shù)據(jù)庫(kù)與分庫(kù)分表玩家數(shù)據(jù)怎么存游戲數(shù)據(jù)庫(kù)設(shè)計(jì)和傳統(tǒng)業(yè)務(wù)數(shù)據(jù)庫(kù)設(shè)計(jì)的最大區(qū)別在于數(shù)據(jù)模型的拆分邏輯。途游的面試官問的是幾百萬(wàn)注冊(cè)玩家的數(shù)據(jù)你怎么設(shè)計(jì)存儲(chǔ)我的方案是玩家ID做分片鍵。按照玩家ID哈希取模分庫(kù)比如分成16個(gè)庫(kù)每個(gè)庫(kù)再按ID段分表。這樣的好處是同一個(gè)玩家的所有數(shù)據(jù)都落在同一張表里不需要跨庫(kù)查詢。面試官追問跨服玩法怎么辦比如全服天梯榜不可能是分片后每片獨(dú)立排名因?yàn)榕琶侨值摹N一卮鹆擞肦edis ZSet維護(hù)全局榜單DB只做異步落庫(kù)這樣讀性能高寫壓力也分散。還有一個(gè)有意思的問題玩家身上動(dòng)輒幾百個(gè)字段比如金幣、道具、成就、任務(wù)進(jìn)度是一張超寬表還是多張窄表我的答案是寬表加JSON字段混合使用核心數(shù)值字段要單獨(dú)建列方便查詢和加索引低頻和結(jié)構(gòu)多變的字段用JSON存。這個(gè)方案在面試中得到了認(rèn)可因?yàn)橛螒蛲婕业淖侄巫兓l繁如果每次加一個(gè)道具類型就要做一次ALTER TABLE開發(fā)效率會(huì)非常低。MySQL這塊還被問到主從延遲如何處理。游戲里玩家充值后立刻查余額因?yàn)橹鲝膹?fù)制延遲可能查到舊值。我的方案是強(qiáng)制讀主庫(kù)或者提供一個(gè)寫后讀一致的標(biāo)記玩家寫完數(shù)據(jù)后一定時(shí)間內(nèi)的讀請(qǐng)求都走主庫(kù)。面試官又追問了死鎖問題我用一個(gè)具體案例說(shuō)明兩個(gè)玩家同時(shí)進(jìn)行道具交換一條SQL先更新玩家A再更新玩家B另一條SQL先更新玩家B再更新玩家A如果并發(fā)執(zhí)行就會(huì)死鎖。解決方案是按照玩家ID排序后再依次更新保證鎖順序一致。4. 項(xiàng)目深挖與線上排查實(shí)錄第三輪面試是項(xiàng)目深挖這輪是最容易翻車也是最能拉開差距的一輪。面試官會(huì)拿著你簡(jiǎn)歷上的項(xiàng)目逐行追問細(xì)節(jié)如果你只是參與過某個(gè)項(xiàng)目但對(duì)核心技術(shù)點(diǎn)講不出所以然很快就會(huì)被識(shí)破。我簡(jiǎn)歷上寫了一個(gè)用Netty實(shí)現(xiàn)的游戲服務(wù)器框架面試官針對(duì)它問了三個(gè)問題。第一個(gè)問題是客戶端斷線重連后服務(wù)器上的玩家狀態(tài)應(yīng)該怎么恢復(fù)這個(gè)問題的核心在于內(nèi)存狀態(tài)和持久化狀態(tài)如何同步。我的設(shè)計(jì)是玩家上線時(shí)加載全部數(shù)據(jù)到內(nèi)存游戲過程中所有操作都直接改內(nèi)存同時(shí)通過異步任務(wù)定期把臟數(shù)據(jù)落庫(kù)。斷線時(shí)TCP連接斷開不立即清理內(nèi)存中的玩家對(duì)象而是保留一段合理時(shí)間比如60秒。如果玩家在這段時(shí)間內(nèi)重連直接復(fù)用內(nèi)存對(duì)象恢復(fù)速度極快。超過時(shí)間才會(huì)清除內(nèi)存并落庫(kù)歸檔。面試官很認(rèn)可這個(gè)方案因?yàn)槭∪チ嗣看螖嗑€都重新加載數(shù)據(jù)的開銷。第二個(gè)問題是服務(wù)器突然宕機(jī)內(nèi)存里有幾萬(wàn)玩家數(shù)據(jù)還沒落庫(kù)你怎么辦這就是經(jīng)典的崩潰恢復(fù)問題。我的方案是兩層保障第一層是Redis玩家關(guān)鍵數(shù)據(jù)比如金幣、等級(jí)等寫操作同時(shí)更新RedisRedis的可靠性由AOF保障宕機(jī)后能從Redis恢復(fù)大部分?jǐn)?shù)據(jù)第二層是數(shù)據(jù)庫(kù)的binlog如果Redis也丟失了可以通過binlog重放恢復(fù)。面試官追問了兩層數(shù)據(jù)不一致怎么辦我回答以數(shù)據(jù)庫(kù)為準(zhǔn)Redis失敗時(shí)重新從DB加載并回填。這個(gè)思路是對(duì)業(yè)務(wù)后端的數(shù)據(jù)鏈路設(shè)計(jì)的一次完整驗(yàn)證。第三個(gè)問題是線上接口突然變慢你怎么排查我按照CPU、內(nèi)存、IO、網(wǎng)絡(luò)四個(gè)方向逐一展開。首先用top命令看CPU使用率如果是CPU打滿用jstack抓線程快照分析是GC線程還是業(yè)務(wù)線程導(dǎo)致的如果是內(nèi)存問題用jstat看GC頻率和堆內(nèi)存使用情況必要時(shí)增加堆內(nèi)存或者優(yōu)化對(duì)象創(chuàng)建如果是IO問題看數(shù)據(jù)庫(kù)慢查詢?nèi)罩居袥]有全表掃描或者緩存失效導(dǎo)致的雪崩。面試官對(duì)緩存雪崩特別感興趣追問了熱點(diǎn)緩存同時(shí)過期怎么辦我給出的方案是過期時(shí)間加隨機(jī)擾動(dòng)同時(shí)用分布式鎖做緩存重建避免大量請(qǐng)求同時(shí)打到數(shù)據(jù)庫(kù)。整個(gè)項(xiàng)目深挖的節(jié)奏很快但核心邏輯是一致的面試官想看到的是你的代碼出過問題嗎你分析過為什么出問題嗎你怎么避免它再次發(fā)生嗎如果你沒有線上故障處理的真實(shí)經(jīng)驗(yàn)至少要在準(zhǔn)備時(shí)把簡(jiǎn)歷上每個(gè)模塊的異常場(chǎng)景都過一遍想想如果遇到極端情況你怎么辦。5. 手撕代碼與場(chǎng)景設(shè)計(jì)題5.1 數(shù)據(jù)結(jié)構(gòu)題別只刷LeetCode Hot 100途游的代碼題不是特別偏難怪但和業(yè)務(wù)貼合度很高。第二輪面試的第一個(gè)代碼題是給定一個(gè)非負(fù)整數(shù)數(shù)組和一個(gè)目標(biāo)值找到數(shù)組中是否存在兩個(gè)數(shù)的和等于目標(biāo)值。這道題一看就是Two Sum的變體但我當(dāng)時(shí)沒有直接上HashMap而是問了面試官一個(gè)問題數(shù)組是排好序的嗎因?yàn)槿绻桥判驍?shù)組可以用雙指針空間復(fù)雜度O(1)如果不是再考慮HashMap的O(n)方案。這個(gè)先問清楚需求再動(dòng)手的習(xí)慣在游戲后端開發(fā)里很關(guān)鍵因?yàn)楹芏鄷r(shí)候產(chǎn)品給的需求是有坑的直接開寫容易返工。面試官對(duì)我這個(gè)反應(yīng)是認(rèn)可的說(shuō)明見過真實(shí)業(yè)務(wù)。第二個(gè)代碼題是設(shè)計(jì)一個(gè)發(fā)牌系統(tǒng)確保每次發(fā)牌的結(jié)果不完全打亂牌堆實(shí)際上就是洗牌算法。我寫的是Fisher-Yates洗牌從數(shù)組末尾開始每次隨機(jī)選一個(gè)位置交換。這個(gè)算法的關(guān)鍵是隨機(jī)數(shù)的邊界不要寫錯(cuò)否則會(huì)引入偏差。寫完后面試官追加了一個(gè)問題如果玩家量很大每秒鐘要發(fā)幾千次牌每次都要打亂一副54張的牌性能瓶頸在哪我回答瓶頸主要在隨機(jī)數(shù)生成和數(shù)組拷貝上可以用預(yù)生成的隨機(jī)數(shù)池以及復(fù)用同一個(gè)牌數(shù)組對(duì)象來(lái)避免每次new數(shù)組。這種追問才是游戲后端面試代碼題的真正目的不是看你會(huì)不會(huì)寫而是看你寫的代碼放到高并發(fā)的游戲環(huán)境里會(huì)不會(huì)把服務(wù)器拖垮。5.2 場(chǎng)景設(shè)計(jì)題排行榜與跨服架構(gòu)第三輪面試有一個(gè)大場(chǎng)景題讓我設(shè)計(jì)一個(gè)跨服排行榜這里我展開講一下完整的答題思路。先明確需求多個(gè)服務(wù)器比如20個(gè)服的玩家都要參與同一個(gè)排行榜榜單實(shí)時(shí)更新玩家能查看自己的排名和前后50名的積分情況。這個(gè)需求卡在跨服上因?yàn)椴煌耐婕覕?shù)據(jù)存儲(chǔ)在不同的分片里如果每秒鐘有上萬(wàn)次積分變動(dòng)同步成本會(huì)非常高。我的方案分了三層。第一層是在各服內(nèi)部做本地排名即每個(gè)服的服務(wù)器在內(nèi)存里維護(hù)一份本服玩家的ZSet這個(gè)更新是毫秒級(jí)的因?yàn)椴簧婕熬W(wǎng)絡(luò)開銷。第二層是異步同步每秒鐘把本服積分變化超過閾值的前N名玩家數(shù)據(jù)上報(bào)到全局排行榜服務(wù)同步用Kafka做異步消息避免直接阻塞本地游戲邏輯。第三層是全局排行榜服務(wù)它維護(hù)一個(gè)全局ZSet用玩家ID做唯一標(biāo)識(shí)值是一個(gè)全局分?jǐn)?shù)。查排名的時(shí)候先查全局ZSet得到大致的名次區(qū)間再結(jié)合本服排名做精確校準(zhǔn)。面試官問了一個(gè)很現(xiàn)實(shí)的問題如果A服上報(bào)數(shù)據(jù)延遲了導(dǎo)致全局榜單不準(zhǔn)怎么辦我回答榜單本身允許秒級(jí)延遲玩家看到的排名是最終一致而不是強(qiáng)一致的這在游戲業(yè)務(wù)里完全可以接受。他又問如果全局Redis掛了怎么辦我的方案是本地榜單不依賴全局服務(wù)玩家玩的還是單服玩法不會(huì)中斷全局榜單會(huì)記錄最近一次成功同步的時(shí)間戳恢復(fù)后從該時(shí)間點(diǎn)開始補(bǔ)拉數(shù)據(jù)。這個(gè)回答把容災(zāi)設(shè)計(jì)也覆蓋了整道題答下來(lái)面試官頻頻點(diǎn)頭。5.3 場(chǎng)景設(shè)計(jì)題一個(gè)吵架系統(tǒng)的反思中途有一個(gè)小插曲值得單獨(dú)說(shuō)。有一個(gè)場(chǎng)景題是如果玩家在游戲聊天頻道里吵架或者刷屏你怎么設(shè)計(jì)系統(tǒng)去管控這個(gè)題看似是產(chǎn)品題實(shí)際考的是規(guī)則引擎和數(shù)據(jù)流設(shè)計(jì)。我的回答用了兩個(gè)方案并舉。第一個(gè)方案是頻率控制。每個(gè)玩家有個(gè)發(fā)言計(jì)數(shù)窗口比如10秒內(nèi)最多發(fā)3條超過就觸發(fā)禁言或驗(yàn)證碼。第二個(gè)方案是內(nèi)容過濾敏感詞庫(kù)用AC自動(dòng)機(jī)做多模式匹配服務(wù)器收到消息后先在內(nèi)存里做一次過濾命中則丟棄并記錄日志。面試官追問道如果玩家用變體字或者拼音繞過過濾怎么辦我回答說(shuō)內(nèi)容過濾永遠(yuǎn)做不到百分百需要疊加人工審核和舉報(bào)機(jī)制同時(shí)要留存聊天日志用于追溯。這道題的用意在于考察你是否能設(shè)計(jì)一個(gè)低成本、高可用的功能系統(tǒng)而不是真的要做一套完美無(wú)缺的AI審核系統(tǒng)。我后來(lái)復(fù)盤覺得類似的場(chǎng)景題在游戲后端面試?yán)锍霈F(xiàn)頻率極高準(zhǔn)備時(shí)可以多練習(xí)頻率控制內(nèi)容過濾人工兜底這個(gè)組合思路。6. 復(fù)盤總結(jié)與避坑建議整場(chǎng)面試走完我最大的感受是途游游戲后端面試更看重實(shí)踐深度而非廣度。面試官不會(huì)拿“你背了多少八股文”來(lái)衡量你而是通過項(xiàng)目追問和場(chǎng)景設(shè)計(jì)題看你能否在真實(shí)的業(yè)務(wù)約束下做技術(shù)決策。以下幾個(gè)點(diǎn)是我復(fù)盤后覺得最值得分享的。第一個(gè)建議是準(zhǔn)備面試時(shí)要建立自己的面試上下文表。把簡(jiǎn)歷里每個(gè)項(xiàng)目都整理成項(xiàng)目背景-技術(shù)選型-核心難點(diǎn)-解決問題-效果量化這五段式結(jié)構(gòu)同時(shí)為每個(gè)難點(diǎn)準(zhǔn)備一個(gè)如果出問題怎么辦的預(yù)案。我這次面試有三個(gè)追問都命中了自己提前準(zhǔn)備的預(yù)案這讓我在面試過程中能比較從容地控制節(jié)奏。第二個(gè)建議是代碼題不要只刷Hot 100要結(jié)合游戲場(chǎng)景練手。比如寫一個(gè)A*尋路、寫一個(gè)環(huán)形隊(duì)列、實(shí)現(xiàn)一個(gè)時(shí)間輪定時(shí)器、用Redis ZSet做一個(gè)排行榜接口。這些題目在游戲后端面試中出現(xiàn)概率極高比盲刷各種偏題要有效得多。我自己在準(zhǔn)備時(shí)間輪算法的時(shí)候本以為用不上結(jié)果面試官問到的定時(shí)任務(wù)過期清理方案里正好用到了這個(gè)概念。第三個(gè)建議是面試中遇到不會(huì)的問題千萬(wàn)不要硬編。游戲后端面試官大多技術(shù)底蘊(yùn)很厚你說(shuō)的是不是真實(shí)經(jīng)驗(yàn)幾句話就能感覺出來(lái)。我有一道題問的是Kubernetes的Pod調(diào)度策略這塊我確實(shí)不熟就如實(shí)說(shuō)這塊我只停留在了解層面沒有實(shí)際部署過然后主動(dòng)把話題引到我熟悉的Docker容器化部署經(jīng)驗(yàn)上。這種誠(chéng)實(shí)認(rèn)短板展示長(zhǎng)板的方式反而比支支吾吾要好因?yàn)槊嬖嚬僖吹氖悄愕膶W(xué)習(xí)能力而不是你要成為一個(gè)什么都會(huì)的百科全書。最后再分享一個(gè)比較實(shí)用的小技巧面試結(jié)束后一定要在24小時(shí)內(nèi)把面試中被問到的問題和你的回答整理成文檔。一方面是為了記錄自己當(dāng)時(shí)的思路方便以后復(fù)盤另一方面如果你進(jìn)入下一輪面試這份文檔能幫你快速回憶這一輪聊了什么面試官之間往往會(huì)傳遞反饋下一輪就會(huì)在你上一輪的基礎(chǔ)上繼續(xù)深挖如果你銜接不上就會(huì)留下這個(gè)項(xiàng)目不是你自己做的的印象。如果你也準(zhǔn)備投游戲后端方向把上面這些內(nèi)容當(dāng)成一個(gè)參考坐標(biāo)但不要把它當(dāng)成標(biāo)準(zhǔn)答案。每個(gè)公司的技術(shù)棧和業(yè)務(wù)階段不同面試風(fēng)格也會(huì)不一樣但底層那些東西是通用的并發(fā)編程、網(wǎng)絡(luò)通信、數(shù)據(jù)結(jié)構(gòu)、存儲(chǔ)選型、線上排查這些硬功夫練到位了不管面試官怎么問你都能接得住。