后端 面试题库
收录真实面试常见问题,答案长度适合口头表达;拿不准时可返回相关主题复习。
HTTP 与 API
31个问题 · 0 已看01 除了 URL 和 JSON 模式,HTTP 操作契约还必须定义什么? 查看答案 ▾ 收起 ▴
它必须定义完整、可观察的 HTTP 交换,包括方法、目标资源、参数与媒体类型、认证和授权规则、成功与失败状态码、响应头以及表示结构。契约还要说明跨请求行为,例如重试安全、并发前置条件、稳定分页和相关保留期限。OpenAPI 能描述大部分消息形状,却无法证明“调用者拥有该订单”或幂等记录与业务写入是原子的。这类承诺要写进契约,并在真实 HTTP 边界用场景测试或策略测试验证。
02 HTTP 操作的安全、幂等和可重试分别是什么意思? 查看答案 ▾ 收起 ▴
安全描述请求意图:客户端没有要求改变服务端状态。幂等描述效果:同一请求重复执行,其预期服务端效果与执行一次相同。GET 既安全又幂等;PUT 和 DELETE 幂等但不安全。可重试则是更宽的应用决策,还取决于故障发生位置和剩余截止时间。POST 只有在调用方复用同一操作键,且服务端按请求指纹原子去重并返回已保存结果时,才能在结果未知后安全重试。
03 ETag 如何防止丢失更新,实现时最重要的细节是什么? 查看答案 ▾ 收起 ▴
服务端为当前表示返回强 ETag,客户端更新或删除资源时把该值放入 If-Match。只有当前标签仍匹配时才执行修改;标签过期通常返回 412 Precondition Failed。关键是原子性:先读版本、在应用代码中比较、再执行无条件更新,仍然会留下新的竞态窗口。比较与写入必须合并为一次数据库条件更新或同一事务。成功后还要返回新的 ETag,让下一次编辑使用最新验证器。
04 你会怎样判断一次 API 变更是否向后兼容? 查看答案 ▾ 收起 ▴
05 GraphQL 解析器为 Non-Null 字段返回 null 时会发生什么? 查看答案 ▾ 收起 ▴
GraphQL 会记录一条错误,并把最近的可空祖先字段替换为 null。失败会沿路径穿过所有 Non-Null 父字段继续冒泡;如果到操作根节点前都没有可空边界,整个 data 结果会变成 null。因此,Non-Null 是可用性承诺,不只是文档标记。例如,把商品子树所有字段都设为 Non-Null,可能让一个可选扩展字段故障导致整个商品消失。我会在部分数据仍有价值的位置设置可空边界,并测试解析失败时精确的 data 与 errors 形状。
06 怎样演进 Protocol Buffers 消息而不破坏现有 gRPC 客户端? 查看答案 ▾ 收起 ▴
必须保留每个已发布字段编号的线协议编码含义。新增字段使用新编号并能接受其默认值;删除字段后保留其编号和名称,避免误用。不能因为旧读取方会忽略未知字段,就把现有字段改成不兼容的 wire type 或重新解释业务含义。兼容性也不止描述符:新枚举值、验证规则、截止时间和状态映射都可能破坏客户端。我会在测试中保留旧版生成客户端或描述符,并在部署前验证旧客户端配新服务端以及新客户端配旧服务端。
37 怎样让 API-first 契约成为工程控制点,而不只是提前编写的文档? 查看答案 ▾ 收起 ▴
在 OpenAPI 3.2.0 与 Node 24 的流程中,契约先经过评审和版本化,客户端与处理器再依赖它。CI 应解析描述、与上一版已发布契约比较,并核对真实 HTTP 状态码、响应头、媒体类型和正文;mock 与生成客户端也必须来自同一修订。代价是增加评审和工具链成本。常见陷阱是把模式一致性当成授权或幂等性的证明,这些行为承诺仍需场景测试与领域测试。
38 API 版本选择器应怎样处理默认版本与 HTTP 缓存? 查看答案 ▾ 收起 ▴
在 Node 24 的 HTTP 边界,应先解析一种有文档的选择器,例如路径或媒体类型请求头,再分派到版本适配器。选择器缺失时,要么拒绝请求,要么映射到语义固定的兼容版本;把默认值悄悄改成最新版会破坏旧客户端。若通过请求头选版本,还必须把该字段写入 Vary,并确认部署后的缓存键确实包含它。这样会降低缓存复用率,但遗漏后可能把 v1 表示返回给 v2 请求。
39 下线一个 API 版本前,应以哪些证据作为门槛? 查看答案 ▾ 收起 ▴
对 Node 24 服务而言,弃用只是宣布意图;下线会真正移除契约,需要更强证据。应记录解析后的版本和不泄露隐私的客户端身份,盘点受支持消费者,发布经过测试的迁移说明,并确认各负责人已在公告日期前完成迁移;还要演练下线后的响应和回滚路径。仅凭流量低不能判断安全,因为无人值守任务可能很少运行。并行维护适配器有成本,但过早删除会造成事后监控无法阻止的破坏性故障。
43 Elysia 路由模式除了 TypeScript 推断之外还提供什么? 查看答案 ▾ 收起 ▴
在 Elysia 1.4.30、Bun 1.3.10 与 TypeScript 6 中,路由模式会在运行时验证不可信的 path、query、header、body 和响应,同时为处理器与客户端提供类型。静态类型到网络边界会被擦除,因此仅写类型注解或断言仍会接收畸形 JSON。应声明按状态码区分的响应模式,并通过 app.handle() 或 Eden Treaty 发送真实请求。代价是模式维护更严格;响应模式缺失或过宽时,编辑器推断看似正确,实际实现仍可能漂移。
44 为什么注册顺序和生命周期作用域对 Elysia 很重要? 查看答案 ▾ 收起 ▴
Elysia 1.4.30 会按注册顺序和明确作用域组合 hook 与 plugin,因此在路由之后添加的中间件可能不会包裹该路由;local、scoped 与 global hook 覆盖的后代也不同。跨路由策略应先于受保护路由注册,需要去重时给 plugin 稳定名称,并同时测试成功与抛错路径。常见陷阱是按文件位置阅读,而没有追踪实际组合出的生命周期:认证 hook 即使写在附近,也可能从未作用于更早或不同作用域的路由。
45 FastAPI 的依赖缓存与 yield 清理怎样影响请求级资源? 查看答案 ▾ 收起 ▴
FastAPI 0.141.1 会为每个请求构建依赖图。默认情况下,同一依赖被多处引用时只解析一次,适合复用身份信息或数据库会话;设置 use_cache=False 才会再次求值。使用 yield 的依赖会在其作用域结束时清理资源,因此提交事务、流式响应和后台工作的时机必须明确。常见陷阱是返回依赖某资源的惰性数据流,而清理阶段已关闭该资源。应通过 HTTP 测试获取、异常、取消与完整消费响应等路径。
46 FastAPI 响应模型保护什么,又不能证明什么? 查看答案 ▾ 收起 ▴
在 FastAPI 0.141.1 与 Python 3.14 中,声明的响应模型会形成 OpenAPI 结构,并在序列化前过滤或验证返回数据。它能减少 ORM 对象意外暴露内部字段的风险,也便于测试按状态码区分的契约。但它不能证明资源授权、事务原子性,也不能保证所有运行时错误都采用已记录的表示。模型过宽会削弱边界,过窄则可能丢掉预期字段。应通过 ASGI 接口发请求,同时核对状态码、响应头、正文与生成的 OpenAPI。
47 客户端完成预签名上传后,后端还必须验证什么? 查看答案 ▾ 收起 ▴
在 Node 24 的设计中,预签名 URL 只授权一次受限的存储操作,并不能证明预期且安全的文件已经到达。服务端应创建上传会话,绑定对象键、大小上限、过期时间与预期校验和。完成后还要检查存储元数据,独立验证字节数和摘要,在隔离区运行内容检测,并仅在全部成功后原子地把元数据标记为已发布。直传能节省应用带宽,但不能把客户端的“完成”回调或存储层 Content-Type 当成发布证据。
52 第一个后端服务应怎样划分传输、领域和持久化职责? 查看答案 ▾ 收起 ▴
在 Node 24 的入门架构中,传输层负责解析 HTTP 输入、建立身份,并把明确结果映射为状态码与响应头;领域函数使用类型明确的命令执行业务规则;repository 则负责持久读写与事务细节。这样业务规则无需网络即可测试,数据库结构也不会泄漏到公共契约。代价是小服务也要多写少量适配器。常见陷阱是一个路由同时完成验证、修改全局内存、执行 SQL,并临时拼出错误 JSON。
53 为什么后端响应失败不能证明写入没有发生? 查看答案 ▾ 收起 ▴
在 Node 24 服务中,数据库提交与 HTTP 响应送达是两个独立事件。服务可能已提交订单,却在客户端收到成功响应前断开连接;此时盲目重试会生成重复记录。应明确提交点,为可重试操作使用稳定的幂等键,并把操作结果与业务效果原子保存。代价是要设计幂等键的保留期与冲突策略。只在进程内保存状态也是陷阱,因为重启和多工作进程会破坏去重边界。
55 请求级批处理怎样避免 GraphQL N+1 查询,同时不削弱授权? 查看答案 ▾ 收起 ▴
在 Node 24 的 GraphQL 执行模型中,同级字段解析器可能各自读取关联记录,使一个列表变成额外 N 次查询。loader 会在同一轮执行中收集 key,一次批量获取,再严格按请求 key 的顺序返回结果。它必须按请求创建,避免缓存数据、身份和租户范围跨用户泄漏。授权仍应位于业务数据边界,不能只靠字段可见性。常见陷阱是使用进程级全局 loader,既可能把其他主体的对象返回给当前用户,也会让缓存无界增长。
56 怎样在执行 GraphQL 操作前限制其成本? 查看答案 ▾ 收起 ▴
在 Node 24 服务端,应先解析并验证操作,要求分页参数有上限,再根据所选字段、嵌套层级和列表基数计算预算,通过后才调用解析器。持久化操作可以缩小可接受范围,却不会让一个已批准的昂贵查询自动变便宜。执行阶段仍要设置超时与下游容量限制,并记录规范化操作而不是含敏感变量的原始请求。单纯限制深度易于运维,但会漏掉很宽或成倍扩张的选择;成本模型更准确,却要随解析器变化维护。
57 为什么 gRPC 截止时间可能让一次写操作处于结果未知状态? 查看答案 ▾ 收起 ▴
在 Go 1.27 与 gRPC-Go 1.83.2 中,客户端截止时间限制等待时长,并通过 context.Context 传播取消,但无法撤销已经发生的提交。服务端可能刚持久化写入,客户端就收到 DeadlineExceeded;盲目重试会重复产生效果。应把截止时间继续传给下游,及时停止可选工作,并为可重试写入使用稳定操作键。代价是维护去重状态。常见陷阱是把传输状态当成领域结果的证明,而忽略结果可能未知。
58 gRPC 流中哪些所有权与背压规则最重要? 查看答案 ▾ 收起 ▴
在 gRPC-Go 1.83.2 中,流的发送与接收是绑定到同一调用上下文的有序协议操作;应用不能假设缓冲无限,也不能共享仍在修改的消息。生产者队列应有界,context 取消后立即停止生产,并由一个组件明确负责关闭与最终结果处理。客户端连接应长期复用,不能每条消息重新拨号。有界队列会限制突发吞吐,但无界队列只是把背压转移到内存,还可能在客户端断连后继续存活。
59 注册顺序怎样影响 Hono 的中间件与路由? 查看答案 ▾ 收起 ▴
Hono 4.13.5 会按注册顺序构建路由与中间件链,因此在匹配路由之后注册的中间件不会反向保护该路由。认证、请求上限和错误策略应先于需要包裹的路由组挂载,并使用 app.request() 测试最终组合应用。宽泛的通配中间件也会影响其后路由,所以顺序属于公开行为。代价是文件不能随意重排。常见陷阱是孤立审查每个处理器,却没有发现部署后的执行链跳过了必需策略。
60 Hono 类型客户端能保证什么,哪些位置仍需运行时验证? 查看答案 ▾ 收起 ▴
在 Hono 4.13.5 与 TypeScript 6 中,导出的路由类型可让配套客户端在构建时检查路径以及 TypeScript 输入输出结构。但它不能验证无类型调用方发送的字节,不能阻止独立部署的旧客户端,也不能证明授权。服务端仍要在请求边界验证数据,并返回按状态码设计的响应;信任边界需要时还要验证外部响应。代价是维护运行时模式,但只依赖推断会让类型断言、JSON 和版本偏差绕过所有编译期承诺。
62 为什么应复用 HTTPX 客户端,并且必须关闭流式响应? 查看答案 ▾ 收起 ▴
HTTPX 0.28.1 把连接池、cookie 与共享配置放在 Client 或 AsyncClient 上,因此每次请求都新建客户端会失去连接复用,并重复支付建立连接的成本。流式响应在正文消费完或响应关闭前一直占用池中连接。应使用上下文管理器或 finally 清理,并让客户端生命周期对应服务或应用。常见陷阱是返回迭代器前先关闭其客户端,或遗弃未关闭响应,最终在负载下耗尽连接池容量。
63 HTTPX 超时应怎样区分网络阶段与连接池容量? 查看答案 ▾ 收起 ▴
HTTPX 0.28.1 分别提供 connect、read、write 与 pool 超时,因此可以把本地连接池饱和与建连缓慢、对端停止发送正文这几种情况区分开。每个阶段都应从统一的端到端截止时间分配预算,并为解析和重试留出余量;read 超时表示数据块之间无进展,不一定限制整个响应总时长。常见陷阱是设置一个很大的统一值后再叠加重试,使延迟成倍增长。还应记录失败阶段,避免把容量问题误判为远端网络故障。
67 为什么 Ktor 序列化并不是完整的输入验证边界? 查看答案 ▾ 收起 ▴
在 Ktor 3.5.1 与 Kotlin 2.4.10 中,内容协商可以把 JSON 解码为 @Serializable 类型,并拒绝结构不匹配。但它不能确认金额为正、标识符属于已认证租户,或某个状态迁移被允许。应把传输数据映射为领域命令,验证字段及跨字段规则,再对目标对象授权后修改。代价是需要区分传输模型和领域模型。一个 data class 到处复用虽然更短,却容易暴露服务端字段,并把解析成功误当成业务有效。
71 Session 与响应生命周期怎样影响 Requests 的连接复用? 查看答案 ▾ 收起 ▴
在 Python 3.14 中,Requests 的 Session 持有连接池以及共享 cookie 或请求头,因此应按一个完整客户端生命周期复用,而不是每次调用都新建。流式响应在正文消费完或执行 close() 前会占用连接,应使用 with,并确保所有错误路径都关闭。并发环境下,共享可变 session 状态还要有明确所有者。常见陷阱是只读取下载的一部分就遗弃响应,最终耗尽连接池,看起来却像远端超时。
72 为什么 Requests 的超时元组并不是完整的端到端截止时间? 查看答案 ▾ 收起 ▴
在 Python 3.14 中,timeout=(connect, read) 限制建连时间和接收响应数据时无进展的间隔,但不一定覆盖 DNS、每次重定向、重试、正文处理或整个操作的墙钟时间。应从调用方统一截止时间分配各阶段上限与重试次数,剩余预算不足时立即停止。代价是需要继续传递 deadline。完全不设超时可能无限等待,而在每个嵌套层都设置很大的值会成倍放大延迟,也会掩盖真正卡住的阶段。
74 REST API 要实现安全的部分更新,必须定义哪些契约? 查看答案 ▾ 收起 ▴
在 Node 24 API 中,应选择并声明语义明确的 patch 媒体类型。JSON Merge Patch 区分未出现的成员与设为 null 的成员,JSON Patch 则描述有序操作;不能从任意 JSON 对象猜测含义。服务端要验证可编辑字段、授权目标对象,并在存在并发修改时结合 If-Match,同时返回明确的冲突与验证错误表示。代价是客户端更复杂。语义含糊的 PATCH 处理器看似灵活,却常覆盖服务端字段或造成丢失更新。
86 为什么 tRPC 路由类型不是语言中立的网络契约? 查看答案 ▾ 收起 ▴
在 Node 24 的 tRPC 模型中,客户端导入服务端路由的 TypeScript 类型,因此 procedure 路径与静态输入输出类型可以通过共享构建图传递,无需生成代码。但类型在运行时会被擦除,也不是 Swift、Python 或独立发布消费者可实现的单独模式。仍需保留运行时输入验证和明确的输出结构。它的权衡是 monorepo 开发体验很好,却与 TypeScript 发布绑定;若消费者需要独立版本和语言中立契约,应选择 OpenAPI、GraphQL 或 Protobuf。
88 URLSession 客户端应怎样区分传输、HTTP 与解码失败? 查看答案 ▾ 收起 ▴
在 Swift 6.3.3 中,URLSession.data(for:) 会为各种 HTTP 状态返回字节和 response;404 并不会作为传输异常抛出。应先处理取消与 URLError,再确认是 HTTPURLResponse,按已声明状态分类,最后只解码该状态对应的表示。204 不能强行走成功正文解码,网关返回的 HTML 错误页也不是 API 的 JSON 错误模型。代价是结果类型更细,但把所有失败都归为“解码失败”会破坏重试判断和用户提示。
89 URLSession 的取消与重试应怎样共享同一请求预算? 查看答案 ▾ 收起 ▴
使用 Swift 6.3.3 的异步 URLSession API 时,取消应从所属 Swift task 传播到网络任务,但它不能证明远端写入尚未提交。只重试安全操作或带幂等键的操作,限制次数,遵守 Retry-After,加入抖动,并在原始截止时间不足以再尝试时停止。CancellationError 应保留为取消,不能包装成普通网络失败。常见陷阱是 URLSession、repository 与 UI 各自重试三次,最终一次用户操作可能放大为二十七个请求。
认证与授权
10个问题 · 0 已看07 为什么认证成功并不能证明请求已获得授权? 查看答案 ▾ 收起 ▴
认证只建立主体身份;授权还要判断该主体能否对这个资源执行当前动作。用户 A 的有效会话并不允许读取用户 B 的发票。处理器或策略层必须依据服务端控制的数据,比较已验证身份、请求动作、租户和目标对象,不能相信调用方提交的所有者字段。我会分别测试缺少凭据与凭据有效但权限不足的情况,通常对应 401、403,或为避免泄露资源存在性而设计的 404。当权限取决于具体对象标识符时,仅做路由级角色检查是不够的。
08 为什么 JWT 签名有效仍不足以完成身份认证? 查看答案 ▾ 收起 ▴
有效签名只证明这些字节由所选验证密钥保护且未被修改。服务还必须固定允许的算法和可信签发者密钥集,并验证 issuer、audience、令牌用途、时间声明、必需声明类型与应用状态。为另一个 API 正确签名的令牌,或被当成访问令牌提交的 ID token,仍应被拒绝。验证完成后,我只向下游返回规范化且字段受限的主体对象,而不是原始载荷,避免业务代码误把任意私有声明当成权限。
09 HS256 与非对称 JWT 签名在信任边界上有什么区别? 查看答案 ▾ 收起 ▴
HS256 使用共享秘密,因此任何能够验证令牌的服务也能签发令牌;它的验证边界同时就是签发边界。非对称方案由签发者保管私钥,其他服务只获得验证公钥,所以验证能力不会带来签名能力。这种分离很有价值,但不等于自动安全:验证器必须从可信配置选择算法,把 kid 限制在该签发者批准的密钥集中,安全处理缓存与轮换,并保护密钥材料。最终选择应服从系统的信任拓扑和密钥运维策略。
10 刷新令牌轮换如何检测重放,发现后应该怎样处理? 查看答案 ▾ 收起 ▴
每次成功刷新都应在同一事务中消费当前令牌、签发唯一后继令牌,并保留令牌族关系。已消费令牌再次出现时,可能是合法客户端或窃取者在重放复制的凭据,而服务端无法判断谁持有当前有效后继。因此应撤销整个活动令牌族,记录安全事件,并要求重新授权。消费与签发必须原子执行,避免两个并发请求同时成功。轮换提供的是重放检测和会话控制;仅缩短访问令牌寿命并不能实现即时撤销。
11 在 OAuth 授权码流程中,PKCE 防止哪类攻击? 查看答案 ▾ 收起 ▴
PKCE 把授权请求绑定到发起它的客户端实例。客户端生成高熵 code_verifier,在授权请求中发送其派生的 code_challenge,兑换授权码时再提交 verifier。只截获授权码的攻击者没有 verifier,因而无法兑换令牌。PKCE 不负责认证最终用户,不能替代对 redirect_uri 的精确校验,也不能取代用于绑定浏览器响应并抵御 CSRF 的 state。授权服务器必须把 challenge 与授权码关联,并保证每个授权码只能兑换一次。
12 为什么文件扩展名和 Content-Type 不足以完成上传验证? 查看答案 ▾ 收起 ▴
两者都是调用方可控制的声明,既不能证明真实字节类型,也不能证明内容安全。我会先在请求层和流式读取层限制字节数,生成服务端存储键,并写入隔离区而不是公开路径。随后交叉检查允许的扩展名、声明媒体类型、文件签名和格式解析结果;高风险内容还可能需要恶意软件扫描或转换。只有全部通过后,发布才作为独立状态迁移执行。下载时要明确设置 Content-Type 和 Content-Disposition,用户文件名只作为元数据,绝不能直接充当文件系统路径或可执行公开名称。
48 应用应怎样安全地提供已上传文件的下载? 查看答案 ▾ 收起 ▴
在 Node 24 的上传流程中,字节应存到服务端生成的不透明对象键下,原始文件名只保留为元数据。下载时先授权目标对象,再设置可信的 Content-Type 和经过清洗的 Content-Disposition;主动内容或类型不确定的文件应下载处理,不能在应用同源环境直接执行。隔离区对象必须不可访问,删除流程也要同时处理元数据与字节。代价是内联预览不够方便。把用户文件名直接映射为公开路径会带来冲突、路径穿越或脚本执行风险。
69 Laravel 嵌套路由中,路由模型绑定与授权应怎样配合? 查看答案 ▾ 收起 ▴
Laravel 13.30.1 可以把路由参数绑定为 Eloquent 模型,scoped binding 还能通过父关系约束子对象,避免 /teams/A/projects/B 解析到无关项目。但模型解析成功仍不等于有权限。返回或修改对象前,必须使用已认证主体和请求动作执行 policy 或显式授权检查。代价是要明确设计 403 与隐藏资源存在性的 404。常见陷阱是只因标识符与父路径有效,就把已绑定模型视为已经授权。
73 重试用户提供 URL 的请求前,需要做哪些检查? 查看答案 ▾ 收起 ▴
在 Python 3.14 的 Requests 中,首先要限制 URL 的 scheme、主机名、端口、解析地址以及每一次重定向,防止客户端访问回环、私网、链路本地或云元数据服务。随后只对可安全重放操作的暂时性故障重试,并在剩余截止时间内限制次数,使用退避、抖动和 Retry-After。重定向可能改变目标,因此每跳都要重新验证。代价是某些灵活集成会被拒绝;对所有方法盲目重试会重复 SSRF 探测或造成 POST 重复写入。
87 多个 tRPC procedure 共享一个批量请求时,服务端必须考虑什么? 查看答案 ▾ 收起 ▴
在 Node 24 中,tRPC 批量传输可以让多个 procedure 调用共用一次 HTTP 请求和 context 创建。身份可从已验证凭据解析一次,但每个 procedure 及其目标对象都必须独立授权,某个调用成功不能给另一个调用授予权限。还要限制批次大小与总工作量,按契约隔离各调用错误,并避免让请求级可变状态产生顺序依赖。批处理能减少传输开销,却可能放大昂贵工作并使事务语义复杂;除非服务端明确实现,否则不能暗示整批具有原子性。
数据库
10个问题 · 0 已看13 规范化解决什么问题,什么时候适合反规范化? 查看答案 ▾ 收起 ▴
规范化让每个事实拥有明确的唯一归属,从而减少重复数据以及由此产生的插入、更新和删除异常。例如,部门名称应归属部门表,而不是复制到每个员工行中。只有经过测量,确认某种读取模式的连接、聚合或可用性成本确实重要时,才应反规范化。重复副本必须有明确维护机制,例如事务、变更流或可重建投影,并定义一致性预期。我会把约束保留在权威模型上,同时验证派生副本在部分失败后可以修复。
14 多个服务实例共享数据库时,怎样确定连接池大小? 查看答案 ▾ 收起 ▴
我会先确定数据库可持续承受的总连接数和查询能力,预留余量后,再把预算分配给所有应用实例、工作进程、迁移任务以及滚动部署期间的新旧实例。每实例池大小乘以最大并存实例数,必须不超过该预算。随后用真实事务时长压测,并观察获取等待、活动连接、查询延迟和数据库饱和度。增大连接池不是通用修复;慢查询、连接泄漏或在远程调用期间长期持有事务都会让问题更严重。连接获取要有上限,资源也要通过结构化清理可靠归还。
15 怎样选择复合 B-tree 索引的列顺序? 查看答案 ▾ 收起 ▴
我会从具体查询的过滤条件和排序要求设计索引,而不是机械套用“选择性最高的列永远放最前”。相等条件通常构成前导前缀,随后考虑排序列和范围过滤列,但最终仍要服从数据库优化器的实际行为。索引 (tenant_id, status, created_at) 很适合以 tenant_id 开头的查询,却不一定支持只按 status 查询。我会使用接近生产的数据和 EXPLAIN 验证执行计划,再评估写放大、存储成本,以及附加列能否形成覆盖索引,并有计划地删除未使用或重叠索引。
16 PostgreSQL 中 EXPLAIN 与 EXPLAIN ANALYZE 有什么区别? 查看答案 ▾ 收起 ▴
EXPLAIN 只显示优化器估算的执行计划和成本,不执行语句。EXPLAIN ANALYZE 会真正执行,并补充实际行数与耗时;加上 BUFFERS 还能观察缓存和 I/O。估算行数与实际行数差距很大,常提示统计信息过期、条件相关性或数据倾斜,也可能解释错误的连接策略。边界在于运维安全:ANALYZE 确实会执行写入和昂贵查询。对合适的修改语句,我会放进可回滚事务,并使用接近生产的数据与代表性参数,对比估算、实际循环次数、缓冲区和总延迟。
17 MongoDB 模式什么时候应嵌入数据,什么时候应使用引用? 查看答案 ▾ 收起 ▴
当子数据规模有界、由父对象独占,并且通常与父对象一起读取或更新时,适合嵌入;这样一次文档读取即可获得数据,相关修改也能在单文档边界内保持原子性。当关联数据有独立生命周期、被多个父对象共享、会无界增长,或需要单独查询时,应使用引用。选择依据是访问模式和一致性要求,而不是关系看起来是否“像 SQL”。订单中的商品快照可能是有意保留的历史事实,而实时客户资料通常应引用;同时还要限制文档大小和数组增长。
18 什么样的数据库分片键较好,为什么这个选择很难撤销? 查看答案 ▾ 收起 ▴
好的分片键应具有高基数,能均匀分布持续负载,出现在常见路由条件中,而且很少变化。它还要在保留有用局部性的同时避免热点:单调递增时间戳可能把当前写入集中到一个分片,纯哈希又会打散范围查询。分片键会进入数据放置、索引、API 和运维工具,修改它意味着在持续写入期间搬迁在线数据。因此我会先用代表性流量测试,量化 scatter-gather 查询和租户倾斜,定义跨分片事务策略,并设计可恢复、可校验且有回退边界的再平衡流程。
40 怎样诊断并修复 Django 视图中的 N+1 查询? 查看答案 ▾ 收起 ▴
Django 6.0.8 的 QuerySet 会延迟求值,因此遍历结果后再访问未缓存的关联,可能每行多发一次查询,触发点还常藏在模板里。应使用有代表性的数据统计完整视图的查询次数;单值关联用 select_related(),多值关联用 prefetch_related(),并用查询数量上限防止模板改动带来回归。陷阱是预取所有关系:未使用的预取会增加查询和内存,应只优化真正被渲染的访问路径。
42 为什么 Django 应用应在验证层和数据库中同时约束业务不变量? 查看答案 ▾ 收起 ▴
Django 6.0.8 的表单或序列化器能为一条请求路径提供友好的解析与错误,但脚本、管理后台操作、批量写入和并发请求都可能绕过它。持久规则应落到外键、UniqueConstraint、CheckConstraint 与事务中,同时保留边界验证改善反馈。先查唯一性再调用 save() 仍有竞态,因此必须捕获最终可能胜出的数据库错误。代价是要把底层失败映射成清晰响应;只依赖 full_clean() 或应用检查会让其他写入者破坏不变量。
70 怎样在 Laravel 中保护并测试涉及多次写入的不变量? 查看答案 ▾ 收起 ▴
在 Laravel 13.30.1 中,相关写入应放进同一数据库事务,并用数据库约束表达持久的唯一性或关系规则。请求验证能给客户端友好错误,却无法阻止另一个并发事务或队列任务绕过纯应用检查。应捕获预期约束冲突并明确映射,同时让编程错误进入集中处理。测试要覆盖使用真实数据库行为的完整请求切片,包括回滚和竞争写入。代价是集成测试较慢;mock Eloquent 无法重现隔离级别或约束竞态。
76 为什么 Rails 模型验证不足以保护并发数据不变量? 查看答案 ▾ 收起 ▴
在 Rails 8.1.3.1 中,validation 会在持久化前执行应用查询并生成友好的对象错误,但另一个事务可能在任一方提交前通过相同检查。持久的唯一性与关系规则应由数据库索引、约束和事务保证,再捕获预期冲突并明确映射。事务内还应避免由 callback 触发无关外部副作用。代价是要处理数据库错误;只依赖 validates_uniqueness_of 会留下竞态,只依赖约束又会缺少友好的边界反馈。
缓存与队列
6个问题 · 0 已看19 Cache-aside 如何工作,陈旧数据会从哪里出现? 查看答案 ▾ 收起 ▴
读取时,应用先查缓存;未命中后读取权威数据源,再以 TTL 写入缓存。写入时,先提交数据库变更,再使缓存键失效。两个步骤之间、失效消息丢失时、另一个读取者在竞态中重新填入旧值时,或 TTL 到期前,都可能出现陈旧数据;数据库是权威来源。我会明确可接受的陈旧时长,让失效操作可观察且可重试,在缓存键中包含租户和查询维度,并在风险较高时使用版本化键或变更驱动的失效机制。
20 热点缓存键过期时,怎样防止缓存击穿? 查看答案 ▾ 收起 ▴
核心是避免一次过期把所有等待者都变成数据库请求。常见手段包括按键合并请求或 single-flight、用短期分布式租约控制重建、stale-while-revalidate、主动刷新,以及给 TTL 加随机抖动,避免相关键同时过期。每种方案都有边界:丢失的租约必须自动到期,陈旧数据要有明确新鲜度上限,重建路径自身也要设置超时和容量保护。若重复未命中属于正常结果,还可短期缓存空值。监控应区分未命中、合并等待者、重建延迟、陈旧返回次数和源站负载。
21 Redis 中键过期与内存淘汰有什么区别? 查看答案 ▾ 收起 ▴
过期是应用为单个键设定的生命周期;TTL 到期后,该键不应再被视为存在。淘汰则是服务端在达到 maxmemory 压力时采取的动作,由配置策略决定,可能删除仍未过期的键,也可能在 noeviction 下拒绝写入。因此,缓存命中绝不是持久性保证,应用必须容忍提前未命中。我会把可丢弃缓存与持久状态分离,根据键的重要性和访问模式选择策略,监控内存与淘汰,并测试写入被拒绝时的行为。持久化配置也不会把淘汰变成应用级正确性保障。
22 怎样让至少一次投递的消息消费者保持安全? 查看答案 ▾ 收起 ▴
我会假设同一消息可能重复到达,包括业务副作用已经提交、但确认尚未到达代理的情况。消息必须携带稳定的事件或操作标识符。条件允许时,消费者要在同一原子边界内记录该标识符和业务变更;重复消息只返回先前结果,不再次执行副作用。只有持久成功后才确认。瞬时故障使用有上限的退避重试,永久故障进入死信路径,并保留原因和重放工具。顺序、重试次数和毒消息策略都应显式定义,不能从消息队列产品名称中推断。
23 什么时候应选择 Core NATS 而不是 JetStream? 查看答案 ▾ 收起 ▴
当低延迟实时传递最重要,而且没有订阅者时丢失消息可以接受,我会选择 Core NATS。它的发布订阅和 queue group 不保存供稍后重放所需的持久消费者状态。当消息必须存储、确认、重新投递、重放,或需要明确保留策略时,则选择 JetStream。JetStream 仍不会替应用完成正确性工作:至少一次投递要求副作用幂等,确认必须发生在持久成功之后,消费者还要限制 lag 和 pending 数量。选择依据应是丢失与恢复契约,而不是单独的吞吐量宣传。
24 为什么 Temporal Workflow 代码必须确定,而 Activity 代码不必? 查看答案 ▾ 收起 ▴
Temporal 通过把已记录历史重新送入 Workflow 代码来恢复工作流状态。对同一段历史,代码必须产生相同的命令序列;直接读取系统时钟、生成随机值、执行网络 I/O,或进行不兼容代码变更,都可能让重放发生偏离。编排决策应使用 Temporal 提供的重放安全时间、随机数、计时器和版本机制。Activity 是数据库调用等副作用的边界,可以非确定执行,其结果会写入历史。由于 Activity 可能在结果未知后重试,外部副作用仍需幂等设计和明确超时。
测试
8个问题 · 0 已看25 怎样在后端单元测试、集成测试和端到端测试之间取舍? 查看答案 ▾ 收起 ▴
我会选择能够观察目标故障的最小测试边界。纯领域规则适合快速单元测试;序列化、路由、数据库约束和消息适配器需要包含真实组件的集成测试;少量关键流程需要端到端测试或已部署冒烟测试,因为 TLS、代理、打包和服务组合只在这些层出现。测试金字塔是成本启发式,不是固定比例。我会对每项风险说明测试能证明什么、哪些仍在边界之外,避免模拟真正要验证的行为,并通过受控数据、时钟和依赖让失败容易定位。
26 API 契约测试能证明什么,又不能证明什么? 查看答案 ▾ 收起 ▴
提供方契约测试证明可观察请求与响应符合已发布接口;消费者契约测试证明提供方仍支持某个具体消费者交互。两者都能在边界发现序列化、状态码、响应头和模式漂移。但它们不能证明两个内部写入原子提交、所有授权决策正确,也不能覆盖消费者没有描述的场景。我会把它们与领域不变量测试和策略测试配合,保留旧契约做兼容性验证,并按证据类型报告失败。把所有测试都叫“契约测试”会掩盖责任边界,也会制造虚假信心。
27 为什么直接调用 FastAPI 路径函数不足以测试端点? 查看答案 ▾ 收起 ▴
直接调用只执行普通 Python 逻辑,会绕过路由匹配、参数来源解析、依赖解析、异常转换、响应模型过滤和序列化,因此内部测试通过时公开端点仍可能损坏。至少一层测试应通过 ASGI 测试客户端发送 HTTP 请求,并断言状态码、相关响应头和公开响应体。我会覆盖畸形输入、缺少认证、对象级拒绝、冲突,以及依赖或序列化失败后的清理。如果连接池由 lifespan 初始化,测试客户端还必须作为上下文管理器运行,确保启动和关闭流程真正执行。
28 MVC slice、SpringBootTest 与随机端口测试的实际区别是什么? 查看答案 ▾ 收起 ▴
MVC slice 只加载经过筛选的 Web 层,适合验证映射、绑定、校验和控制器行为,但不包含大部分生产装配。SpringBootTest 会加载完整应用上下文;配合 MockMvc 时仍走模拟 servlet 请求路径,而且默认不会打开服务器。随机端口测试会启动嵌入式服务器并使用真实套接字,能覆盖更多 HTTP 服务器行为,但成本更高。三者都不能证明外部入口、TLS 或打包正确。我只用它们验证各自边界内的风险,并至少保留一个按部署主配置和 profile 启动的测试。
29 数据库分支能改善 CI 的什么问题,又有哪些风险仍然存在? 查看答案 ▾ 收起 ▴
数据库分支为测试或预览环境提供隔离状态,避免所有套件共享同一套可变模式和数据集,适合迁移演练、并行 pull request 和可复现夹具。但它不会自动保证数据安全或测试确定性。源自生产的分支在交给不可信预览代码前必须脱敏,并控制创建时间点、模式版本、种子数据、凭据、过期和清理。我仍会单独测试迁移锁和真实数据规模,因为 copy-on-write 隔离无法复现所有生产负载与运维故障。
30 Elysia 进程内测试应覆盖什么,为什么还要保留真实网络测试? 查看答案 ▾ 收起 ▴
通过 Web Request 调用 app.handle,或使用进程内 Treaty 客户端,应覆盖路由、运行时验证、生命周期钩子、短路行为、响应模式和错误映射,而且不需要打开端口。应用模块要导出完整组合但尚未调用 listen 的实例,避免导入产生部署副作用。不过这种测试无法观察 DNS、套接字、TLS、反向代理改写、基础 URL,或公开端点实际运行的服务版本。我会保留规模更小的真实网络测试;当类型兼容性可能与部署拓扑漂移时,还要测试候选部署。
54 一个小型后端成为生产服务前,应补上哪些测试? 查看答案 ▾ 收起 ▴
对 Node 24 服务而言,领域不变量用纯函数测试;repository 用能体现真实数据库行为的测试;HTTP 测试则一起覆盖路由、解析、认证、序列化与错误映射。还应为代理请求头、资源上限、关闭流程等基础设施行为增加少量部署路径检查。各层观察的边界不同;在路由测试中 mock 所有依赖,只能证明装配假设。集成测试较慢,因此应保持夹具隔离且目标明确,不能用一套万能端到端测试取代快速领域测试。
64 测试中何时应替换 HTTPX transport,这类测试不能证明什么? 查看答案 ▾ 收起 ▴
HTTPX 0.28.1 的自定义或 mock transport 可以在不打开 socket 的情况下检查已构造请求并返回确定响应;ASGITransport 还能调用进程内 ASGI 应用。这很适合测试状态映射、解码、重试决策和畸形正文,但不能证明 DNS、TLS、代理行为、HTTP/2 协商、真实超时或连接池。上述边界仍需少量真实网络集成测试。代价是基础设施测试较慢。常见陷阱是友好的 mock 接受了真实服务器或中间层会拒绝的请求。
部署
30个问题 · 0 已看31 什么时候应选择四层负载均衡而不是七层负载均衡? 查看答案 ▾ 收起 ▴
四层负载均衡按传输层地址和端口路由 TCP 或 UDP 连接,适合非 HTTP 协议、TLS 透传,或简单的高吞吐连接边界。七层负载均衡理解 HTTP 等应用协议,可以按 host、path、header 或 cookie 路由,还能终止 TLS 并执行 HTTP 感知策略。这种灵活性需要解析,也让代理成为应用语义的一部分。我会根据路由与可观测需求选择,而不是笼统比较性能。两种方案都要定义主动和被动健康信号,移除节点时排空连接,并测试节点变慢但未完全宕机的情况。
32 后端应怎样信任 Nginx 转发的客户端地址和协议头? 查看答案 ▾ 收起 ▴
后端只能信任由已知代理节点覆盖或清洗的转发头。公网客户端也能自行发送 X-Forwarded-For、X-Forwarded-Proto 或看似身份信息的请求头;若接受任意对端提供的值,就会产生伪造风险。Nginx 应按明确的代理跳数模型追加或替换字段,应用则要配置精确的可信代理网段,并只解析预期数量的跳。必须阻止客户端直连后端,或把直连请求视为不可信。我会同时测试正常代理路径,以及绕过代理或到达第一可信节点的伪造请求头。
33 Envoy 的熔断与异常点检测分别保护什么边界? 查看答案 ▾ 收起 ▴
熔断限制整个上游集群承受的压力,例如并发连接、等待请求、活动请求或重试数量;达到上限时,Envoy 会拒绝部分工作,避免队列和资源无界增长。异常点检测则评估单个端点,并按策略暂时剔除持续失败的实例。两者与健康检查互补,但都不能证明业务结果正确。容量限制要按优先级分配预算,并配合有界重试和过载指标;异常剔除要有足够流量证据、最大剔除比例和恢复行为,避免噪声信号移除所有仍有服务能力的端点。
34 为什么无服务器事件处理器必须具备幂等性? 查看答案 ▾ 收起 ▴
事件源可能在超时、工作进程崩溃、批次部分失败或确认丢失后重新投递,因此一个逻辑事件可能多次调用处理器。处理器应使用源事件 ID 或领域操作 ID 作为去重键,并尽可能把持久进度与每个副作用原子记录。平台支持部分批次失败协议时,只报告失败项,避免无谓重放成功记录。处理器外初始化的客户端可能在温热执行环境中复用,但内存只是优化,不是持久状态。重试上限、死信处理和可观测性仍要显式设计。
35 哪些后端工作适合边缘函数,哪些应留在区域服务? 查看答案 ▾ 收起 ▴
边缘函数适合能在单次请求内快速完成、且确实受益于就近执行的工作,例如路由、重定向、请求头规范化、轻量认证检查和缓存决策。有状态事务、长时间 CPU 任务、大型依赖、需要完整 Node API 的代码,以及反复访问遥远主数据库的逻辑,通常应留在区域服务。只把计算移到边缘,而每次仍跨区取数据,并不会降低延迟。我会核对具体平台的运行时、CPU、内存、请求大小和子请求限制,确保密钥与日志合规,并设计边缘依赖或区域不可用时的明确回退。
36 WSGI 与 ASGI 最重要的能力差异是什么? 查看答案 ▾ 收起 ▴
WSGI 是同步 HTTP 调用接口:服务器提供 environ 和 start_response,再从可迭代对象拉取字节。ASGI 是异步事件接口,由 scope、receive 和 send 组成,支持 HTTP 流、断连事件、WebSocket 和 lifespan。仅写 async 并不会让阻塞的数据库或文件调用变成非阻塞,所有依赖和中间件边界都必须配合。WSGI 到 ASGI 的适配器可以在线程池运行同步工作,却不能增加原生 WebSocket 语义,而且可能耗尽线程池。我会按所需能力选择,并测试取消、背压、清理和每工作进程资源容量。
41 为什么 Django 异步视图仍可能阻塞,事务密集型工作应在哪里运行? 查看答案 ▾ 收起 ▴
在 Django 6.0.8 与 Python 3.14 中,async def 只改变视图调用边界,不会自动把同步中间件、文件访问、客户端或 ORM 调用变成非阻塞。真正支持异步的操作使用异步 ORM 方法,同步依赖则通过官方适配边界调用。Django 6.0 的异步模式不支持事务,因此事务密集型单元应保持同步。代价是上下文切换和线程容量,应对实际部署的 ASGI 栈做负载测试,而不是只统计异步函数数量。
49 Flask 为什么适合使用应用工厂,哪些设置必须在其中完成? 查看答案 ▾ 收起 ▴
在 Flask 3.1.3 与 Python 3.14 中,应用工厂负责创建已配置的应用实例,并在服务器接收请求前初始化扩展、注册 blueprint、中间件和错误处理器。这样测试可以使用独立配置,而不依赖可变的模块级单例。首次请求后再修改设置并不安全,不同工作进程可能看到不同路由或策略。代价是依赖装配更显式。数据库迁移和一次性数据任务应作为部署步骤,不能成为每个工作进程或测试实例都会重复的工厂副作用。
50 为什么 Flask 的请求上下文对象不应逃逸到后台任务中? 查看答案 ▾ 收起 ▴
Flask 3.1.3 通过上下文本地代理提供 request、session 和 g,它们只在所属请求上下文存活时才能正确解析。后台线程或队列任务稍后再读取,可能直接失败,也可能错误理解资源生命周期。应只复制任务需要且已验证的标量数据与标识符,并显式传参;非请求代码确实需要 current_app 时可建立应用上下文。这样会增加交接代码,但复制整个请求既可能泄露凭据,也会让持久任务依赖短暂状态。
51 Flask 异步视图何时有帮助,什么时候 Flask 的执行模型并不合适? 查看答案 ▾ 收起 ▴
在 Flask 3.1.3 中,安装 async 额外依赖后,异步视图可以在一次 WSGI 请求内并发等待受支持的 I/O,但每个请求仍占用一个工作进程,视图事件循环结束时自行创建的任务会被取消。它不提供原生 WebSocket 或长连接 ASGI 语义,同步依赖也仍会阻塞。持久后台工作应交给任务队列,长连接则更适合 ASGI 框架。常见陷阱是只看协程语法,不测工作容量、取消行为和依赖实际性质。
61 怎样让 Hono 应用在不同 JavaScript 运行时之间保持可移植? 查看答案 ▾ 收起 ▴
Hono 4.13.5 的处理器以 Web 标准 Request、Response 与 fetch 语义为中心,但 Node 24 绑定、文件系统 API、环境访问和服务器启动仍属于适配器。应把这些能力封装在有类型的接口后,通过 context 传入 binding,并把部署适配器留在路由和领域代码之外。核心逻辑用 app.request() 测试,再对真实适配器运行少量测试。抽象层会增加代码,但在处理器深处直接导入运行时专用全局对象,会让看似可移植的类型图悄悄绑定单一平台。
65 为什么 suspend 不会让 Kotlin 后端中的阻塞调用自动变成非阻塞? 查看答案 ▾ 收起 ▴
在 Kotlin 2.4.10 与 Ktor 3.5.1 中,suspend 只有在被调用操作配合时才能挂起协程。阻塞式 JDBC 驱动、文件 API 或旧 HTTP 客户端仍会占住线程,并可能耗尽 Ktor 执行容量。应优先使用非阻塞库;无法避免的阻塞工作放到依据下游容量设置上限的 dispatcher,并继续传递请求截止时间。常见陷阱是使用无界线程池,短期掩盖阻塞,却制造无法控制的队列、连接数和关闭行为。
66 结构化并发应怎样约束 Kotlin 请求中的副作用? 查看答案 ▾ 收起 ▴
Kotlin 2.4.10 会把子协程绑定到所属 scope,使请求取消与失败能够传播,并可在返回前等待必需工作结束。请求内必须完成的工作使用 Ktor 请求作用域;需要在响应后继续的效果应持久交接给任务系统,不能用无主的 GlobalScope 启动。取消是协作式的,也不会回滚已经提交的数据库写入,因此必须定义提交点和幂等策略。代价是生命周期设计更显式,但脱离所有者的任务会丢失错误,也无法有序关闭。
68 为什么 Laravel 服务容器的绑定生命周期对长驻工作进程很重要? 查看答案 ▾ 收起 ▴
在 Laravel 13.30.1 与 PHP 8.3.33 中,singleton binding 的寿命等于容器寿命;在长驻 HTTP 或队列工作进程里,它可能跨越多个请求。若其中保存 request、已认证用户、租户或可变累加器,状态就会泄漏到后续任务。应使用请求级 binding 或显式传递请求数据,并按框架机制在任务间重置状态。无请求数据的长生命周期客户端可以复用。代价是要管理生命周期;在每次新进程模式下无害的代码,到了长驻进程可能变成正确性或隐私问题。
75 Rails 约定怎样影响自动加载与应用边界? 查看答案 ▾ 收起 ▴
Rails 8.1.3.1 与 Ruby 4.0.6 依靠命名和目录约定连接常量、路由、controller、model、job 与测试,减少重复配置。这种效率以文件名和常量路径作为契约;二者不一致时,开发环境的按需加载与生产环境的 eager load 可能表现不同。controller 应保持为 HTTP 适配器,领域或查询逻辑使用职责清晰的对象,而不是藏进 callback。代价是目录结构不能随意设计;绕开约定会增加自定义启动逻辑和框架难以诊断的环境差异。
77 怎样在 Rails 中安全部署破坏性的数据库结构变更? 查看答案 ▾ 收起 ▴
在 Rails 8.1.3.1 中,破坏性结构调整应跨多个发布采用 expand-and-contract:先增加可空的新列或表;部署能读取新旧结构并写入过渡状态的代码;再以有界、可恢复批次回填;所有进程兼容后才加约束并删除旧结构。还要测试回滚和长时间运行的 job。代价是暂时存在重复数据。一次部署直接重命名或删除列,会破坏仍运行旧代码的 web worker、队列任务或 console 操作。
78 为什么 Rust 异步后端必须隔离阻塞工作? 查看答案 ▾ 收起 ▴
在 Rust 1.98 中,异步 future 只有被 poll 时才推进,并需要协作式让出执行权。若在 Tokio 工作线程上运行阻塞数据库驱动、文件调用或长时间 CPU 循环,同线程上的其他 future 都无法继续。应优先选异步依赖;否则把有界阻塞工作交给 spawn_blocking 或容量受限的独立服务,并传递截止时间。代价是调度与交接开销。常见陷阱是把 async fn 当成非阻塞证明,再用无界任务队列处理饥饿,把过载转移到内存。
79 为什么丢弃 Rust 请求 future 不会回滚其副作用? 查看答案 ▾ 收起 ▴
在 Rust 1.98 的异步执行中,超时或断连可能丢弃请求 future,并析构其拥有的值,但已经提交的事务或已被远端接受的调用仍然有效;某些阻塞工作甚至会在等待它的 future 取消后继续运行。事务应有明确所有者,提交点要清楚,可重试写入使用幂等键;每个 await 处发生取消时,清理都应安全。代价是状态建模更复杂。把 RAII 当成外部效果回滚,会混淆内存资源清理与分布式事务语义。
80 Rust 后端应怎样协调共享状态、背压与优雅关闭? 查看答案 ▾ 收起 ▴
在 Rust 1.98 中,不可变配置和线程安全客户端应放入明确的应用状态,再依据下游容量设置连接池、semaphore 与有界队列。开始关闭时先停止接收新工作,通知所属任务,只在截止时间内排空,并按依赖顺序关闭资源。Axum extractor 能让所有权可见,但 Arc 只证明共享所有权,不代表内部修改或业务顺序安全。代价是过载时要用 429 或 503 拒绝部分请求;无界 channel 只会把拒绝推迟到内存与关闭时间耗尽。
81 为什么 Scala 后端应在一个有所有者的边界运行 effect? 查看答案 ▾ 收起 ▴
在 Scala 3.9.0 与 Cats Effect 3.7.1 中,IO 描述一个 effect,构造它并不会执行工作。应用运行时应只在顶层运行 effect,路由和服务负责组合值,客户端、连接池与服务器则通过 Resource 获取。这样释放操作在成功、失败和取消时都有明确所有者。代价是 API 中会显式出现 effect 类型。在业务代码内部调用 unsafe runner,或创建无主 Future,会割裂生命周期控制,并可能在关闭时泄露资源或隐藏失败。
82 http4s 服务应怎样处理阻塞式 JDBC 或文件系统工作? 查看答案 ▾ 收起 ▴
在 Cats Effect 3.7.1 与 http4s 0.23.36 中,把阻塞调用包进普通 IO 并不会使它变成非阻塞。应使用阻塞边界,让运行时保护 compute 线程,并根据数据库或文件系统容量另外限制并发。还要传播取消和截止时间,同时承认底层阻塞调用可能无法立即停止。代价是额外上下文切换。无界 blocking 区域只是表面上避免 compute 饥饿,仍可能耗尽连接、线程、队列与优雅关闭时间。
83 Cats Effect 中,取消应怎样与提交边界配合? 查看答案 ▾ 收起 ▴
在 Cats Effect 3.7.1 中,取消是协作式的,finalizer 会释放所属资源,但无法撤销已经提交的外部效果。应把可取消的准备阶段与最小必要的不可取消提交区分开,后续工作再恢复可取消状态。若调用方可能在结果未知时重试,应使用稳定操作键,并提供可查询结果。代价是存在一小段不可中断时间;把整个请求都设为不可取消会浪费容量并拖慢关闭,而让提交过程随意中断又可能留下含糊的部分状态。
84 Spring Boot 自动配置怎样决定何时让步? 查看答案 ▾ 收起 ▴
Spring Boot 4.1.1 会根据 classpath、环境、应用类型和已有 bean 评估自动配置条件。典型配置只在必需类存在且应用尚未提供对应 bean 时创建默认实现,使显式应用策略优先。遇到意外行为应查看 condition evaluation report,而不是随意增加排除项。代价是启动结果依赖配置状态;过宽的组件扫描或无意加入的依赖可能激活 bean,而自定义一个替代 bean 也可能使一整条有用的默认配置链让步。
85 Spring Boot 服务应怎样验证配置,同时避免在运维接口泄露密钥? 查看答案 ▾ 收起 ▴
在 Spring Boot 4.1.1 中,应把相关设置绑定到有类型的 @ConfigurationProperties,添加验证约束,并在必需值缺失或格式错误时让启动失败。凭据放在外部密钥源中,不要记录整个绑定对象。Actuator 的健康与配置端点必须明确设置暴露范围、授权和脱敏;运维元数据并不等于公开数据。代价是部署协调更严格。零散的 @Value 字符串会推迟错误并模糊所有权,而开放全部 Actuator 端点可能泄露环境值、bean 名称或基础设施细节。
90 URLSession 执行后台传输时,哪些状态必须跨进程存活? 查看答案 ▾ 收起 ▴
在 Swift 6.3.3 中,后台 session 会把基于文件的传输交给系统进程,应用可能终止后再通过稳定且唯一的配置标识符重新连接。任务描述、业务标识、目标位置与状态迁移都应持久化;内存 closure 和进度观察者不是恢复状态。下载完成后要及时移走临时 URL 中的文件,并在持久处理完成后才结束后台事件。代价是状态机更复杂。若每次启动都生成新的 session 标识符,就会失去现有传输及其回调的所有权。
91 ASGI 应用应怎样处理流式响应与客户端断连? 查看答案 ▾ 收起 ▴
按 Python 3.14 对应的 ASGI 语义,应只发送一次 http.response.start,随后按顺序发送正文事件,并且只有确实还有下一块时才把 more_body 设为 true。断连存在竞态:send() 可能先抛错,也可能之后的 receive() 先得到 http.disconnect,因此清理要兼容两种顺序且只执行一次。请求正文必须循环读取 more_body 并限制累计大小。显式状态机会增加代码,但全部缓冲会破坏背压,并让不可信数据流无界占用内存。
92 哪些资源属于 ASGI lifespan,WSGI 适配器又无法提供什么? 查看答案 ▾ 收起 ▴
在 Python 3.14 部署中,ASGI lifespan 负责事件循环本地的连接池和客户端:启动时创建,通过 lifespan state 传递引用,关闭时释放。多工作进程服务器会为每个事件循环运行一套生命周期,因此总连接容量会按 worker 数相乘。WSGI 到 ASGI 的适配器可以在线程池执行同步 HTTP 调用,却不能增加原生 WebSocket、异步请求流,也无法立即取消阻塞工作。它适合分阶段迁移,但把适配误当成能力升级会耗尽线程并错误设置资源作用域。
93 最小的 WSGI 应用与响应契约是什么? 查看答案 ▾ 收起 ▴
在 Python 3.14 与 PEP 3333 下,服务器调用 application(environ, start_response)。应用通过 start_response 提供状态字符串与响应头列表,再返回由 bytes 组成的可迭代对象;文本必须先编码,再计算字节长度。逐跳请求头由服务器负责。生成器可能到迭代时才真正执行,但响应头必须在第一块正文之前送达。常见陷阱是返回 str,或按字符数猜测 Content-Length;可用 wsgiref.validate 做边界测试以捕获协议违规。
94 WSGI 中间件包装流式响应时必须保留什么? 查看答案 ▾ 收起 ▴
在 Python 3.14 的 WSGI 中,透明中间件必须保留正文块顺序、重复响应头、start_response 可选的 exc_info 参数,以及下游可迭代对象的 close() 生命周期。正文不变时直接返回原对象最安全;若包装并转换正文,就要在正常、异常和提前断连路径中转发清理。仅为记录状态而调用 list(result) 会缓冲整个流并改变资源时序。代价是包装代码更谨慎;丢失 close() 会泄漏生成器持有的文件或事务,而短列表测试通常发现不了。
95 WSGI 应用应怎样处理请求正文与服务器并发? 查看答案 ▾ 收起 ▴
在 Python 3.14 的 WSGI 中,wsgi.input 是二进制流,不是已经解析的正文。应检查 CONTENT_LENGTH 缺失、无效和超限情况,设置端点字节上限,只读取允许的长度后再解码。另一方面,wsgi.multithread 与 wsgi.multiprocess 描述服务器可能怎样并发调用应用;同步并不等于单线程。代价是需要显式限制资源并按进程设计状态。无界 read() 可能阻塞或耗尽内存,可变全局状态则可能在线程间竞态,或在多个 worker 中彼此分叉。
没有符合筛选条件的问题。