ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

请求链路全解析:从DNS到数据库的微服务架构细节

请求链路全解析:从DNS到数据库的微服务架构细节 1. 这条链路为什么 54 个人的项目反而说不清1. 这条链路为什么 54 个人的项目反而说不清先说个我真实的观察面试官问“请求链路怎么走”很多人第一反应是高兴——“这题我会浏览器发起请求经过 Nginx到后端服务查数据库返回 JSON”。 但只要你在这个全是 54 个人的项目里真正干过就会发现我们都会背“标准答案”可只要一被人追问最容易被问住的就是两个断面。 一个是从客户端到网关的入口段另一个是服务与服务之间那截调用链路。1.1 大家都能背出的“标准答案”细节一追就倒我记得有次面试候选人简历写得非常漂亮项目里做了消息中间件、分库分表、缓存架构。我问了一个很基础的问题“用户从 App 点了一个按钮这条请求到你们服务器之后到底经过了哪些组件你负责的部分在哪你依赖的别人的部分在哪”他想了想说“先走负载均衡然后到网关网关转发到我们的应用服务应用服务查数据库然后再返回来。”听起来没毛病。但我接着问“你们负载均衡用的什么四层还是七层四层和七层对后端的转发有什么差别”“网关这层你放了什么逻辑白名单、鉴权、限流还是说只是透传”“App 端第一次请求的时候有没有鉴权 tokentoken 怎么校验的校验失败返回什么”“服务 A 要调服务 B服务 B 的地址是怎么拿到的”问题一多人就有点卡住了。 后来我发现这不是个案。 在一个 54 人共创的场景里每个人只对自己负责的那一小块熟悉链路这东西恰恰跨越了所有人的职责边界。1.2 最容易翻车的两个断面我把这些面试场景做了个复盘最后总结出两个“重灾区”。第一段是“入口链路”从 DNS 解析、CDN 节点、接入层负载均衡到 API 网关再到会话鉴权。这段它横跨前端、运维和网关开发三个角色通常没有一个人能讲全。第二段是“服务间调用”当你进入微服务架构一个业务请求往往要串起三四个应用从服务发现、负载均衡算法、超时时间设置、重试策略、熔断降级到 trace ID 如何透传几乎每一个细节都能把候选人问住。这两段之所以容易翻车不是候选人不会写代码而是因为他们平时根本没机会完整地看一条链路。 这和 54 人协作项目天然有关系想融会贯通你得把别人的代码也读进去同时得知道别人为什么这么设计。2. 第一段“盲区”从客户端到网关入口层的每一跳都埋着面试官想要的细节2. 第一段“盲区”从客户端到网关入口层的每一跳都埋着面试官想要的细节2.1 第一跳DNS 解析、CDN 接入与四层/七层负载均衡先讲第一跳。用户在浏览器输入域名或者 App 里写了一个 URL这个请求不会直接就到服务器。它要先由 DNS 做域名解析。这里有一个常被忽略的点DNS 不只是“把域名翻译成 IP”。在企业级项目里它通常搭配 CDN、多机房、动态加速等能力。比如用户在上海DNS 解析时可能直接给你返回一个离你最近的 CDN 节点 IP而不是源站 IP。那面试官在这里会关心什么 我举几个真实的追问Q1四层负载均衡和七层负载均衡的区别四层负载均衡工作在网络层根据 IP 和端口做转发性能极高但不能感知 HTTP 请求内容。七层负载均衡工作于应用层可以解析 HTTP 头、URI、Cookie做更灵活的转发策略。很多项目实际的部署是“两层”的外层用四层负载均衡扛流量内层用 Nginx 之类的七层负载均衡做路由。你要能在回答链路时讲清楚自己项目里用的是哪种为什么这么选面试官才会认可你是真做过。Q2连接从客户端到负载均衡负载均衡和后端服务器之间长连接还是短连接一个很容易答错的点。客户端和负载均衡之间可能是长连接因为要保持会话但负载均衡和后端应用服务器之间需要根据业务选择 HTTP/1.1 keep-alive 还是短连接。如果这里设置不当会出现大量 TIME_WAIT 连接这些都是压测才见得到的坑。2.2 网关不只是一个“转发”请求到了 API 网关这一层很多人就说“网关把请求转发到后端服务”。 这个说法太单薄了。网关在一条链路里做的事情通常是这四类身份认证校验 token、签名、证书决定“你是谁你能否进到系统里”。权限校验用户角色、接口权限、数据权限通常在网关层粗粒度鉴权一次在业务层再细粒度鉴权一次。流量控制限流、熔断、降级。全局 QPS 过高时网关直接拒绝一部分请求保护下游。路由转发看看 URL 对应哪个服务然后把请求转发过去。这里还常伴随协议转换比如把外网 HTTP 转为内网 RPC 调用。如果你只说“转发”那你丢掉了 80% 的价值。我更喜欢这样描述网关是整条链路的“入口守门员”它把流量做标准化让下游服务只关注业务本身。2.3 会话、跨域和全局请求头入口段还有一个高频记忆点会话和请求头。在一些老旧系统里会话Session是存在服务端内存里的。那这时候就会出现一个问题请求第一次打到 A 节点Session 存在 A 节点的内存里第二次请求被负载均衡转发到 B 节点B 节点上没有这个 Session用户就被登出了。解决办法有三种一是会话粘滞让同一个用户的请求始终转发到同一个节点简单但有节点热点和故障风险。二是把 Session 集中存放放到 Redis 这类外部存储里节点重启也不丢。三是改成无状态用 JWT 这类 token 方案服务端不保存会话每次请求自行校验。面试官在这里还想听一个词跨域。你有过一次前端口试后端口试会发现网关如果不处理 CORS前端每次调用都会被浏览器拦下来。很多 54 人协作的项目里这个问题经常是搞了三个月才发现“前端调不通后端”其实跟业务代码一点关系都没有。2.4 入口段面试官想听到的关键词我把入口段的加分关键词统一整理一下供大家自查DNS 解析策略就近接入、TTL、DNS 缓存CDN 静态资源加速与动态请求回源四层 LB 七层 LB 的分工网关的认证、鉴权、限流、灰度发布能力token 校验采用无状态JWT还是有状态全局请求头、链路追踪 header 的透传跨域 CORS 配置预检请求的处理如果你的项目里没有 CDN那你可以直接说“我们项目因为调用量集中在特定区域没有引入 CDN这条链路是……”这样反而更真实不要为了显得高级什么都往上堆。3. 第二段“盲区”服务间调用协同链条里最容易断的那一截3. 第二段“盲区”服务间调用协同链条里最容易断的那一截如果说入口段难在对“全局组件”不熟那服务间调用这一段就是 54 人共创项目里真正的“死亡交叉口”。为什么因为服务间调用不是你一个人写的代码它至少涉及三个团队的约定调用方、服务提供方、平台组或基础设施组。接口谁来定协议谁定异常谁负责3.1 服务发现为什么不是把 IP 写在配置里在服务规模很小的时候我们经常直接在后端配置里写api-service: url: http://10.0.0.1:8080这当然能跑。但项目一旦有几十个人协作、服务节点几十个这套方案就出问题了发布时要改配置、扩缩容时 IP 会变、故障转移根本没得做。所以正规项目里通常都会引入注册中心和负载均衡。这个过程我建议你按四步讲清楚启动服务启动时把自己的 IP、端口、服务名注册到注册中心。发现调用方从注册中心拉取服务列表拿到所有可用节点。选择客户端负载均衡通过某种算法选一个节点随机、轮询、最少连接、一致性哈希。容错如果发现某个节点调用超时或连续报错从本地缓存列表里剔除它隔一段时间再探活。这里容易混淆的是“服务端负载均衡”和“客户端负载均衡”。 传统 Nginx 是服务端负载均衡代理收到请求后选一个后端。而微服务里的注册中心方案很多是客户端负载均衡因为它的路由决策发生在调用方进程内。这也是面试官非常喜欢考的一个点。3.2 调用方式的差异HTTP、RPC、消息异步服务间到底怎么调用三种方式常用你必须分清它们的适用场景。HTTP/REST 调用简单直接跨语言、跨团队调试方便。缺点是序列化开销大、性能相对低、没有复杂的服务治理能力。很多创业公司初期全用 HTTP。RPC 调用像 Dubbo、gRPC 这类框架性能高、自带服务发现、负载均衡、超时控制。缺点是需要约定 IDL/接口定义跨语言时要额外处理团队协作成本更高。消息队列异步调用发送方只投递消息不关心接收方是否立即处理。常用于订单创建后的通知、积分发放、短信发送等场景。它打断了“同步请求-响应”的链路转而变成一前一后两个流程。面试时你应该能够根据业务场景说出选择理由。比如“我们这个订单创建接口核心链路用 RPC 同步调用保证用户能立刻看到下单结果但订单完成之后的短信、积分、优惠券发放都用消息队列异步处理这样上游不关心下游的结果也不会因为下游慢而阻塞主流程。”这段回答比干巴巴背 HTTP 和 RPC 区别要加分得多。3.3 超时、重试和熔断之间的联锁关系服务间调用这一段的“隐藏深水区”是超时、重试、熔断这三个参数的配合。先说超时。 一个请求必须设置超时时间而且每一层的超时时间不能乱设。假设网关超时 5 秒下游服务 A 超时 3 秒再下游服务 B 超时 5 秒这时候就会出现一种尴尬最外层网关还在等响应但底下的服务端早就返回超时错误了。经验做法是整体超时时间是个“漏斗”上游必须大于下游的总和还要留出缓冲。再说重试。 很多项目一遇到超时就想重试但重试是双刃剑。如果下游已经压力过大你一重试反而把流量加倍打过去引发“重试风暴”。 我见过一个真实事故晚上高峰期数据库慢查询接口超时调用方设置了 3 次重试结果本来 10% 的异常被放大了 3 倍直接把数据库打挂了。安全的重试姿势是这样的只在幂等接口上做重试比如查询接口或者带唯一订单号的写接口。设置全局重试上限比如最多 1~2 次。重试前加一点退避时间比如等待 100ms、200ms不要瞬间重试。配合熔断器当失败率达到阈值直接短路后续请求快速失败不再重试。面试时你如果能讲到“重试风暴”和“幂等控制”说明带过真实事故的脑子这跟平时背八股文完全不一样。3.4 上下文传递让一条请求在多个服务里“留痕”54 人共创项目里线上出了问题最可怕的不是技术的复杂度而是“找不到问题发生了什么”。 为什么因为一条业务链路可能经过了 4 个服务每个服务各打各的日志你怎么把这几份日志串起来答案是 trace ID也就是链路追踪 ID。整个过程一般是这样的客户端或网关首先接收请求生成一个全局唯一的 trace ID。请求头里带上这个 trace ID比如X-Request-Id。服务 A 调用服务 B 时把 trace ID 透传过去B 再调用 C 时继续透传。每层日志输出时都把 trace ID 跟着打出来可以使用日志框架的 MDC 概念自动把 traceId 放到日志上下文中。在链路追踪系统比如 SkyWalking、Zipkin、Jaeger里通过 trace ID 能看到整条调用链的耗时分布哪个环节慢了一眼就能看出来。我在面试里最喜欢问这句话“你们线上查一个慢请求怎么定位是哪个环节慢的” 如果候选人只能回答“看日志搜关键词”说明他缺少链路追踪工具的实践。真正的链路追踪工具能给出一个时间线包含网关耗时、服务 A 耗时、服务 B 耗时、数据库耗时甚至精确到某个方法。这一段的通用套路值得背下来链路 ID 生成 → 中间件自动透传 → 日志自动采集 → 可视化展示。不要觉得这是“加分项”在 54 人协作的微服务项目里这已经是标配了。4. 数据读写这一段缓存、数据库和消息队列的编排4. 数据读写这一段缓存、数据库和消息队列的编排很多候选人答请求链路讲到“查数据库”就停了。但一个真实的业务请求数据读写这一段往往比想象中复杂得多先查缓存缓存没有才查数据库写完数据库要更新缓存还可能发一个消息出去异步触发后续动作。4.1 缓存放在链路中的什么位置最常见的链路是这样请求先到应用服务业务代码查询缓存。缓存命中直接返回不打数据库。缓存未命中查询数据库拿到结果后回填缓存再返回。这个“先查缓存再查库”的顺序看似简单但面试官至少有三个追问点。追问一缓存穿透。如果查询一个根本不存在的数据缓存永远没有每个请求都会打到数据库这就是穿透。解决方案缓存空值或者在请求入口用布隆过滤器把不存在的数据挡掉。追问二缓存击穿。某个热点 key 在缓存失效的瞬间大量请求同时打到数据库。解决方案设置热点 key 永不过期或者用加锁的方式只让一个线程去回源数据库。追问三缓存雪崩。大量 key 同一时间失效数据库瞬间压力过大。解决方案给缓存失效时间加随机值避免同时过期还可以做多级缓存本地缓存扛住一部分流量。我不是在背八股这些现象在大型协同项目里非常常见。而且当你把缓存放在链路里去讲就不只是在背名词而是在描述“请求到达数据存储之前的最后一层防线”。4.2 数据库访问的常见链路细节链路走到数据库还得拆两块读写分离架构。主库负责写入从库负责读取。通常在应用服务里配置一个数据源路由根据 SQL 类型决定走主库还是走从库。很多项目还搞了分库分表把用户的订单按某个维度分散到不同的数据库节点。这一层你要是能讲出来说明你对请求链路的理解覆盖到了基础设施层。乐观锁、悲观锁、分布式锁。如果同一时刻有多个请求修改同一条数据我们要保证并发安全。这虽然更偏业务代码层面但也在链路的重要节点上。面试官问“多条请求同时进来怎么保证数据一致”时你就需要把数据库锁、Redis 分布式锁和本地锁的选型讲清楚。分布式锁是个经典话题。简单说多个服务实例共同访问一个 Redis 或 Zookeeper抢到锁的实例执行写操作其余实例等待或快速失败。这里还会涉及锁的过期时间和续租问题属于进阶展开。4.3 消息队列如何把“同步链路”拉成“异步流程”在真实项目里不是每一条链路都要同步走完。我举一个订单场景用户下单时主链路是前端请求 → 网关 → 订单服务创建订单 → 订单入库 → 返回订单号。这条主链路的延迟要求很高必须快。所以下单完成之后的消息推送、优惠券发放、积分累计、报表统计等都不应该同步做而应该丢进消息队列。从请求链路的角度看这相当于在订单服务里发出消息可能写到 MQ。上游请求继续返回用户看到下单成功。下游的消费者服务异步消费消息再各自去更新数据。这里有一个非常容易被面试官追问的细节发送消息和写数据库如何保证一致性比如你先把订单插入数据库再发消息如果发消息失败下游就收不到通知如果你先发消息再写数据库消息已经出去了但订单没落库下游消费就会出错。常见的解法有几种最简单的是“本地消息表”在同一事务里把业务数据和待发送消息一起写入本地数据库然后由后台任务把消息投递到 MQ成功后才删除本地表记录。 还会有可靠消息、异步确保、事务消息等方案但核心思路是一样的——把“写库”和“发消息”变成一个原子操作。5. 54 人共创项目的特殊链路问题与落地治理5. 54 人共创项目的特殊链路问题与落地治理聊完技术链路我要专门写一节关于“54 人共创项目”的话题。因为这个数字一摆出来真正的问题已经不是“你知不知道链路怎么走”而是“这么多人在一条链路上协作你是怎么管理的”。5.1 接口契约没人说了算链路就容易断一个 54 人的团队通常会被拆成多个小组比如前端组、用户组、订单组、支付组、数据组。每个组维护一个或多个服务。那么问题来了订单服务调用用户服务的时候用户服务提供了什么接口入参是什么返回结构是什么错误码怎么定义如果这些接口契约没有统一文档或者文档和代码不一致就会出现“A 组的请求到了 B 组B 组一看参数不对直接挂了但两侧都认为对方有问题”的扯皮现场。我在实际项目里的经验是必须有一个统一的接口文档平台契约变更要在文档里可追溯。每个接口必须有负责人不能出现“接口是我写的但我不负责改”的情况。接口变更要尽量兼容旧版本比如新增字段可以删除字段要评估所有调用方。做好接口版本号比如/v1/order、/v2/order避免破坏性变更。5.2 环境的隔离、依赖和联调54 个人的项目还有一个很烦的问题环境不统一。你在本地启动服务连的数据库可能是一套旧数据隔壁同事连的是测试环境的 MQ你发了消息他根本收不到前端同学连的网关还是另一套部署。这些环境不一致的问题最终都会折射到“请求链路”上链路明明通了但数据是错的或者链路某一环连错了服务导致报错。所以在团队协作中建议做这样几件事维护一套清晰的“环境说明”包括 DEV、TEST、STAGING、PROD 的依赖关系。所有服务之间的调用尽量通过服务名来配置而不是硬编码 IP。联调前先确认“服务注册中心”是哪个环境避免开发集群和测试集群串了。把本地启动一个服务需要的最小依赖列表写进 README。5.3 我们实际用的链路治理手段在这些项目里我们实际上用了一套组合拳契约测试。服务提供方和消费方分别维护契约用自动化方式验证接口是否一致避免发布上线后才发现链路断了。全链路压测。定期对整个入口、网关、服务、数据库压一遍看看哪一环最先扛不住。这一点在多人协作的复杂项目里尤其重要——你永远不知道哪个团队新增的一个慢查询会在大流量下拖垮全链路。全链路灰度。每次发版不把所有流量直接切到新版本而是先让少量灰度流量走完整条链路没问题再逐步放量。SLA 看板。把整个链路中每个环节的耗时、错误率、慢调用数都可视化每周开会看一次。链路是否健康不用靠感觉直接看数据。这些手段是我建议每个大型协作项目都去推进的。它们不是某一个开发者的技术任务而是需要团队管理层和基建负责人共同参与的项目工程化动作。6. 面试回答“请求链路怎么走”的实战拆解6. 面试回答“请求链路怎么走”的实战拆解最后这部分我们直接面对面试如果面试官把这个问题抛给你你该怎么答6.1 结构化回答从四个层面入手我建议你分四步讲。第一步讲入口层。“用户发起请求先经过 DNS 解析如果是静态资源就命中 CDN动态请求会到达负载均衡层再转发到 API 网关。网关负责认证、限流、路由转发把请求头标准化。”第二步讲服务层。“请求到业务服务之后如果涉及多服务协作会通过注册中心发现下游服务用 RPC 或 HTTP 调用同时通过 trace ID 把整条调用链串起来方便问题追踪调用过程中有超时、重试和熔断机制。”第三步讲数据层。“服务访问数据时会先查缓存没命中再查数据库有些写操作不能同步完成的会投递到消息队列异步处理保证主链路快速响应。”第四步讲你负责和熟悉的部分。“这条链路里我最核心负责的是 XX 服务里面的 XX 环节。在某某场景下我优化过缓存策略把接口 P99 延迟从 200ms降到 60ms。”这四步下来你既展示了全局观又展示了聚焦的深度。面试官很难不认可。6.2 应对追问不知道怎么答时不要硬编如果被问到完全没接触过的组件千万别乱编。 我面试中见过很多候选人明明没做过网关限流非要说“我们网关用了 Redis 令牌桶”当我追问“令牌桶容量你设了多少满了之后怎么办”就只能卡壳。更成熟的回答是“这块我们项目里没有实践但我了解它的原理是……如果让我去设计我会重点考虑 X、Y、Z 几点。”这种坦诚思考组合比硬编一个细节强得多。6.3 结合项目经历讲链路的加分做法最后给大家一个非常实用的角度把你自己的项目经历里踩过的真实坑放进链路回答里。比如你可以这样说“我们 54 人协作的项目里就发生过一次经典链路事故。某个服务一次发布新增了 3 个上游依赖调用但没有预估超时时间结果上线后因为下游某个接口变慢整个请求链路全部积压网关线程池被打满。后来我们做了三件事一是给所有下游调用统一设置超时时间二是加了熔断降级下游一慢就快速失败三是完善了链路追踪能一眼看到瓶颈。”这段话的价值在哪里它不只是背链路而是把链路放在真实事故和真实处理中。面试官听完就知道你不是一个只会背八股的人你是真正在大项目里被链路坑过、也通过链路治理救过火的人。按照我自己的经验一场面试里如果你能完整讲清楚这条链路再配上一段真实事故经历这个问题的通过率会非常高。因为它考察的不是你知识面多广而是你有没有真的把事情从头到尾想通过。要是你再把文中提到的那两个“盲区”刻意练几遍下次面试官再问你“请求链路怎么走”你就能从 DNS 一路讲到数据库再从数据库讲到异步消息最后落在一个真实故障上。那时候卡壳的人就不是你了。
返回列表