ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

请求链路全解析:从入口网关到服务间调用的排查与面试指南

请求链路全解析:从入口网关到服务间调用的排查与面试指南 前两天一个朋友找我做模拟面试他在一个 54 人共创的中大型后端项目里待了一年多业务很熟但被问到“请求链路怎么走”时居然卡在最基础的两段一段是请求从浏览器进到服务端的“入口链路”一段是服务之间互相调用的“服务间链路”。这两段恰好是多人协作项目里最容易扯皮、最容易踩坑、也最容易被面试官连续追问的地方。这篇就把这两段拆开讲透链路每一层到底做了什么、面试官追问时想听什么、以及真实项目里怎么排查和回答。1. 先说清楚请求链路是什么为什么 54 人项目里这两段最容易被问住1.1 链路的分层与边界很多初入后端的人以为“请求链路”就是从浏览器发一个 HTTP 请求后端接住、查数据库、返回 JSON。真实项目里远没这么简单。一个典型的互联网后端请求从用户点击到数据落库中间要经过 DNS 解析、接入层负载均衡、API 网关、服务框架的拦截器链、业务层、数据访问层、缓存、消息队列以及可能存在的其他微服务。全链路中每个环节都有自己的职责网关管路由和鉴权拦截器管上下文和日志Controller 只做参数映射Service 管业务规则DAO 管数据读写MQ 用来解耦和削峰。之所以要把链路拆成“段”是因为不同段的故障表现、排查工具、优化手段完全不同。入口链路出问题用户直接表现为“打不开”“登录态失效”“接口 404”服务间链路出问题表现往往是“接口转圈”“超时重试”“数据不一致”。面试官问“请求链路怎么走”其实是在验证你有没有把系统当成一个整体看待而不是只盯着自己写的那几个类。1.2 为什么偏偏是这两段54 人的共创项目意味着代码库被多个人、多个小组反复修改。实际经验中入口链路和服务间链路是最容易“各管一段、谁也不看全貌”的地方。入口链路涉及网关、前端、后端、运维多个角色任何一个环节改了配置都可能影响整条链路服务间链路涉及接口契约、序列化、超时时间、鉴权传递、链路追踪任何一方改动都可能让下游躺枪。面试官身经百战最容易从这两段问出候选人的真实水平。入口链路常问“网关做了什么”“Filter 和 Interceptor 区别”“用户信息怎么传递”服务间链路常问“Feign 超时怎么设置”“事务怎么跨服务”“如何追踪一次完整的调用”。这些问题都不是背八股能答好的必须有实际项目里踩坑、看日志、理调用链的经验。所以我才说这两段是整个项目里最容易被问住的地方。2. 第一段从 URL 到 Controller 的入口链路最容易答漏的鉴权与路由细节2.1 完整入口链路包含什么我在带项目时习惯让新人先把入口链路画出来再动手写代码。一次普通请求从浏览器发起到 Controller 收到参数至少经过以下环节DNS 解析把域名解析到接入层 IP。这里常被人忽略但面试问“全站 HTTPS 怎么做”或“DNS 劫持怎么防”时会牵扯到。接入层负载均衡常见 Nginx 或云 LB负责终止 TLS、转发 HTTP、做基础限流。API 网关常见 Spring Cloud Gateway 或自研网关负责路由匹配、统一鉴权、灰度路由、请求头改写。服务框架拦截器如果后端是 Spring Boot通常注册了多个 HandlerInterceptor做登录态校验、用户信息注入、traceId 生成。DispatcherServlet 路由通过 HandlerMapping 找到对应 Controller 方法执行参数解析、数据绑定、参数校验。Controller 方法真正进入业务代码入口此时已经拿到标准化后的入参和用户上下文。这个过程中最容易被问住的是第 3、4 步网关 Filter 和 SpringMVC Interceptor 到底有什么区别以及用户信息是怎么从网关一路传到业务层的。如果团队里有人直接用 ThreadLocal 存用户又在异步线程里用同一个上下文这里绝对是个坑。我在具体项目里见过一种很有代表性的写法网关解析 JWT 后把用户 ID 和租户 ID 放到请求头 X-User-Id、X-Tenant-Id 里后端用拦截器读取后塞进自研的 UserContext底层就是 ThreadLocal。后续 Service 层直接 UserContext.getUserId() 取值。这个方案本身没问题但它埋了三个隐患一是任何人只要绕过网关直连服务就能伪造请求头二是异步任务线程拿不到 ThreadLocal三是测试代码里容易忘掉初始化 UserContext 导致空指针。面试官问“用户上下文怎么传递、有没有安全隐患”就是冲这三个点来的。2.2 面试官真正想听的三个细节只背“Filter 先执行Interceptor 后执行”远远不够。我在复盘大量面试题后总结出入口链路三个高频追问点网关鉴权与业务鉴权的边界。好的回答是网关只做粗粒度鉴权比如是否登录、路由是否允许访问、基础限流细粒度鉴权比如某个用户是否有某条数据的操作权限必须放在业务层做因为只有业务层知道资源归属关系。如果网关做了所有鉴权权限规则变更就要动网关耦合极重多人项目里还会互相阻塞发布。请求头污染与可信来源。直连服务绕过网关时伪造 X-User-Id 就能提权。正确做法是后端服务间用内网或 mTLS 保障可信必要时在网关入口剥离客户端传入的敏感头只允许网关写入。异常处理链路。很多人只知道 Controller 里 try-catch。真正规范的项目会用 RestControllerAdvice 做全局异常处理并区分业务异常、参数异常、系统异常同时在异常响应里附带 traceId方便前端报障时快速定位全链路日志。这三个细节都能直接对应到工作场景。我面试别人时如果候选人能把“网关只做粗鉴权、业务层做细鉴权、敏感头必须由可信入口写入”讲清楚我基本能确认他真实参与过线上项目而不是只刷过面试题。2.3 实操排查一次入口链路超时/401 问题的记录有一次线上反馈某个接口偶发 401登录态明明还有效用户却要重新登录。我按入口链路逐层排查过程很典型第一步看接入层 Nginx 日志发现请求已经到达后端排除 DNS 和网络问题。第二步看网关日志发现部分请求带有 X-User-Id部分请求没有。同一个客户端按理说不应该出现这种差异。第三步看拦截器日志发现取不到用户信息时直接抛了未登录异常。第四步回头看前端请求头发现移动端和 Web 端用了两套请求库一套会自动拼接认证头另一套在 token 刷新竞态时没带上新 token。最终定位是端上问题。这个排查过程能展示“链路思维”每层日志能确认什么、不能确认什么逐层缩小范围。面试里被问“你怎么排查 401”如果能按这个步骤讲就已经比大多数候选人强了。实战中注意一点入口链路排查一定要保证日志里有 traceId 或 requestId否则多个请求日志混在一起根本无法对应。3. 第二段跨服务调用与数据访问链路分布式环境下的重点雷区3.1 从 Controller 到下游服务、DB、缓存的完整路径服务间链路比入口链路更难看懂因为一次业务操作往往要跨越多个进程。假设一个典型的订单下单动作订单服务收到请求后先查用户服务拿用户等级再调库存服务扣库存然后往消息队列发一条订单创建事件最后写订单库和缓存。这条链路的每一跳都包含服务发现通过 Nacos 或 Consul 找到下游实例列表负载均衡策略是轮询还是最小连接数。远程调用常见 OpenFeign HTTP 或 Dubbo TCP涉及连接池管理、序列化协议JSON/Protobuf/Hessian。超时与重试连接超时、读超时、重试次数、重试是否幂等。上下文传递traceId、用户信息、租户信息通过请求头或隐式传参带到下游。数据访问与缓存下游服务处理时访问 Redis、MySQL 主从库、对象存储等。这五个环节每个都可能出错而多人共创项目里最典型的“坑”是上游重试时没有幂等导致下游库存扣了两次或者下游接口新增了必填参数上游没升级全线调用失败。我在项目中为了防止这类问题要求所有服务间接口必须做三件事明确超时时间、规划幂等键、在接口文档里标注是否可能被重试。这些内容在面试中非常加分因为它们不是书本知识而是真实分布式系统的“生存法则”。3.2 事务边界与异步化这两处最容易翻车跨服务调用时“事务”是面试必问也是项目里最容易翻车的点。很多候选人被问“分布式事务怎么做”时只能背出两阶段提交、TCC、Seata 等名词但讲不清楚实际项目中怎么设计事务边界。我的经验是把事务边界严格控制在单个服务内部跨服务操作不上本地事务。比如订单服务和库存服务各有自己的数据库订单服务不能直接对库存库做事务控制。常见做法是本地事务只覆盖本服务的数据修改。把“扣库存”和“创建订单”设计成最终一致先写本地订单状态为“创建中”发送 MQ 消息给库存服务库存服务消费后扣减并回发结果订单服务通过回调或轮询更新状态。对关键操作使用事务消息或本地消息表确保消息不丢、不重复。异步化也是常见面试点。多人项目里一个订单创建后会触发发短信、送积分、更新统计等操作这些绝不能在主链路同步执行否则任何一个下游抖动都会拖垮下单接口。我会把非核心操作通过 MQ 异步化并设置好重试和死信队列。面试官若追问“异步后怎么保证数据一致”你就把本地消息表和 MQ 的 half message 机制讲清楚基本就能过关。3.3 链路追踪落地怎么用 traceId 把 54 人的调用串起来服务间链路最核心的落地工具就是链路追踪。如果没有 traceId54 人共创项目的排查效率会低得可怕一个请求跨三四个服务每台机器各自打日志人肉拼接根本没法定位问题。我在项目里采用过 SkyWalking也用过基于日志的方案。最基础但要害的是 traceId 的生成与传递网关入口生成全局唯一 traceId通过 MDC 放入日志同时放进所有下游请求的 Header下游服务从 Header 里取出并重新放入 MDC。这样只要按 traceId 搜索日志就能还原一次请求在所有机器上的轨迹。这个环节的坑也很具体HTTP 调用可以传 Header但 MQ 消费、定时任务、异步线程池都需要手工传递。很多人只给 Feign 加了统一拦截器却忘记在 MQ 消费者里恢复 MDC导致消息处理链路完全看不到日志关联。我现在的做法是封装一个线程池装饰器和 MQ 消费基类强制所有异步入口自动透传 traceId并在日志配置里固定打印这个字段。面试时能讲清楚这条“透传链路”比你背十个链路追踪名词都管用。4. 结合 54 人共创项目接口治理、文档、可观测性如何反哺面试回答4.1 多人协作下的链路“所有权”问题面试官问“请求链路怎么走”其实还想知道你在一个大型团队里是怎么协作的。54 人不是小团队代码仓、服务数量、接口数量都多最容易出现的场景是接口是 A 组写的调用方是 B 组出现问题后两边互相甩锅。解决链路“所有权”问题我见过最有效的方式就是三个明确的接口负责人、契约测试、可观测性大盘。接口负责人保证每个服务间接口都有单一 owner改动要发变更通知契约测试用 Consumer-Driven Contracts 在 CI 里自动检查参数兼容性避免上游改了字段下游不知情可观测性大盘把接口的 QPS、延迟、错误率、依赖关系画在一起谁调用谁、谁依赖谁一目了然。面试时如果你能从项目治理角度讲“为什么这个链路上线后很少出问题”会比单纯讲技术细节更能打动面试官。这说明你不只看到了技术还看到团队协同的脆弱点和解法。4.2 面试答题模板把项目链路讲出彩我最常建议候选人的是准备一个端到端的业务链路故事不要泛泛而谈。比如用“下单”这个业务串起完整链路入口用户通过 App 点下单Nginx 转发到网关网关校验 token 并生成 traceId。Controller 与 Service订单服务创建订单校验参数开启本地事务写入订单表。服务间调用订单服务通过 Feign 调用库存服务扣库存带超时与幂等键。异步化发送“订单已创建”MQ 消息触发积分、短信等非核心流程。数据访问Redis 缓存商品信息缓存未命中时查 MySQL考虑穿透与击穿。可观测性SkyWalking 中根据 traceId 查看整条调用链耗时哪个环节慢了直接定位。讲的时候按“用户看到什么 - 系统每一层做了什么 - 出了什么问题我怎么排查”这个顺序展开。面试官追问时你再补充某段的细节比如网关鉴权用什么算法、Feign 超时配了多少毫秒、扣库存的幂等键怎么设计。这套回答既有广度又有深度是典型的“点-线-面”结合。最后提醒一点真实项目里的配置值、异常案例、优化前后的数据比什么都重要。哪怕你只是改过一个超时时间只要把“为什么改、改了之后效果如何”讲清楚都算实战经验。我个人在这些年带项目的过程中最大的体会是链路思维不是背出来的是拿着日志一行一行查出来的。如果你所在的项目还没有统一 traceId建议你先和团队推动补上这件事比换框架、上中间件都更治本。下一次再有人问你“请求链路怎么走”你能把入口到服务间再到底层存储每一段说得明明白白面试也就稳了一大半。
返回列表