面試3天沖刺:注冊中心、分布式事務(wù)與可靠性設(shè)計核心考點)
準(zhǔn)備 Java 微服務(wù)面試最忌諱的是拿到一堆面試題就開始背背完就忘面試時遇到一個場景題就不知道怎么組織答案。微服務(wù)是一個覆蓋分布式通信、注冊中心、配置中心、網(wǎng)關(guān)、分布式事務(wù)、熔斷限流、容器化部署等多個技術(shù)域的體系靠死記硬背無法應(yīng)付面試中“追問”環(huán)節(jié)。這篇內(nèi)容按 3 天節(jié)奏梳理微服務(wù)面試的核心模塊每個模塊都按“核心概念 高頻面試題 回答思路 常見坑”組織。第一天打基礎(chǔ)第二天抓方案設(shè)計第三天鞏固排查和實戰(zhàn)題。每部分都可以直接拿去復(fù)習(xí)和自測。1. 面試官問微服務(wù)真正想考察的四個方向1.1 微服務(wù)是什么以及為什么需要它微服務(wù)是一種將單一應(yīng)用拆分為一組小服務(wù)的架構(gòu)風(fēng)格。每個服務(wù)圍繞業(yè)務(wù)能力構(gòu)建可以獨立開發(fā)、獨立部署、獨立擴(kuò)展服務(wù)之間通過輕量級機(jī)制通信常見的是 HTTP REST 或消息隊列。面試時不能只背定義要能說清楚拆分前后發(fā)生了什么變化。單體應(yīng)用在規(guī)模不大時開發(fā)效率其實很高團(tuán)隊、代碼、數(shù)據(jù)庫、部署都在一起問題出現(xiàn)在規(guī)模變大之后代碼沖突變多、局部流量影響整體、技術(shù)棧綁定單一、發(fā)布驗證成本變高。微服務(wù)正是為了應(yīng)對這些規(guī)?;瘑栴}而出現(xiàn)不是因為它更簡單而是因為它把復(fù)雜度做了重新分配。可以這樣作答單體應(yīng)用把功能模塊放在同一個進(jìn)程里共享同一個數(shù)據(jù)庫部署時打成一個包。微服務(wù)把“按技術(shù)層劃分”改成“按業(yè)務(wù)能力劃分”每個服務(wù)包含自己的接口、業(yè)務(wù)邏輯和持久化服務(wù)之間通過網(wǎng)絡(luò)調(diào)用。它解決的是獨立演進(jìn)的問題但同時也帶來了服務(wù)發(fā)現(xiàn)、分布式事務(wù)、鏈路追蹤這些新問題。1.2 微服務(wù)和分布式的關(guān)系這道題很常見但很多人講不清楚。分布式是一個更寬泛的概念指的是多個節(jié)點通過網(wǎng)絡(luò)協(xié)作完成任務(wù)微服務(wù)是一種具體的架構(gòu)風(fēng)格它是分布式系統(tǒng)的一種落地形態(tài)。舉例來說一個系統(tǒng)用 Nginx 做負(fù)載均衡后端掛了三臺相同服務(wù)這是分布式部署但還不是微服務(wù)。只有當(dāng)系統(tǒng)按業(yè)務(wù)邊界拆分成訂單服務(wù)、用戶服務(wù)、庫存服務(wù)并且這些服務(wù)獨立部署、獨立演進(jìn)時才算真正使用了微服務(wù)架構(gòu)?;卮饡r可以補(bǔ)一句分布式關(guān)注的是“多節(jié)點如何協(xié)同”微服務(wù)關(guān)注的是“如何按業(yè)務(wù)拆分并管理這些服務(wù)”。如果項目里所有服務(wù)代碼放在一個倉庫、依賴一個數(shù)據(jù)庫、一起發(fā)布那只能說做了分布式部署不是微服務(wù)。1.3 微服務(wù)帶來的挑戰(zhàn)與解決方案面試官問完微服務(wù)解決了什么問題通常緊接著就會問帶來了什么問題。這一輪要體現(xiàn)思考深度。微服務(wù)的核心挑戰(zhàn)集中在六個方面服務(wù)發(fā)現(xiàn)與注冊服務(wù)地址動態(tài)變化客戶端怎么找到服務(wù)。配置管理幾十個服務(wù)各自有環(huán)境配置怎么統(tǒng)一管理和動態(tài)刷新。網(wǎng)關(guān)路由與鑒權(quán)統(tǒng)一入口怎么做路由、限流和認(rèn)證。服務(wù)間調(diào)用與容錯依賴的服務(wù)掛掉或變慢怎么避免雪崩。分布式事務(wù)跨服務(wù)的數(shù)據(jù)一致性如何保障。可觀測性調(diào)用鏈跨了多個進(jìn)程如何排查問題?;卮饡r建議用“問題 方案 你在項目里怎么落地”的結(jié)構(gòu)不要只羅列名詞。例如服務(wù)發(fā)現(xiàn)這塊我們用的是 Nacos服務(wù)啟動時把實例 IP 和端口注冊上去消費者從 Nacos 拉取服務(wù)列表為了防止拿到過期地址客戶端做了本地緩存同時服務(wù)端配合心跳檢查和健康檢查剔除異常實例。1.4 如何回答“你們項目為什么要用微服務(wù)”這道題一定要結(jié)合自己的項目來答不能背標(biāo)準(zhǔn)答案。推薦的結(jié)構(gòu)是項目規(guī)模團(tuán)隊人數(shù)、模塊數(shù)量、迭代頻率。遇到的單體痛點例如某個模塊發(fā)布影響全站、數(shù)據(jù)庫連接不夠、代碼合并頻繁沖突。拆分的依據(jù)按業(yè)務(wù)域拆訂單、用戶、商品分開。拆分后的效果獨立發(fā)布、獨立擴(kuò)容、故障隔離。付出了什么代價部署復(fù)雜、排查鏈路變長、事務(wù)一致性問題變多。如果項目本身規(guī)模不大直接說“我們項目并發(fā)不高、團(tuán)隊小所以沒有拆微服務(wù)而是采用模塊化單體”也是完全正確的答案。面試官真正想聽的是你有沒有判斷能力而不是你有沒有用過微服務(wù)。2. 第 1 天服務(wù)注冊發(fā)現(xiàn)、網(wǎng)關(guān)與配置中心的高頻題2.1 Nacos 和 Eureka、Consul 如何選型這個對比題幾乎是微服務(wù)面試必考。不能只說“Nacos 比較新功能全”要對比核心機(jī)制和適用場景。對比項EurekaConsulNacos服務(wù)注冊發(fā)現(xiàn)支持支持支持配置管理不支持不支持原生 KV 配置支持配置中心是核心能力一致性協(xié)議APCP基于 Raft注冊中心 AP/CP 可切換配置 CP健康檢查客戶端心跳多種檢查方式心跳 主動探測運維成本較低社區(qū)維護(hù)放緩需要獨立集群維護(hù)集成度高國內(nèi)使用廣泛Spring Cloud 集成已停更維護(hù)集成成本較高適配較好社區(qū)活躍選型思路可以這樣回答如果項目只做注冊發(fā)現(xiàn)選 Eureka 或 Nacos 都夠用如果既需要注冊發(fā)現(xiàn)又需要配置管理Nacos 能減少一套組件如果團(tuán)隊已經(jīng)有 Consul 且運行穩(wěn)定沒必要為了追新替換它。有一個細(xì)節(jié)值得補(bǔ)充Eureka 2.0 并沒有被大規(guī)模推廣所以現(xiàn)在新項目用 Nacos 的更多。Nacos 的 AP/CP 切換機(jī)制要了解默認(rèn)在臨時實例場景下是 AP在需要強(qiáng)一致的配置發(fā)布場景下使用 CP 模式。實際使用中不要讓一個集群同時承擔(dān)所有職責(zé)至少要把注冊中心和配置中心的使用場景分開理解。2.2 Nacos 臨時實例與持久化實例的區(qū)別這道題是細(xì)節(jié)題很多人只背了概念沒有理解背后的設(shè)計邏輯。臨時實例默認(rèn)注冊方式。客戶端與 Nacos 保持心跳心跳超時后服務(wù)端直接剔除實例。適合常規(guī)微服務(wù)場景因為實例狀態(tài)變化頻繁需要快速感知。持久化實例不依賴臨時心跳由服務(wù)端主動探測健康狀態(tài)。實例數(shù)據(jù)會持久化到數(shù)據(jù)庫。適合服務(wù)提供方數(shù)量固定、需要用數(shù)據(jù)庫判斷服務(wù)狀態(tài)的場景?;卮饡r要補(bǔ)一句臨時實例用的是 AP 思路犧牲強(qiáng)一致?lián)Q可用性持久化實例更接近 CP用持久化換可恢復(fù)性。項目中我們默認(rèn)用臨時實例只有在需要保留服務(wù)歷史狀態(tài)或依賴數(shù)據(jù)庫做服務(wù)治理時才考慮持久化實例。2.3 網(wǎng)關(guān)為什么要單獨一層網(wǎng)關(guān)在微服務(wù)中的定位是所有外部請求的統(tǒng)一入口負(fù)責(zé)路由轉(zhuǎn)發(fā)、鑒權(quán)、限流、白名單、日志記錄、請求聚合等功能。面試時先答職責(zé)再答為什么不能在每個服務(wù)里做。舉個例子如果鑒權(quán)邏輯散落在每個服務(wù)里新增一個服務(wù)時就要重寫一遍過濾器邏輯而且策略不統(tǒng)一時有的服務(wù)放行有的服務(wù)攔截安全邊界就破了。網(wǎng)關(guān)把橫切關(guān)注點收斂到一個獨立的組件里這是它存在的核心價值。Spring Cloud Gateway 是基于 WebFlux 和 Reactor 實現(xiàn)的響應(yīng)式網(wǎng)關(guān)底層 Netty 處理請求非阻塞模型適合 IO 密集型場景。Zuul 1.x 是 Servlet 阻塞模型性能上限較低不推薦在新項目中使用。需要重點理解 Gateway 的執(zhí)行流程客戶端 - Gateway Handler Mapping - Gateway Web Handler - 過濾器鏈 - 代理服務(wù)過濾器分為 global filters 和 gateway filters常見用途包括 StripPrefix 去掉路由前綴、RequestRateLimiter 做限流、自定義 GlobalFilter 做統(tǒng)一鑒權(quán)?;卮饡r給一個簡化配置片段spring: application: name: gateway-server cloud: gateway: routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix1這里lb://是重點它表示要使用負(fù)載均衡能力從注冊中心獲取服務(wù)實例而StripPrefix1表示把請求路徑的第一段前綴去掉。如果忘了去掉前綴服務(wù)端接收到的路徑會多一層/api很容易出現(xiàn) 404。2.4 動態(tài)刷新配置的原理配置中心不是簡單的“把配置挪到遠(yuǎn)端”更關(guān)鍵的是“配置變更后如何讓客戶端感知并生效”。Nacos 動態(tài)刷新的原理可以概括為客戶端啟動時建立長輪詢請求服務(wù)端配置發(fā)生變化時通過 HTTP 請求通知客戶端更新本地緩存。長輪詢相比純輪詢能減少無效請求相比 WebSocket 又減少了連接維護(hù)成本。Spring Cloud 中刷新配置最常見的方式RefreshScope標(biāo)記的 Bean 會在配置刷新時重新創(chuàng)建。Spring Cloud Bus MQ 廣播配置變更事件讓多個實例同時刷新。實際項目要注意不是所有配置都適合動態(tài)刷新。數(shù)據(jù)庫連接池、線程池這類創(chuàng)建開銷大的資源動態(tài)刷新可能帶來連接重建和性能抖動。推薦的做法是啟動時加載核心資源配置運行時只刷新不太敏感的開關(guān)配置。2.5 一個容易忽略的坑服務(wù)啟動成功但注冊不上面試即使不直接考也可能通過場景題引出。常見原因包括沒有引入注冊中心客戶端依賴。spring.cloud.nacos.discovery.server-addr配置寫錯。服務(wù)配置了spring.cloud.nacos.discovery.enabledfalse。網(wǎng)絡(luò)不通或防火墻攔截 8848 端口。服務(wù)啟動時注冊中心使用 namespace 隔離填錯了命名空間導(dǎo)致在控制臺看不到。排查路徑# 檢查注冊中心是否健康 curl http://localhost:8848/nacos/v1/console/health/readiness# 查看服務(wù)日志中是否有注冊成功關(guān)鍵字 tail -f logs/xxx.log | grep register如果日志沒有明顯報錯優(yōu)先檢查 namespace 和 group 是否和服務(wù)端一致。Nacos 控制臺默認(rèn)是 public 命名空間如果客戶端配置了namespacedev而控制臺沒有切換到 dev是看不到服務(wù)的這不代表注冊失敗。3. 第 2 天分布式事務(wù)、調(diào)用鏈與可靠性設(shè)計的答題要點3.1 分布式事務(wù)有哪些方案跨服務(wù)的數(shù)據(jù)一致性是微服務(wù)面試的重頭戲。先記住一個判斷標(biāo)準(zhǔn)分布式事務(wù)不是銀彈方案選型取決于業(yè)務(wù)一致性要求和團(tuán)隊運維能力。主流方案方案核心思想一致性強(qiáng)度適用場景2PC兩階段提交準(zhǔn)備 提交/回滾引入?yún)f(xié)調(diào)者強(qiáng)一致性對一致性要求極高的少數(shù)據(jù)量場景TCCTry、Confirm、Cancel 三段補(bǔ)償最終一致需要極強(qiáng)業(yè)務(wù)補(bǔ)償邏輯本地消息表本地事務(wù) 消息表 定時任務(wù)投遞最終一致小團(tuán)隊低成本實現(xiàn)可靠消息事務(wù)消息消息事務(wù)保證本地操作與發(fā)消息原子性最終一致使用 RocketMQ 時常用方案SAGA長事務(wù)拆分失敗時反向補(bǔ)償最終一致業(yè)務(wù)流程長、跨越多個服務(wù)面試模擬一個場景用戶下單后扣庫存、扣余額、生成訂單分屬三個服務(wù)要求回答怎么做。推薦回答結(jié)構(gòu)先分析業(yè)務(wù)能否接受最終一致。如果可以優(yōu)先選擇事務(wù)消息或本地消息表。例如扣庫存成功后發(fā)送“庫存已扣減”消息訂單服務(wù)消費消息生成訂單如果生成失敗通過人工或定時任務(wù)兜底回補(bǔ)。如果業(yè)務(wù)要求高一致性例如用戶支付金額不能短暫不一致需要引入 TCC但 TCC 的代碼復(fù)雜度和運維成本會明顯上升。補(bǔ)充一個 TCC 常見坑Cancel 階段必須做冪等因為網(wǎng)絡(luò)超時會導(dǎo)致協(xié)調(diào)者重試 CancelConfirm 和 Cancel 操作不能依賴上下文之外的狀態(tài)查詢最好把事務(wù)上下文通過參數(shù)傳遞。Seata 是常用的分布式事務(wù)框架項目中用 AT 模式時要注意全局鎖和分支事務(wù)會帶來數(shù)據(jù)庫資源占用高并發(fā)場景反而更容易產(chǎn)生鎖等待超時?;卮饡r可以主動提到這一點比直接說“我們項目用了 Seata”更有說服力。3.2 冪等性怎么保證常見的重試場景有哪些面試官經(jīng)常把冪等性和分布式事務(wù)放在一起問。冪等是指同一個請求執(zhí)行多次和執(zhí)行一次的結(jié)果相同。產(chǎn)生重復(fù)請求的場景包括網(wǎng)絡(luò)超時后客戶端重試。MQ 重投機(jī)制。用戶重復(fù)點擊提交按鈕。定時任務(wù)重復(fù)調(diào)度。常見實現(xiàn)方式唯一索引數(shù)據(jù)庫表中對業(yè)務(wù)單號加唯一約束重復(fù)插入直接沖突。狀態(tài)機(jī)訂單從“待支付”到“已支付”只允許單向流轉(zhuǎn)重復(fù)支付回調(diào)直接忽略。Token 機(jī)制前端提交時攜帶后端生成的一次性 token后端通過 Redis 判斷 token 是否已消費。分布式鎖用 Redis SETNX 對業(yè)務(wù)鍵加鎖鎖存在時拒絕重復(fù)請求?;卮饡r要強(qiáng)調(diào)沒有通用的冪等方案冪等鍵必須和業(yè)務(wù)語義對應(yīng)。例如支付回調(diào)冪等應(yīng)該用支付流水號作為冪等鍵而不是用戶 ID。3.3 熔斷、降級和限流的關(guān)系這是微服務(wù)可靠性三劍客很多人分不清楚??梢杂靡痪湓捀爬ㄈ蹟嘞掠喂收蠒r上游快速失敗不再繼續(xù)調(diào)用。降級系統(tǒng)資源不足時犧牲非核心功能保證核心功能。限流控制進(jìn)入系統(tǒng)的請求速率防止被流量打垮。三者經(jīng)常配合使用但觸發(fā)條件和處理方式不同。機(jī)制觸發(fā)條件處理方式示例熔斷下游錯誤比例或耗時超閾值快速失敗進(jìn)入 Open 狀態(tài)下單服務(wù)調(diào)用庫存服務(wù)連續(xù)失敗直接返回默認(rèn)結(jié)果降級資源不足、依賴不可用返回降級頁面或兜底數(shù)據(jù)廣告服務(wù)不可用返回空列表限流QPS 超過閾值丟棄請求或排隊接口每秒最多 1000 次超出直接 503回答 Sentinel 或 Hystrix 時要說明核心參數(shù)熔斷的閾值、時間窗口、最小請求數(shù)限流的 QPS 閾值和排隊超時時間。Sentinel 相比 Hystrix 在 Dashboard、規(guī)則動態(tài)下發(fā)、熔斷策略靈活性上更有優(yōu)勢是目前更常用的選擇。3.4 調(diào)用鏈追蹤的底層原理微服務(wù)排查問題難難在請求跨了多個服務(wù)日志分散在不同節(jié)點。鏈路追蹤就是為了解決這個問題。核心思想是 traceId 和 spanId請求入口生成全局唯一 traceId。每次服務(wù)間調(diào)用生成子 spanId。所有日志帶上 traceId 上下文匯入統(tǒng)一日志平臺后按 traceId 聚合。技術(shù)選型常見的是 Spring Cloud Sleuth Zipkin或者 SkyWalking。這里不需要太深入但要能說清楚上下文傳遞的機(jī)制。在 Spring Cloud 中Feign 或 RestTemplate 發(fā)起遠(yuǎn)程調(diào)用時攔截器會把 traceId 從當(dāng)前線程上下文中取出放入 HTTP Header 隨請求傳遞。因此要注意如果調(diào)用線程切換必須手動傳遞上下文否則 traceId 會斷。以 Feign 為例Lo4j2 traceId 傳遞需要保證 MDC 中的 traceId 在異步任務(wù)中也存在。面試時可以主動指出使用線程池異步調(diào)用時MDC 上下文默認(rèn)不傳遞需要自定義 TaskDecorator 或手動 put。這一點非常加分。Component public class MdcTaskDecorator implements TaskDecorator { Override public Runnable decorate(Runnable runnable) { MapString, String contextMap MDC.getCopyOfContextMap(); return () - { if (contextMap ! null) { MDC.setContextMap(contextMap); } try { runnable.run(); } finally { MDC.clear(); } }; } }代碼解釋了異步線程池應(yīng)用中 traceId 丟失的最常見修復(fù)方式完全可以直接用在項目落地里。3.5 面試高頻追問如果服務(wù)間調(diào)用超時怎么處理這個問題考察綜合能力可從以下層次回答設(shè)置超時時間Feign/RestTemplate 都要顯式配置連接超時和讀取超時不要依賴默認(rèn)值。超時后快速失敗讓上游線程立刻釋放避免線程池堆積。觸發(fā)熔斷超時比例達(dá)到閾值后進(jìn)入半開試探恢復(fù)后才放量。數(shù)據(jù)補(bǔ)償如果超時后部分步驟已成功需要通過消息或定時任務(wù)對齊最終狀態(tài)。異步化優(yōu)化非核心鏈路改為 MQ 異步通知降低同步等待時間和失敗影響面。還要提到排查順序先確認(rèn)是服務(wù)端處理慢還是網(wǎng)絡(luò)問題通過壓力測試確認(rèn)服務(wù)端閾值的合理值再根據(jù)容量評估設(shè)置超時時間而不是拍腦袋隨意填一個 5000ms。4. 第 3 天動手準(zhǔn)備可演示的微服務(wù)項目與環(huán)境排錯4.1 本地最小可運行的微服務(wù)架構(gòu)面試時能說清楚自己實際跑通過的項目效果遠(yuǎn)好于背概念。本地搭建一個最小微服務(wù)項目需要準(zhǔn)備JDK 8 或 JDK 11對應(yīng) Spring Boot 2.x如果使用 Spring Boot 3.x需要 JDK 17。Maven 或 Gradle用于依賴管理。Nacos 服務(wù)端作為注冊中心和配置中心。一個 Spring Cloud Gateway 或簡單直連調(diào)用。兩個業(yè)務(wù)服務(wù)例如 order-service 和 user-service通過 OpenFeign 調(diào)用。以 Maven 為依賴管理工具父 POM 中需要引入 Spring Cloud 版本和 Spring Cloud Alibaba 版本。Spring Cloud 與 Spring Boot 版本強(qiáng)綁定必須確認(rèn)兼容性常見組合Spring CloudSpring BootSpring Cloud Alibaba2021.0.x2.6.x2021.0.5.0 左右2022.0.x3.0.x2022.0.0.0 左右2023.0.x3.2.x2023.0.1.0 左右版本號迭代快上述只作參考落地前要去 Maven 倉庫或官方文檔再次核對。Spring Cloud Alibaba 的版本命名和 Spring Cloud 并不完全一致直接抄一個組合很容易踩坑。下面是核心依賴說明。父 POM 中加入dependencyManagement dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version${spring-cloud.version}/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version${spring-cloud-alibaba.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement服務(wù)模塊中引入 Nacos 注冊發(fā)現(xiàn)和配置中心依賴dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency這里有一個反復(fù)出現(xiàn)的坑spring-cloud-starter-alibaba-nacos-discovery必須在依賴中明確加入不能只引入spring-cloud-starter-alibaba-nacos-config否則服務(wù)無法注冊到 Nacos。4.2 環(huán)境變量與 JDK 配置問題搜索材料很多提到“java 環(huán)境變量配置”這其實不是面試題而是初學(xué)者動手搭建項目前最容易卡住的環(huán)節(jié)。面試前如果要在自己電腦上演示項目環(huán)境必須提前配好。Windows 下 JDK 環(huán)境變量配置JAVA_HOMEC:\Program Files\Java\jdk-17 Path%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;...驗證方式j(luò)ava -versionjavac -version如果java -version正常但javac找不到通常是因為JAVA_HOME沒有生效或 Path 中%JAVA_HOME%\bin被其他 JDK 路徑覆蓋。Linux 環(huán)境推薦使用export或/etc/profile添加也可以用 SDKMAN 管理 JDK 版本。這里要提醒新手不要同時安裝多個 JDK 再手動來回改配置很容易出現(xiàn)兩個版本混用導(dǎo)致的編譯或運行錯誤。4.3 服務(wù)啟動后無法調(diào)用的問題排查本地跑通微服務(wù)調(diào)用時最常遇到的錯誤是java.net.UnknownHostException。這個錯誤的直接原因一般是服務(wù)名寫錯。沒有通過注冊中心發(fā)現(xiàn)服務(wù)而直接拼接了主機(jī)名。服務(wù)沒有成功注冊。排查順序打開 Nacos 控制臺確認(rèn)目標(biāo)服務(wù)是否存在。檢查調(diào)用方是否配置了服務(wù)發(fā)現(xiàn)依賴。檢查服務(wù)提供方是否啟動了監(jiān)聽端口??凑{(diào)用方日志里是否有完整的異常堆棧。另一個高頻問題是Connection refused或Read timed out。前者優(yōu)先關(guān)注服務(wù)是否啟動、端口是否正確后者要關(guān)注網(wǎng)絡(luò)、服務(wù)端處理慢、線程池阻塞。4.4 內(nèi)存溢出問題的面試回答思路熱搜詞中出現(xiàn)了java: outofmemoryerror: insufficient memory這類問題在微服務(wù)面試中也屬于可靠性考察方向尤其是容器化部署之后更容易出現(xiàn)?;卮鹚悸贩謨蓪?。第一層是 JVM 層面先分清是堆溢出、棧溢出還是直接內(nèi)存溢出java.lang.OutOfMemoryError: Java heap space堆空間不足常見原因是對象堆積、內(nèi)存泄漏。java.lang.OutOfMemoryError: Metaspace元空間溢出常見原因是動態(tài)生成類未回收。java.lang.OutOfMemoryError: Direct buffer memory直接內(nèi)存溢出常見原因是 NIO 分配過多。java.lang.StackOverflowError不屬于 OOM是棧深度超限常見原因是遞歸無出口。第二層是容器部署層面。容器中 JVM 默認(rèn)內(nèi)存可能沒有感知容器限制導(dǎo)致進(jìn)程被 Cgroup 殺掉?,F(xiàn)在通用做法是使用-XX:MaxRAMPercentage讓 JVM 根據(jù)容器內(nèi)存自動分配例如java -XX:InitialRAMPercentage50.0 -XX:MaxRAMPercentage75.0 -jar app.jar回答問題時要體現(xiàn)排查流程先通過監(jiān)控看內(nèi)存趨勢再導(dǎo)出堆快照分析大對象和引用鏈最后定位泄漏源。4.5 3 天復(fù)習(xí)計劃給一個可執(zhí)行的復(fù)習(xí)計劃比“背八股文”更有效。階段學(xué)習(xí)內(nèi)容產(chǎn)出第 1 天上午服務(wù)注冊發(fā)現(xiàn)、Nacos 原理、服務(wù)拆分原則畫一張服務(wù)調(diào)用拓?fù)鋱D第 1 天下午網(wǎng)關(guān)路由、過濾器鏈、配置中心動態(tài)刷新本地跑通一個 Gateway 轉(zhuǎn)發(fā)請求第 2 天上午分布式事務(wù)方案、冪等方案、分布式鎖寫出一個下單場景的事務(wù)方案設(shè)計第 2 天下午熔斷、限流、降級、鏈路追蹤本地驗證 Feign 超時和重試配置第 3 天上午Feign、RestTemplate、線程池隔離、內(nèi)存排錯用 jstack 分析一次線程阻塞案例第 3 天下午綜合項目串講 場景題模擬用 1 分鐘講清項目架構(gòu)用 5 分鐘畫架構(gòu)圖4.6 面試中畫微服務(wù)架構(gòu)圖的方法熱詞中多次出現(xiàn)“微服務(wù)架構(gòu)圖”。面試能畫出清晰架構(gòu)圖是溝通能力的重要體現(xiàn)。不需要畫得多精美但結(jié)構(gòu)必須正確。推薦結(jié)構(gòu)客戶端 - Nginx可選做負(fù)載均衡 - 網(wǎng)關(guān) - 認(rèn)證服務(wù) - 訂單服務(wù)注冊到 Nacos配置在 Nacos調(diào)用商品服務(wù) - 用戶服務(wù) - 消息服務(wù) 公共組件Nacos、Sentinel、Zipkin/SkyWalking、Redis、MQ畫圖時順手標(biāo)注調(diào)用鏈路請求從哪里進(jìn)哪些調(diào)用是同步哪些是異步數(shù)據(jù)庫和緩存分別由哪些服務(wù)訪問。面試官通過這張圖基本能判斷出你有沒有真正做過微服務(wù)項目。5. 常見考點補(bǔ)充Feign、消息隊列和分布式鎖5.1 OpenFeign 怎么用有哪些坑OpenFeign 是 Spring Cloud 中聲明式 HTTP 客戶端。核心用法是定義接口并加上FeignClient(name order-service)調(diào)用方直接注入接口調(diào)用底層由動態(tài)代理生成實現(xiàn)。常見的兩個坑超時配置不生效。需要確認(rèn)是否同時配置了connectTimeout和readTimeout并且 Ribbon 或 LoadBalancer 的配置不能和 Feign 沖突。FeignClient接口上無法使用GetMapping等 Spring MVC 注解實際上可以但要注意RequestParam需要顯式聲明 value否則參數(shù)名在編譯后丟失導(dǎo)致傳參為 null。示例配置feign: client: config: default: connectTimeout: 2000 readTimeout: 3000 order-service: connectTimeout: 1000 readTimeout: 2000面試中能說出服務(wù)級配置優(yōu)先級比只寫一個 default 要好得多。5.2 消息隊列在微服務(wù)里的作用微服務(wù)里消息隊列主要解決解耦、異步、削峰三個問題。解耦訂單服務(wù)創(chuàng)建訂單后通過 MQ 通知積分服務(wù)、短信服務(wù)、庫存服務(wù)不需要在訂單服務(wù)代碼里調(diào)一堆接口。異步把非核心同步操作異步化讓核心鏈路返回更快。削峰高流量瞬間通過消息暫存由下游按消費能力處理?;卮饡r一定補(bǔ)一句引入 MQ 同樣會增加問題。消息可能丟失、重復(fù)消費、消費順序無法保證、消息積壓后系統(tǒng)負(fù)荷會平移給下游。所以使用 MQ 時至少要考慮生產(chǎn)者確認(rèn)機(jī)制。消費者手動 ACK。消費冪等。死信隊列和告警。5.3 Redis 分布式鎖的正確實現(xiàn)分布式鎖經(jīng)常和冪等、扣減庫存場景綁定出現(xiàn)?,F(xiàn)在不推薦直接寫SETNX expire兩步操作因為無法保證原子性。推薦使用 Redisson也可以使用SET key value NX EX timeout一步完成加鎖和過期時間設(shè)置。示例Boolean locked stringRedisTemplate.opsForValue() .setIfAbsent(lock:order: orderId, 1, Duration.ofSeconds(30)); if (Boolean.TRUE.equals(locked)) { try { // 處理業(yè)務(wù) } finally { stringRedisTemplate.delete(lock:order: orderId); } }但要注意釋放鎖時先比對 value 再刪除避免誤刪他人鎖。這也是面試官最常追問的細(xì)節(jié)String value stringRedisTemplate.opsForValue().get(key); if (當(dāng)前線程唯一標(biāo)識.equals(value)) { stringRedisTemplate.delete(key); }從工程角度看高并發(fā)場景更推薦 Redisson 的看門狗機(jī)制它會自動續(xù)期避免業(yè)務(wù)還沒執(zhí)行完鎖就過期了。分布式鎖不能只靠 Redis還要考慮主從切換丟鎖問題面試中能把 RedLock 的爭論提出來說明自己有深度但不要過度推銷某個方案。6. 項目代碼正確性之外還要準(zhǔn)備哪些隨手的工程亮點微服務(wù)面試不只是背題。面試官往往會根據(jù)你的項目經(jīng)歷繼續(xù)追問。準(zhǔn)備幾個能隨時講的工程實踐點效果會好很多。配置外置化不同環(huán)境使用bootstrap.yml配置 Nacos 地址業(yè)務(wù)配置統(tǒng)一放到 Nacos本地不保存環(huán)境相關(guān)密鑰。日志規(guī)范所有請求打上 traceId入口輸出請求參數(shù)出口輸出響應(yīng)狀態(tài)和耗時。統(tǒng)一異常處理使用RestControllerAdvice統(tǒng)一封裝錯誤碼避免服務(wù)間拋出的異常格式不一致。接口冪等設(shè)計寫操作接口統(tǒng)一要求前端傳requestId后端通過 Redis 做重復(fù)提交校驗。發(fā)布策略服務(wù)分批滾動發(fā)布先灰度一臺確認(rèn)無異常后放量。回答任何項目題都可以套用這個原則先說明業(yè)務(wù)場景和問題再說技術(shù)方案最后說上線后如何驗證和兜底。不要只講功能要講清楚技術(shù)選型的理由和代價。7. 快速自查清單面試前把這幾項過一遍面試前一天對照清單做一輪自查比臨時背題更靠譜。檢查項具體要求服務(wù)拆分邊界能說清某個服務(wù)為什么拆出來劃分依據(jù)是什么注冊中心機(jī)制能畫出服務(wù)注冊、發(fā)現(xiàn)、心跳剔除的時序圖網(wǎng)關(guān)過濾器能說出自定義 GlobalFilter 的邏輯和放置順序分布式事務(wù)能針對自己的業(yè)務(wù)場景給出選型對比而不是只背方案名冪等設(shè)計能回答重復(fù)請求場景下具體怎么防重超時與重試知道 Feign/Nacos/Gateway 各自超時配置項限流與熔斷能說出核心參數(shù)含義例如 QPS、熔斷時間窗鏈路追蹤能說出 traceId 傳遞機(jī)制以及異步場景丟失的處理項目亮點能穩(wěn)定輸出 3 個自己真正做過的優(yōu)化點動手能力能在 30 分鐘內(nèi)本地啟動一個最小微服務(wù) Demo并完成一次跨服務(wù)調(diào)用微服務(wù)面試的本質(zhì)是檢驗?zāi)闶欠窬邆洹安鸱?wù)、管服務(wù)、調(diào)服務(wù)、查服務(wù)”的能力。3 天時間足夠把高頻考點系統(tǒng)過一遍但真正拉開差距的是面對一個沒有標(biāo)準(zhǔn)答案的場景題時你能不能給出有取舍、有依據(jù)的方案。文章里的知識點可以作為骨架但最終要結(jié)合自己的項目經(jīng)歷組織答案。