Interview Bank
328 questions total; 5 shown with current filters.
CourseAllFrom Frontend Engineer to Agent Engineer in 30 DaysPrompt Engineering From Scratch in 5 DaysMastering Claude: From Conversation to Claude Code in 5 DaysMastering Codex and the OpenAI Agents SDK in 5 DaysMCP in 7 Days: Wire Tools Into Any AgentAgent Skills in 7 Days: Turn Experience Into Reusable CapabilityContext Engineering in 5 DaysRAG in 14 Days: From Retrieval to Trustworthy AnswersBuild an AI Short-Drama Production Pipeline With Agents in 14 Days
Tag
All#deployment5#reliability22#cost15#architecture12#streaming11#security10#observability9#distributed-systems8#idempotency8#multi-agent8#rag7#system-design7
136 more tagsShow fewer tags
#api-design6#operations6#sse6#message-bus5#tool-calling5#agent-loop4#behavioral4#error-handling4#evaluation4#framework-design4#mcp4#routing4#concurrency3#context-engineering3#interview-prep3#langgraph3#llm-basics3#model-routing3#orchestration3#prompt-injection3#protocol3#redis-streams3#scalability3#scheduling3#agent-design2#auth2#checkpointing2#communication2#cost-control2#database2#debugging2#interview-process2#latency2#long-term-memory2#memory2#ordering2#prompt-engineering2#rate-limiting2#react2#resume2#retrieval2#sharding2#state-machine2#state-management2#tool-design2#tool-permissions2#trade-offs2#ux2#agent-basics1#agent-quality1#async1#atomicity1#cancellation1#capacity-planning1#career1#chunking1#compression1#configuration1#consistent-hashing1#context1#context-compression1#context-management1#correctness1#customer-support1#data-modeling1#deliberate-practice1#docker1#documentation1#engineering-tradeoffs1#escalation1#event-driven1#fallback1#fan-out1#fencing-token1#forking1#framework-selection1#frontend1#global-market1#hybrid-search1#interrupt-merge1#isolation1#json-parsing1#jwt1#knowledge-organization1#lease1#least-privilege1#llm-as-judge1#loop-guard1#mobile1#multi-tenancy1#nodejs1#performance1#persistence1#pgvector1#portfolio1#prioritization1#proactive-messaging1#product-engineering1#project-storytelling1#prompt1#provider-abstraction1#quiet-hours1#ranking1#recall1#reconnect1#redis1#reflection1#replay1#reporting1#rerank1#retrieval-quality1#retry1#retry-semantics1#rrf1#sampling1#sandboxing1#schema-design1#secrets-management1#self-assessment1#self-introduction1#self-presentation1#service-architecture1#session-management1#split-brain1#star1#stateless1#storytelling1#structured-output1#system-prompt1#testing1#timezone1#tool-execution1#tools1#tracing1#transport1#vector-database1
From Frontend Engineer to Agent Engineer in 30 Days
D7 Packaging It as a Service: Fastify + SSE + Docker (dg P07); Week One Retrospective
What are the key decisions in a Dockerfile that packages a Node service?把一个 Node 服务打包成 Docker 镜像,Dockerfile 里有哪些关键决定?
Common in ChinaCommon overseasIntermediate#docker#deployment#nodejsHow to reason about it · think before answering
- It looks like a recipe question, but it tests whether you have ever traded off build speed against security. Reading FROM, COPY, RUN, CMD in order is the least differentiating answer.
- The first decision is instruction order, the only one with an immediately measurable payoff. Images are stacked layers and a layer whose inputs are unchanged is reused, so copy the manifest and lockfile first, install, then copy source. Editing one line of code then invalidates only the last two layers instead of forcing a full reinstall.
- Second, pin the base image. Using latest means the image silently jumps a major version some morning, which destroys the reproducibility that was the whole reason to containerize.
- Third, runtime configuration: bind to 0.0.0.0 inside a container. Binding 127.0.0.1 leaves the service reachable only from inside, so a published port still refuses connections — and it works perfectly on your laptop, which is why it is so common. Also note EXPOSE only documents intent; the port is actually published by docker run -p.
- Fourth, security: run as a non-root user, since containers share the host kernel and root widens the blast radius of an escape. Keep node_modules out via .dockerignore (host binaries will not run in a Linux container and the build context balloons) and keep .env out too, passing secrets at runtime with --env-file.
- Expect the follow-up, and it is the one a streaming service should volunteer: use the exec-form CMD to launch node directly so it becomes PID 1. With pnpm start, PID 1 is the package manager, SIGTERM from docker stop may never reach node, your graceful shutdown never runs, and the container is SIGKILLed after the timeout — cutting every in-flight SSE stream.
分析过程 · 先想清楚再作答
- 这题看着是背步骤,其实考的是「你有没有为构建速度和安全性做过取舍」。把 FROM、COPY、RUN、CMD 顺着念一遍是最没有区分度的答法。
- 第一个决定是指令顺序,也是唯一能立刻量化收益的:镜像是逐层叠出来的,某层的输入没变就复用缓存。所以先只拷 package.json 和 lockfile、装完依赖再拷源码——改一行业务代码只让最后两层失效,依赖那层照旧命中;反过来一上来就 COPY 全部,改一个字都要重装依赖。
- 第二个是基础镜像钉版本。写 latest 等于让镜像在某天悄悄升到下一个大版本,可复现性当场归零,而可复现正是用容器的全部理由。
- 第三个是运行时配置:容器里必须监听 0.0.0.0,只听 127.0.0.1 的话它只在容器内部可达,宿主机做了端口映射也连不上——这个坑在本机跑的时候完全正常,所以特别常见。另外 EXPOSE 只是声明意图,真正开端口的是 docker run 的 -p。
- 第四个是安全:用非 root 用户跑业务进程(容器和宿主机共用内核,逃逸后 root 的破坏面大得多),.dockerignore 排除 node_modules(宿主机的二进制在 Linux 容器里跑不起来,还会让构建上下文暴涨)和 .env(密钥打进镜像等于发给每个能拉到镜像的人,运行时用 --env-file 传)。
- 可以预期的追问,也是长连接服务最该主动说的一条:CMD 要用数组形式直接起 node,让它当 PID 1。写成 pnpm start 的话 PID 1 是包管理器,docker stop 的 SIGTERM 未必传得到 node,优雅退出代码永远不执行,只能等十秒超时被 SIGKILL——对 SSE 服务,那意味着所有在途的流被硬切。
Key points
- Instruction order drives cache hits: copy the manifest, install, then copy source, so code edits do not reinstall dependencies
- Pin the base image instead of latest — reproducibility is the entire point of containerizing
- Bind 0.0.0.0 inside the container; EXPOSE only documents intent while docker run -p publishes the port
- Run as a non-root user, and keep node_modules and .env out via .dockerignore, injecting secrets at runtime
- Use exec-form CMD to run node as PID 1 so SIGTERM reaches it and graceful shutdown actually executes
答题要点
- 指令顺序决定缓存命中:先拷依赖清单装依赖,再拷源码,改代码不会触发重装依赖
- 基础镜像钉版本不用 latest,可复现是用容器的全部理由
- 容器里监听 0.0.0.0;EXPOSE 只是声明,真正开端口靠 docker run -p
- 用非 root 用户运行;.dockerignore 排除 node_modules 与 .env,密钥运行时用 --env-file 注入
- CMD 用数组形式直接起 node 让它当 PID 1,SIGTERM 才能传到进程,优雅退出才有效
For a long-lived SSE service in production, what problems do heartbeats, disconnect handling and graceful shutdown each solve?一个 SSE 长连接服务上线,心跳、连接断开处理和优雅退出分别在解决什么问题?
Common in ChinaCommon overseasDeep dive#sse#reliability#deploymentHow to reason about it · think before answering
- The discriminator is that the three have completely different failure symptoms. Someone who can describe each symptom has shipped one; 'they all improve stability' is a non-answer.
- Heartbeats prevent middleboxes from killing you. Load balancers and gateways commonly close idle connections after 60 to 120 seconds, and agents are full of silent gaps while the model reasons, calls a tool or waits on a slow API. The symptom is a stream that dies halfway for no visible reason and never reproduces against a local server. Implement it as an SSE comment line, which clients silently ignore, so no client change is needed.
- Disconnect handling is about money. When a user closes the tab the server does not stop on its own: the model keeps generating and tokens keep billing with nobody receiving. It is the most expensive oversight in streaming services, and staging never reveals it because nobody closes tabs mid-run. Watch for the response closing, distinguish a premature close from a normal finish, and abort the upstream request.
- One detail must be right or it exposes you immediately: in Node listen on the response object's close, not the request's. The request emits close once its body has been read, so using it as a disconnect signal misfires on every normal request and you see streams stopping after one or two chunks.
- Graceful shutdown is about deploys cutting live requests. On SIGTERM the process should stop accepting new connections, give in-flight streams a short window, then exit; otherwise users watch a reply stop mid-sentence. This assumes the signal actually reaches the process — if the container's PID 1 is a package manager, SIGTERM never arrives and the runtime kills you on timeout.
- Expect: how long is the window? Shorter than the orchestrator's termination grace period (10s by default in Docker, 30s in Kubernetes), or you get SIGKILLed anyway; and refuse new connections immediately so the load balancer drains traffic away.
分析过程 · 先想清楚再作答
- 这题的区分度在于三件事各自的失败现象完全不同,能分别说出现象的人一定真上过线。答成「都是为了稳定性」等于没答。
- 心跳解决的是「被中间设施误杀」。负载均衡和网关普遍有空闲超时,常见 60 到 120 秒,一段时间没有字节流动就关连接;而 Agent 天生有大量静默期——模型在思考、在调工具、在等慢接口。现象是连接莫名其妙断在一半,且本地直连时完全复现不了。实现上用 SSE 的注释行(冒号开头)做心跳,客户端会安静忽略,不用改客户端代码。
- 连接断开处理解决的是「花钱」。用户关掉页面之后服务端不会自动停,模型继续生成、token 继续计费,只是没人接收。这是流式服务里最贵的疏忽,而且测试环境暴露不出来,因为没人会中途关页面。做法是监听响应对象的关闭事件,判定是被掐断而不是正常收尾,就把上游请求一起中止。
- 这里有个必须说对的细节,说错会当场暴露没写过:Node 里要监听的是响应对象的 close,不是 request 的——request 的 close 在请求体读完时就触发,拿它当断线信号会把每一条正常请求都误判成客户端跑了,现象是每次只推出一两个片段就停。
- 优雅退出解决的是「发布时切断在途请求」。容器收到 SIGTERM 后应当先停止接受新连接,给在途的流一点收尾时间再退出,否则用户看到的是回复说了一半突然没了。前提是信号真的能传到进程——CMD 写成包管理器的话 PID 1 不是 node,SIGTERM 传不到,只能等超时被强杀。
- 可以预期的追问:收尾时间给多久?答案是要小于编排系统的终止宽限期(Docker 默认十秒、K8s 默认三十秒),超过就会被 SIGKILL,等于白设计;同时新连接要立刻拒绝,让负载均衡把流量挪走。
Key points
- Heartbeats defeat idle timeouts in middleboxes, since agent silence often exceeds a gateway's 60 to 120 seconds; SSE comment lines do it transparently
- Disconnect handling stops waste: after a user closes the tab, an unaware server keeps burning tokens, and staging never shows it
- In Node listen on the response's close, not the request's — the latter fires when the body is read and misclassifies normal requests as disconnects
- Graceful shutdown stops deploys from cutting live streams: on SIGTERM refuse new connections and drain, within the orchestrator's grace period
- It only works if the signal reaches the process, so PID 1 must be node itself rather than a package manager
答题要点
- 心跳防的是中间设施的空闲超时,Agent 的静默期常常超过网关的 60 到 120 秒,用 SSE 注释行实现,客户端无感
- 断开处理防的是浪费:用户关页面后服务端不停就是纯烧 token,测试环境暴露不出来
- Node 里要监听响应对象的 close 而不是 request 的——后者在请求体读完时就触发,会把正常请求误判成断线
- 优雅退出防的是发布切断在途流:SIGTERM 后先停收新连接、给在途流收尾时间,收尾窗口要小于编排系统的终止宽限期
- 前提是信号能传到进程:容器的 PID 1 必须是 node 本身,不能是包管理器
D14 Deployment and Operations: Multi-Worker Compose, Heartbeats, Health Checks, Graceful Shutdown, Dev/Prod Isolation; Week Two Retrospective
With multiple replicas, how do you design heartbeats and health checks? Are they the same thing?多实例部署下,怎么设计心跳和健康检查?两者是同一件事吗?
Common in ChinaCommon overseasIntermediate#observability#deployment#distributed-systemsHow to reason about it · think before answering
- The hinge is are they the same thing. Answering both check liveness loses the point — the interviewer wants to see you split one word into three distinct questions, because conflating them causes real outages.
- Separate them: a liveness probe answers should this process be restarted, a readiness probe answers can you send me traffic now, and a heartbeat dashboard answers what is the cluster's state. The audiences differ: the first two are for the orchestrator, the third is for a human.
- Then say why heartbeats are not optional: the orchestrator only sees process liveness, but a worker can be alive while doing no work at all — a blocked event loop, an exhausted connection pool timing out every read, a noisy neighbour saturating host CPU. This kind of zombie is exactly what the orchestrator cannot see, and only an application-level heartbeat catches it.
- Get the direction right too: replicas push their own heartbeat rather than the gateway polling each one. Containers change IP and hostname constantly, so a poller needs a roster that is always changing — and maintaining that roster is what heartbeats are for, so the logic is circular. Report at least three things: a timestamp for liveness, in-flight count to distinguish idle from overloaded, and a version so you can watch old and new replicas during a rollout.
- The sharpest point is isolation: do not query downstream dependencies inside a readiness probe. One worker going quiet would turn every gateway's readiness red, and the orchestrator would pull the entire ingress layer — turning a non-critical fault into a full outage. In reality that worker's absence does not stop intake at all: messages sit in the stream, unacked ones get claimed by someone else, and its lease changes hands when the TTL expires.
- Expect: so how does the gateway decide whether a worker is usable? Answer that it does not, and does not need to — the gateway never assigns work to a specific worker; the consumer group and the lease decide that. Heartbeat data is for observability and alerting, not routing. Getting here shows you actually understand the layering.
分析过程 · 先想清楚再作答
- 题眼是「两者是同一件事吗」。答「都是探活」直接失分——面试官想看你能不能把一个词拆成三个不同的问题,因为混起来会造成真事故。
- 先拆问题:存活探针回答「这进程要不要被重启」,就绪探针回答「现在能不能给我发流量」,心跳面板回答「集群此刻是什么状态」。三者的读者不同:前两个给编排系统,第三个给人。
- 再说心跳为什么不可省:编排系统只能看到进程存活,而 Worker 完全可以进程活着而活儿全停——事件循环被死循环占住、连接池耗尽后取消息全超时、宿主机 CPU 被邻居打满。这类假死恰好是编排系统看不见的那种,只有业务自己上报的心跳能发现。
- 方向也要答对:心跳是副本自己 push,不是 Gateway 逐个 pull。因为容器随时换 IP 和主机名,去问的一方需要一份永远在变的名单,而那份名单本身就得靠心跳维护,逻辑绕回来了。上报内容至少三样:时间戳判活、在跑任务数区分闲和忙、版本号在滚动发布时看新旧两批各剩几个。
- 最关键的一刀是隔离性:**不要把下游依赖查进就绪探针**。一个 Worker 失联导致所有 Gateway 的就绪探针同时转红,编排系统会把整个接入层摘光——一个非核心故障被自己升级成全站不可用。而实际上那个 Worker 失联根本不影响接单:消息还在流里,没确认的会被别人接手,它的租约会因 TTL 到期而易主。
- 可以预期的追问:那 Gateway 怎么判断某个 Worker 可不可用?答「它不判断,也不需要判断」——Gateway 从不指定某个 Worker 干活,派活由消费组和租约决定,心跳的用途是观测和告警,不是路由。答到这里就说明你真的想清楚了分层。
Key points
- Split one word into three questions: liveness (restart me?), readiness (send me traffic?), heartbeat dashboard (what is the cluster doing?) — first two for the orchestrator, third for humans
- The orchestrator sees process liveness but not zombies (blocked loop, exhausted pool, stolen CPU), so an application-level heartbeat is mandatory
- Heartbeats must be pushed by replicas, not polled by the gateway: containers change IP constantly and polling needs a roster that heartbeats themselves maintain
- Report timestamp, in-flight count and version — for liveness, load, and rollout progress respectively
- Never query downstream dependencies in a readiness probe, or one quiet worker pulls the whole ingress layer and escalates a minor fault into an outage
- The gateway does not judge worker availability — the consumer group and lease assign work; heartbeats are for observability, not routing
答题要点
- 一个词要拆成三个问题:存活探针(要不要重启)、就绪探针(能不能发流量)、心跳面板(集群什么状态),前两个给编排系统、第三个给人
- 编排系统只看得见进程存活,看不见假死(事件循环卡住、连接池耗尽、CPU 被抢),所以业务层心跳不可省
- 心跳必须是副本 push 而不是 Gateway pull:容器随时换 IP,pull 需要一份靠心跳才能维护的名单,逻辑绕回来了
- 上报时间戳、在跑任务数、版本号三样,分别用于判活、区分忙闲、观察滚动发布进度
- 不要把下游依赖查进就绪探针,否则一个 Worker 失联会让整个接入层被摘掉,把非核心故障升级成全站不可用
- Gateway 不判断 Worker 可用性——派活由消费组和租约决定,心跳只用于观测告警,不用于路由
What is graceful shutdown, and why is killing a process outright risky? Walk through the steps.什么是优雅停机?为什么直接 kill 进程有风险?请说出具体步骤。
Common in ChinaCommon overseasIntermediate#deployment#reliability#operationsHow to reason about it · think before answering
- This question tests whether you have actually shipped a release. Reciting finish in-flight work before exiting is just the definition; the interviewer wants the cost, the steps, and the ordering.
- Make the cost concrete. Deploys, scale-downs, host maintenance and spot reclamation all send SIGTERM, wait a grace period, then SIGKILL. SIGKILL cannot be trapped, and landing it on a worker mid-agent-loop means: the run is stuck in running forever while the user watches a spinner; you already paid for the model call but never persisted the reply; the unacked message waits for the idle threshold before anyone claims it. One deploy cuts off dozens of conversations — that is the everyday cost.
- Then give three steps and stress that the order is fixed. One, stop accepting work: flip a flag so the consume loop stops reading from the stream (messages already fetched but not started stay in pending for someone else, which is faster than forcing a whole batch through). Two, wait for the in-flight execution, but with a ceiling. Three, proactively release leases, deregister from the heartbeat dashboard, and exit.
- The ceiling in step two earns points: a hung model call means you wait forever, and the grace period will SIGKILL you anyway. Better to concede and exit — the unacked message is still pending and someone will redo it. This course uses 20 seconds, derived from the upper bound of a normal execution plus margin.
- Step three also earns points: leases normally change hands via TTL expiry, but that path exists for sudden death. On a planned shutdown you know you are leaving, so releasing proactively lets the successor take over on its next scan instead of waiting out a full TTL. The release must be conditional — delete only the badge that still bears your name, or you will tear down the badge of whoever just claimed it after your lease expired.
- Finish with two companions; miss either and the rest is wasted. The configured grace period must exceed the wait ceiling in code (code waits 20s while compose defaults to 10s, so SIGKILL lands at second 10 and your three steps only half-run). And the signal must actually reach your process (if the entrypoint is a package manager, PID 1 is the package manager, SIGTERM may never arrive, and your shutdown code never runs once).
分析过程 · 先想清楚再作答
- 这题考的是「你有没有真的发过版」。答「等任务跑完再退出」只是定义,面试官要的是代价、步骤和顺序。
- 先把代价说具体。发版、缩容、机器维护、抢占式实例回收都会先发 SIGTERM、等宽限期、超时 SIGKILL。SIGKILL 拦不住,落到正在跑 Agent 循环的 Worker 身上:这次的 run 永远停在 running,用户界面一直转圈;模型调用的钱已经付了,回复却没落库;没确认的消息要等空闲阈值到了才被别人接手,用户白等一轮。一次发版掐断几十次对话,这就是日常代价。
- 然后给三步,强调顺序不能变:第一步拒新——把开关拨过去,消费循环下一轮不再从流里取消息(已经读到手上还没开始的那几条,留在 pending 里由别人接手,比硬扛完一整批更快);第二步等手头这次执行跑完,但要有上限;第三步主动交还租约、从心跳面板注销,然后退出。
- 第二步的上限是加分点:一次卡死的模型调用会让你永远等不到,而宽限期一到照样 SIGKILL。与其被动挨刀,不如自己认输退出——没确认的消息还在 pending 里,别人会接手重做。本课取 20 秒,取法是「一次正常执行的耗时上限」再留余量。
- 第三步也是加分点:租约本来靠 TTL 到期自然易主,但那是为进程猝死准备的。计划内下线你明知道自己要走,主动交还能让接手方下一轮扫描就上岗,而不是白等一个 TTL。交还必须带条件——只删还写着自己名字的那把牌子,否则租约已过期、别人刚抢到时,你就把对方的值班牌撕了。
- 最后两件配套的事,漏一件前面全白做:宽限期的配置必须大于代码里的等待上限(代码等 20 秒而 compose 默认只等 10 秒,第 10 秒就 SIGKILL,三步只走到一半);以及信号得真的传到你的进程(启动命令写成包管理器,PID 1 就是包管理器,SIGTERM 未必传得到,停机代码一次都不会执行)。
Key points
- Concrete cost of a hard kill: the run is stuck in running, the user stares at a spinner, the model call is paid for but the reply is unsaved, and the unacked message waits out the idle threshold
- Three steps in a fixed order: refuse new work, wait for in-flight work with a ceiling, then release leases and deregister before exiting
- The wait needs a ceiling (20s here): a hung model call never returns and the grace period kills you anyway, so concede — the message is still pending for someone else
- Releasing leases proactively lets the successor start on its next scan instead of waiting a full TTL; the release must be conditional on still owning it
- The configured grace period must exceed the in-code wait ceiling, or the three steps only half-run (stop_grace_period / terminationGracePeriodSeconds)
- Make sure the signal reaches your process: exec the business process directly rather than letting a package manager be PID 1
答题要点
- 直接 kill 的具体代价:run 永远停在 running、用户界面一直转圈、模型的钱已付但回复没落库、没确认的消息要等空闲阈值才被接手
- 三步且顺序不能变:拒绝新任务 → 等手头的跑完(有上限)→ 主动交还租约并注销心跳,然后退出
- 等待必须有上限(本课 20 秒):卡死的模型调用会让你永远等不到,宽限期一到照样被 SIGKILL,不如自己认输,消息还在 pending 里
- 主动交还租约让接手方下一轮就上岗,而不是白等一个 TTL;交还必须条件化,只删还写着自己名字的那把
- 宽限期配置必须大于代码里的等待上限,否则三步只执行到一半(compose 的 stop_grace_period / K8s 的 terminationGracePeriodSeconds)
- 信号要真传到进程:用 exec 形式直接起业务进程,别让包管理器当 PID 1
During a rolling deploy, how do you keep in-flight tasks from being interrupted?滚动发布时,如何避免正在处理的任务被打断?
Common in ChinaCommon overseasIntermediate#deployment#reliability#operationsHow to reason about it · think before answering
- This is the applied version of the previous question, and the difference is that it demands the orchestrator's side too — describing only the in-process steps answers half of it.
- The full skeleton is both sides cooperating: the orchestrator first removes traffic (turns readiness red so the load balancer stops sending new requests), then sends SIGTERM, then waits out the grace period; the process uses that window to finish in-flight work, hand back ownership, and exit cleanly. That sentence is the trunk; everything else is detail.
- Then distinguish the two kinds of replica, which is where the points are. A gateway has inbound connections, so draining traffic means something for it. A worker has no inbound connections at all — it pulls work from the bus, so draining for it means stop fetching new messages, which is step one of graceful shutdown. The same word is two different mechanisms on the two replica types, and saying so shows you understand pull versus push.
- Next, batching and ordering: replace only a subset at a time (manual batches in compose, maxUnavailable / maxSurge in Kubernetes) so enough replicas are always alive to absorb traffic. This is where the version field in the heartbeat payload pays off — you can see how many old and new replicas remain instead of deploying blind.
- Also mention state compatibility: during a rolling deploy old and new code run simultaneously, so schema migrations must be backward compatible (add a nullable column, dual-write, drop the old column last) and message formats cannot change in one shot. Many candidates miss this layer — however gracefully processes stop, two versions that cannot read the same data will still cause an incident.
- Expect: what if a single execution legitimately takes five minutes and the grace period cannot wait that long? The answer is not to stretch the grace period to five minutes but to make the task interruptible and resumable — break long work into steps that checkpoint progress (the run state machine from D11 plus at-least-once with idempotency from D9 give you exactly this), so the next replica continues the interrupted step.
分析过程 · 先想清楚再作答
- 这题是上一题的应用题,区别在于它要求你把编排系统那一侧也讲进来——只讲进程内的三步只答了一半。
- 完整骨架是两侧配合:编排系统先摘流量(把就绪探针转红,让负载均衡不再把新请求打过来)、再发 SIGTERM、然后等宽限期;进程在这段时间里把手头的活做完、交还所有权、干净退出。这一句话就是答案的主干,剩下都是细节。
- 然后区分两类副本,这是拿分点。Gateway 有入站连接,摘流量对它有意义;Worker 没有任何入站连接,它是自己去总线取活的,所谓「摘流量」对它就是「自己不再取新消息」——也就是停机三步的第一步。**同一个词在两类副本上是两种机制**,能说清这一点说明你理解拉与推的差别。
- 接着讲批次与顺序:一次只换一部分副本(compose 里手动分批,K8s 里靠 maxUnavailable / maxSurge),保证任何时刻都有足够的存活副本接得住流量。心跳面板上的版本号字段这时派上用场——你能看到新旧两批各剩几个,而不是盲发。
- 还要提一句状态兼容:滚动发布期间新旧代码同时在线,所以数据库迁移必须向后兼容(先加可空列、再双写、最后才删旧列),消息格式也不能一次性改。这是很多人漏掉的一层——进程停得再优雅,新旧版本读不了同一份数据照样出事故。
- 可以预期的追问:如果一次执行本来就要跑 5 分钟,宽限期不可能等那么久怎么办?答案不是把宽限期拉到 5 分钟,而是让任务可中断可重入——把长任务切成可保存进度的小步(D11 的 run 状态机和 D9 的 at-least-once 加幂等正好提供了这个基础),被打断的那一步由下一个副本接着做。
Key points
- The full skeleton is both sides: orchestrator drains traffic, sends SIGTERM, waits the grace period; the process finishes in-flight work, hands back ownership, exits cleanly
- Draining means two different things for gateways and workers: readiness turning red versus the worker itself stopping its fetch from the bus
- Replace in batches (maxUnavailable / maxSurge or manual) so enough replicas stay alive; the version field in heartbeats shows how many old and new remain
- Old and new code run concurrently, so migrations must be backward compatible (nullable column, dual-write, drop last) and message formats cannot change in one step
- Long tasks are not solved by a longer grace period but by being interruptible and resumable — checkpointed steps that the next replica can continue
答题要点
- 完整骨架是两侧配合:编排系统先摘流量、再发 SIGTERM、等宽限期;进程在这段时间做完手头的活、交还所有权、干净退出
- Gateway 和 Worker 的「摘流量」是两种机制:前者靠就绪探针转红让负载均衡停止转发,后者靠自己不再从总线取新消息
- 分批替换(maxUnavailable / maxSurge 或手动分批),保证任何时刻有足够存活副本;心跳里的版本号让你看到新旧两批各剩几个
- 新旧代码同时在线,所以数据库迁移必须向后兼容(加可空列 → 双写 → 最后删旧列),消息格式不能一次性改
- 长任务不该靠拉长宽限期解决,而要做成可中断可重入:切成能保存进度的小步,被打断的那步由下一个副本接着做