Spring可观测性独立面试题
本页只负责“面试时怎样准确回答”。完整自动配置源码、生命周期、传播链、可运行Demo、失败窗口和生产Runbook统一放在Spring Boot 3可观测性内部原理与生产治理中。
版本必须先讲清楚:JDK 8、Spring Boot 2.7.x、Spring Cloud 2021.x的存量系统常见Sleuth 3.x与Brave;Java 17+、Spring Boot 3.x的现代系统应以Micrometer Observation、Micrometer Tracing和OpenTelemetry或Brave桥接为主。下面以Boot 2.7.18/Sleuth 3.1.9和Boot 3.2.4/Micrometer Tracing 1.2.4/OTel SDK 1.31.0作为可复核样本,不把某个样本版本的默认值误认为所有版本都永久不变。
回答主线
flowchart TD
A["先说明版本基线"] --> B["再说对象与执行链"]
B --> C["说明失败窗口"]
C --> D["给出验证证据"]
D --> E["落到生产治理"]好的回答不能停留在“引入Starter就有traceId”。至少要能说清:谁创建Observation、谁创建Span、上下文怎样跨进程和跨线程传播、采样在哪一层决定、Span怎样异步导出、Collector失败会发生什么,以及如何用指标、日志和业务事实交叉验证。
一、版本、定位与核心对象
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| Spring Boot 2和Boot 3的链路追踪主线有什么区别 | Boot 2存量项目常见Spring Cloud Sleuth自动接入Brave,并通过Zipkin Reporter导出;Boot 3不再以Sleuth为主线,而是由Micrometer Observation表达被观测操作,Micrometer Tracing提供追踪门面,再桥接OpenTelemetry或Brave。迁移不是只换依赖,还要核对传播格式、采样、MDC字段、Span命名和Exporter。 | 为什么Sleuth依赖不能原样升到Boot 3? | Sleuth迁移 |
| Java 17和Spring Boot 3是什么关系 | Spring Boot 3的最低Java基线是17,因此新项目可以使用Java 17语言与运行时能力;JDK 8存量代码则不能复制Record、文本块、Map.of或String.isBlank等现代写法。业务原理可以相同,但依赖、API和编译目标必须分别验证。 | Java 21能否运行Boot 3? | 版本基线 |
| 可观测性和监控有什么区别 | 监控通常围绕预先定义的指标发现已知问题;可观测性要求从系统外部信号推断内部状态,组合Metrics、Traces、Logs和必要的Profiles探索未知故障。它不是某一个产品,也不是只打印traceId。 | 四类信号分别回答什么? | 完整数据链 |
| Observation是什么 | Observation是Micrometer对“一个被观测操作及其生命周期”的抽象,包含名称、上下文、标签、错误和Scope。它本身既不是Span也不是Metric,但不同Handler可以在同一生命周期上产生计时指标、Span和其他副作用。 | Observation一定生成Span吗? | 核心对象 |
| ObservationRegistry是什么 | Registry保存全局配置、Handler、Convention、Predicate、Filter以及当前Observation访问能力。Boot自动配置会创建默认Registry并通过后处理器把相关Bean装配进去。它不是遥测存储,也不负责把Span永久保存。 | 自定义Registry会怎样? | Boot启动 |
| ObservationHandler是什么 | Handler监听Observation的start、scope、error和stop等事件,并按支持的Context执行动作。Tracing Handler创建和结束Span,Meter Handler记录Timer等指标;注册顺序和逆序关闭影响嵌套关系与上下文恢复。 | supportsContext有什么意义? | 生命周期 |
| ObservationConvention解决什么问题 | Convention把框架或业务Context转换为统一的名称、低基数和高基数KeyValue,使不同调用点遵守相同语义。它避免每个团队随意命名Span和Metric,但不能把无限业务ID安全地变成Metric标签。 | GlobalConvention与局部Convention谁优先? | 基数治理 |
| Micrometer Tracing是什么 | 它是Spring生态的追踪门面,提供Tracer、Span、Propagator和CurrentTraceContext等抽象,可桥接OTel或Brave。它不等于OpenTelemetry SDK、Collector或Trace后端。 | 门面带来什么价值和限制? | OTel自动配置 |
| OpenTelemetry是什么 | OpenTelemetry是一套跨语言遥测规范、API、SDK、语义约定、传播协议和Collector生态。OTel SDK负责采样、处理和导出,但通常不承担长期存储与查询,后端可以是Tempo、Jaeger、Zipkin或商业APM。 | API、SDK、Collector和Backend怎样分工? | Collector |
| Span、Trace、traceId和spanId是什么关系 | Trace表示一次端到端因果链,共享同一个traceId;Span表示链中的一个操作,每段有自己的spanId,并通过parent span形成树。异步批量等非单一父子关系可使用Span Link表达。 | 为什么不能只靠traceId表达调用树? | HTTP入口 |
二、Boot 3自动配置与生命周期
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| Boot 3启动时可观测性组件怎样装配 | 条件满足时Boot先创建ObservationRegistry,再收集Predicate、Convention、Filter和Handler;Tracing自动配置创建Tracer和传播器,OTel自动配置创建Sampler、SdkTracerProvider、SpanProcessor、Exporter和Micrometer桥,Web及HTTP客户端随后接入Registry。 | 缺少Actuator或Bridge会在哪层断开? | 启动链 |
| 一个Observation从开始到结束怎样执行 | start阶段解析Convention并按注册顺序调用Handler;openScope把它设为当前上下文;error记录异常并通知Handler;stop前再次整理语义并应用Filter,然后按逆序执行onStop。Scope和Observation都应在finally或try-with-resources语义下正确关闭。 | 为什么stop要逆序? | 生命周期 |
openScope()和stop()区别是什么 | openScope/close控制当前线程看到哪个Observation及MDC上下文;start/stop控制这次操作的计时、Span和Metric是否真正完成。只stop不关Scope可能污染后续线程,只关Scope不stop会留下未结束Span和不完整计时。 | try-with-resources应该包哪一个对象? | Scope与停止 |
为什么异常必须调用error() | 仅抛异常不保证所有手工Observation都自动记录错误。error()把Throwable放入Context并触发Handler,使Span状态、错误事件和指标结果获得一致语义;随后仍要stop。敏感异常信息不能无界写入标签。 | error后是否还要stop? | Scope与停止 |
| Observation Predicate有什么用 | Predicate在创建或启动观察时按名称与Context决定是否启用,可用于关闭高成本、低价值或有风险的观测。它是埋点级过滤,不等同于Trace采样;被Predicate拒绝时连对应Observation副作用都可能不产生。 | 与Sampler有什么区别? | Boot自动配置 |
| Observation Filter有什么用 | Filter在Observation结束前修改Context,例如统一补充结果码或清理属性。它应保持轻量、确定和低风险,不能在stop路径执行慢I/O,否则会给业务请求尾部增加延迟。 | Filter何时执行? | 生命周期 |
| 为什么一个Observation能同时产生Metric和Span | Registry可以同时注册MeterObservationHandler与TracingObservationHandler。它们监听同一生命周期,但前者把低基数维度聚合为时间序列,后者创建带上下文的Span;Boot还会对Meter与Tracing Handler分组以维持正确顺序。 | 两者标签能否完全相同? | Metrics与Tracing |
@Observed和手工Observation怎样选 | @Observed适合方法边界清晰、名称稳定的通用埋点;手工Observation适合需要精确控制Context、错误、Scope和业务阶段的操作。框架已自动覆盖HTTP入口时,不应为Controller、Service、Repository机械地每层再建Span。 | 自调用为什么可能不生效? | 手工埋点 |
三、HTTP传播、MDC与远程调用
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| 一个HTTP入口怎样加入已有Trace | Receiver Handler通过Propagator从请求Header Carrier提取远程Context;提取成功就创建其子Server Span,失败或没有合法上下文则创建新的根Trace。Scope激活后,业务代码、日志和下游客户端才能看到当前Span。 | 非法traceparent如何处理? | Receiver链 |
| HTTP客户端怎样传播Trace | Sender Handler基于当前Span创建Client Span,把新的传播上下文通过Propagator注入请求Header,再由受框架管理的HTTP Client发送。下游提取后形成同一traceId下的Server Span。直接new未接入观测的客户端可能绕过这条链。 | Feign在哪一层接入? | Sender链 |
W3C traceparent包含什么 | 它包含版本、32个十六进制字符的trace-id、16个十六进制字符的parent-id和trace-flags。它传播追踪因果和采样标志,不传播登录身份,也不能把来自公网的值当可信授权数据。 | tracestate作用是什么? | 传播格式 |
| W3C和B3怎样迁移 | 先盘点入口、出口、网关、MQ和APM支持的格式;迁移窗口可配置消费多格式并选定一种主输出格式,确认跨语言链不断后再收敛。永久同时传播多格式会增加Header、歧义和测试成本。 | B3 single与multi有什么区别? | 传播迁移 |
为什么手写X-Trace-Id不是完整Tracing | 自定义UUID只能做日志关联,通常没有Span父子、Span生命周期、采样、标准传播、上下文Scope、Exporter和Collector链。可以作为未接框架的存量兜底,但不能冒充完整分布式追踪。 | 老系统暂时不能迁移怎么办? | HTTP入口 |
| traceId怎样进入日志 | Tracing Scope被激活时,MDC监听器把当前traceId和spanId写入SLF4J MDC;Scope恢复或关闭时还原或移除。日志Pattern读取这些MDC键,因此日志关联依赖正确的Scope生命周期。 | 为什么子Span结束后要恢复父Span ID? | MDC |
| 日志traceId错乱常见原因是什么 | 常见原因是线程池复用却未清MDC、手写Filter覆盖框架MDC、异步任务只复制MDC却没复制Tracing Context、Scope未在finally关闭,或日志发生在线程上下文建立前。应同时验证当前Span和MDC,而不是只补UUID。 | 如何复现线程污染? | MDC错乱 |
| Baggage能否存userId和token | Baggage会跨服务传播并增加每次请求Header,应使用白名单、长度限制和脱敏。token、身份证、完整用户信息等敏感数据不应放入Baggage;Baggage也不会天然自动变成Span属性或Metric标签。 | Baggage与MDC有什么区别? | 传播与安全 |
| 外部传入traceId能否作为幂等键 | 不能。Trace Header来自不可信调用方,可能重复、伪造或被采样策略改变,只用于诊断关联。幂等键必须按业务主体、操作和请求语义设计,并由服务端持久化校验。 | traceId能否作为订单号? | 安全治理 |
四、异步、线程池、Reactor与消息队列
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| 线程池为什么会让链路断开 | 当前Trace Context通常绑定提交线程的ThreadLocal,而任务在另一个可复用线程执行,ThreadLocal不会天然跨线程。正确做法是在提交时捕获Context,执行前恢复,finally关闭并还原旧Context。 | 为什么不能只在执行时捕获? | 异步传播 |
| Micrometer Context Propagation怎样工作 | ContextRegistry注册不同ThreadLocalAccessor;ContextSnapshot在提交点捕获可访问上下文,在执行线程建立Scope并在完成后恢复。它可包装Runnable、Callable和ExecutorService,但仍要控制捕获时机与清理边界。 | Accessor漏注册会怎样? | 异步传播 |
| CompletableFuture为什么经常丢traceId | 未显式传Executor的异步方法常进入公共ForkJoinPool,它通常没有应用的Context包装;即使第一个阶段已传播,后续切换Executor的阶段也可能断链。应显式使用受管、可监控且已包装的Executor。 | thenApply与thenApplyAsync有何影响? | CompletableFuture |
| Reactor为什么不能只依赖ThreadLocal | Reactor事件可能在线程间切换,同一线程也可能交错处理多个订阅,ThreadLocal与逻辑订阅不一一对应。上下文应随Reactor Context传播,并由框架在需要时桥接到ThreadLocal;手工阻塞和随意切Scheduler会破坏链路。 | publishOn后上下文在哪里? | Reactor传播 |
| MQ生产者怎样传播Trace | 生产者创建Producer/Sender Span,把标准追踪上下文注入消息Header后发送;消费者从Header提取并创建Consumer/Receiver Span。Header白名单、序列化和中间件转发不能把传播字段丢掉。 | 重试消息的Span怎样建? | MQ追踪 |
| 批量消费为什么适合Span Link | 一批消息可能来自多个Trace,无法选择一个父Span准确代表所有因果关系。消费批次Span可使用Link关联多个生产Span,并对Link数量设上限,避免把批次错误地挂到第一条消息下面。 | 单消息消费还需要Link吗? | MQ追踪 |
| 定时任务没有上游Context怎么办 | 定时任务本身是新的入口,应创建新的根Observation/Span,并记录稳定的任务名、调度实例和结果;不要复用上一次HTTP请求残留的MDC。任务触发下游调用时再由Sender传播新Context。 | XXL-JOB分片怎样关联? | 手工Observation |
| 为什么只复制MDC仍不够 | MDC只是日志键值,不包含Tracer的当前Span、采样决定和传播能力。只复制traceId字符串可能让日志看似连续,但新建Client Span时仍找不到正确父Context,形成“日志不断、Trace已断”的假象。 | 应复制哪个Context? | MDC与异步 |
五、采样、标签与成本控制
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| Head Sampling是什么 | 在Trace开始附近按概率、父决定或属性做采样,能尽早降低SDK、网络和存储成本;缺点是此时还不知道请求最终是否报错或高延迟,因此可能漏掉最有诊断价值的Trace。 | ParentBased有什么作用? | 采样 |
| Boot 3.2.4默认采样率是多少 | 该样本的management.tracing.sampling.probability默认是0.10,并使用parent-based的trace-id ratio采样。回答时必须带版本限定,实际项目应查目标Boot版本的配置元数据和运行配置。 | 为什么不能死背10%? | 采样默认值 |
| Tail Sampling是什么 | Collector先聚合同一Trace的Span,等待一段时间后按错误、延迟、属性等最终结果决定是否保留。它诊断价值高,但需要内存、等待窗口、同Trace路由和容量保护,不是免费地“错误全保留”。 | Trace分散到多个Collector怎么办? | 尾采样 |
| 为什么请求结束后不能把未采样Trace改成完整采样 | Head阶段若决定不记录,过程中的Span事件和属性可能根本没有完整保存;结束时只知道错误也无法凭空恢复此前丢弃的数据。要保留错误Trace应使用合适的头采样策略、记录型采样或Collector尾采样。 | 错误100%保留怎样设计? | 错误采样 |
| 低基数和高基数有什么区别 | 低基数值集合有限且稳定,如规范化route、method、status,适合作Metric标签;高基数值如orderId、userId、原始URL、异常文本,组合会爆炸,应进入受控Trace或脱敏日志。 | SKU能否做Metric标签? | 基数 |
| 为什么traceId不能作为Prometheus Label | 每个请求的traceId几乎唯一,会让时间序列数量接近请求数,引发内存、索引、存储和查询爆炸。Metrics应聚合,具体Trace通过Exemplar或日志关联查询。 | Exemplar怎样解决跳转? | Metrics闭环 |
| Span是否越多越好 | 不是。每个Span都有创建、属性、队列、导出和存储成本。应覆盖远程边界、关键业务阶段和真正有诊断价值的耗时,不应机械地为每个Getter或每层方法建Span。 | 怎样制定Span预算? | 反模式 |
| 100%采样什么时候可用 | 低流量测试环境、短期受控诊断或监管要求且容量经过评估的关键链路可以考虑;高流量生产全量采样会显著增加CPU、内存、网络、Collector和后端成本,必须有容量模型与退出机制。 | 临时全采后忘记恢复怎么办? | 采样治理 |
六、OTel SDK、OTLP与Collector
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| OTel Bridge做什么 | OtelTracer把Micrometer Tracing调用桥接到OTel API/SDK,OtelPropagator负责提取和注入,OtelCurrentTraceContext维护当前上下文。Bridge不是Exporter,也不负责后端存储。 | 换成Brave Bridge会影响什么? | OTel桥 |
| SdkTracerProvider做什么 | 它是OTel SDK创建Span、应用Sampler并把结束Span交给SpanProcessor的核心入口。应用能拿到Tracer不代表Span一定会导出,还取决于采样、Processor、Exporter和后续链路。 | Provider关闭时要做什么? | OTel自动配置 |
| BatchSpanProcessor为什么存在 | 它把业务线程结束的Span放入有界队列,由后台线程按批次和时间交给Exporter,降低逐Span同步网络开销并隔离遥测延迟。队列满时会丢Span而不是无限阻塞或无限占内存。 | 与SimpleSpanProcessor区别? | 批处理器 |
| OTel SDK 1.31.0批处理默认值是什么 | 该源码样本默认调度延迟5000ms、最大队列2048、最大导出批次512、导出超时30000ms。它们是特定版本默认值,不是所有Boot或SDK版本的永久契约,生产应按吞吐、故障时长和内存预算校验。 | 队列容量怎样估算? | 批处理器 |
| 为什么应用正常退出仍可能丢最后一批Span | Span可能仍在BatchSpanProcessor队列中,进程在下一次定时导出前结束。应走框架优雅关闭,给forceFlush/shutdown留出有界时间;kill -9、容器宽限期过短和异常崩溃无法保证刷新。 | K8s该怎样配宽限期? | 关闭丢失 |
| OTLP是什么 | OTLP是OpenTelemetry遥测传输协议,可通过HTTP/protobuf或gRPC承载。常见默认端口分别是4318与4317,但地址、路径、TLS、Header和代理应以部署配置为准。 | Boot 3.2.4自动配置哪种Exporter? | OTLP |
| Boot 3.2.4怎样配置OTLP Trace出口 | 样本使用management.otlp.tracing.endpoint触发HTTP/protobuf Exporter连接配置,并可设置超时和gzip。若用户提供自定义OTLP gRPC Exporter,自动配置会按条件退让,不能同时假设两套Exporter都按默认生效。 | endpoint没配会怎样? | OTLP自动配置 |
| Collector Pipeline有哪些阶段 | Receiver接收OTLP等协议,Processor做内存保护、资源补充、过滤、批处理或尾采样,Exporter写入一个或多个后端。生产上还要观测accepted、refused、dropped、queue和export failed。 | Processor顺序为什么重要? | Collector |
| Agent Collector和Gateway Collector怎样选 | Agent靠近应用,降低跨网络出口复杂度并可做初步批处理;Gateway集中做租户、路由、尾采样和后端出口。大规模系统常分层组合,但每层都要有界,避免遥测积压变成业务资源事故。 | 尾采样通常放哪层? | Collector拓扑 |
| Collector挂了应该阻塞业务吗 | 不应。应用侧导出应异步、有界、超时和可丢,并对丢弃产生自身指标;Collector故障不能把支付、订单或采集线程同步卡住。代价是故障窗口内部分遥测可能丢失,需要Metrics和业务审计兜底。 | “零丢失Trace”现实吗? | 失败窗口 |
七、生产场景与排查题
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| Trace平台完全没有数据怎样排查 | 从应用到后端逐层查:依赖与配置、Registry/Tracer/Exporter Bean、采样、手工Observation、SDK队列、Exporter网络、Collector accepted/refused/dropped、后端写入,最后查UI时间范围、服务名和租户。不能一上来只重启Collector。 | 怎样证明应用已生成Span? | 无Trace Runbook |
| 某个服务开始traceId断链怎样排查 | 对比上游Client Span与下游Server Span的traceId,抓取脱敏后的传播Header,检查是否使用未受管HTTP Client、代理是否删Header、两端传播格式是否兼容;MQ则查消息Header和重试链。 | 只有日志连续但Trace断了说明什么? | 断链 Runbook |
| 异步任务日志没有traceId怎样排查 | 确认提交线程与执行线程,检查是否用了公共ForkJoinPool、是否在提交时捕获Context、Accessor是否注册、执行结束是否finally清理,并排除手写MDC覆盖框架。 | 如何做最小复现? | 异步 Runbook |
| 为什么错误请求在Trace平台查不到 | 可能在Head Sampling阶段已被丢弃,也可能Exporter、Collector、Tail Sampling或后端失败。用全量错误Metric和业务日志证明错误,再查父采样决定、Collector路由与尾采样策略,不能仅临时把全局采样改成100%。 | 如何长期保证关键错误可诊断? | 错误采样 Runbook |
| Collector故障后业务变慢怎样定位 | 检查Exporter是否在业务线程同步阻塞、DNS与连接超时、后台重试日志风暴、队列内存和GC。目标是遥测失败快速且有界,恢复后也要限制积压回放,防止冲垮后端。 | 哪些指标能证明Exporter背压? | Collector故障 Runbook |
| Prometheus时间序列突然暴涨怎样处理 | 找出新增Metric及标签组合,重点检查原始URL、订单号、用户ID、异常文本;改为规范化route和有限状态码,高基数转入Trace或脱敏日志。删Dashboard不能消除源头序列,必须修埋点。 | 怎样在发布前阻断高基数? | 基数 Runbook |
| Trace平台没数据能否说明请求没发生 | 不能。可能未采样、传播中断、队列满、Exporter失败、Collector拒绝、尾采样丢弃、后端写失败或查询条件错误。业务是否发生必须看数据库事实、审计、消息状态和日志等权威证据。 | 哪个信号是交易成功的最终事实? | 失败窗口 |
| 观测链有哪些典型丢失窗口 | 创建前未埋点、Head Sampling不记录、线程或协议传播断裂、Batch队列满、进程未flush、Exporter超时、Collector拒绝或采样、Backend写入失败、UI查询错误都可能造成不同形式的缺失。 | 每层怎样建立自监控? | 失败窗口 |
| 如何把Metrics、Trace和Logs串起来 | Metrics用低基数维度发现错误率与延迟,Histogram Exemplar跳到代表性Trace,Trace按Span定位服务和操作,再用traceId检索脱敏日志;最终以业务状态或审计确认结果。不要把traceId直接做Metric标签。 | 没有Exemplar怎么办? | 三类信号闭环 |
| 如何设计可观测性SLO | 先定义用户可感知SLI,如成功率和延迟分布,再设SLO与错误预算;告警使用多窗口Burn Rate减少只看瞬时CPU的噪声。遥测链自身也要有接收、丢弃、导出失败与延迟SLO。 | SLI和SLA有什么区别? | 分布式可观测性 |
八、迁移、安全与架构判断
| 面试题 | 标准回答 | 常见追问 | 原理跳转 |
|---|---|---|---|
| Sleuth迁移到Boot 3的步骤是什么 | 先锁定Java 17、Boot和Cloud兼容矩阵;移除Sleuth并引入Actuator、Micrometer Tracing Bridge及Exporter;映射采样与Exporter配置;校验W3C/B3、MDC字段、Span名称和自定义Bean;再灰度比较Trace连续性、采样量、延迟和成本。 | 能否一次全量切换传播格式? | 迁移主线 |
| 迁移期间怎样避免新旧服务断链 | 先确认所有语言和代理支持范围,必要时让接收端暂时兼容W3C与B3,发送端保持单一主格式;用跨版本端到端测试验证Gateway、Feign、MQ和异步任务,稳定后再去掉旧格式。 | 双格式传播有什么副作用? | 传播迁移 |
| Sleuth的自定义Sampler能否直接复制到Boot 3 | 不能假设API兼容。Boot 3可能通过Micrometer Tracing与底层OTel或Brave Sampler配置,Bean类型、属性名和父采样语义都不同;应按目标桥实现重新映射并用实际Span数量验证。 | OTel ParentBased怎样影响子Span? | 迁移 |
| 为什么遥测数据也要做安全治理 | Span、Baggage和日志可能包含请求路径、账号、医疗或支付信息,Collector与Backend又会集中存储并跨团队查询。必须做最小采集、脱敏、TLS、认证、租户隔离、权限、保留期和审计。 | Authorization能否写Span属性? | 安全 |
| 为什么不能记录完整请求体和响应体 | 内容可能包含密码、token、身份证、病历或支付数据,还会造成Span过大、网络与存储成本上升。应记录稳定错误码、长度、摘要或经过审批的白名单字段,并在采集端和后端双重治理。 | 调试时临时开启怎么办? | 安全 |
| 观测系统是否应该进入核心事务 | 不应把Exporter或Collector确认作为订单事务提交条件,否则遥测故障会扩大成业务不可用。核心业务先保证自身事务和审计,遥测异步、有界、允许受控丢失,并通过自身告警暴露退化。 | 合规审计是否也能异步丢失? | 失败边界 |
| SkyWalking和Micrometer/OTel是否互斥 | 不一定。SkyWalking可作为探针和APM平台,Micrometer/OTel是应用观测门面与标准生态;组合取决于Agent、字节码增强、SDK重复埋点和后端接收能力。必须避免同一HTTP调用生成重复Span和双倍导出。 | 如何识别重复埋点? | 反模式 |
| 什么时候需要手工业务Span | 当操作有独立业务意义、跨多个内部步骤、需要单独耗时和错误边界时,例如库存预留、批次生成、风控决策。普通Controller或Repository若已有框架Span,不应重复机械埋点。 | Span名称怎样保持低基数? | 手工Observation |
九、项目回答模板
我们先按版本拆分:JDK 8、Boot 2.7存量服务保留Sleuth 3.1.x和Brave,在兼容窗口同时接收B3与W3C;Java 17、Boot 3新服务使用Actuator、Micrometer Observation/Tracing桥接OpenTelemetry。HTTP入口由Receiver提取上下文,Feign或受管HTTP客户端由Sender创建Client Span并注入,下游创建Server Span;线程池在提交时捕获Context、执行时恢复并在finally清理,批量MQ使用Span Link表达多来源。
指标只使用route、method、status等低基数标签,订单号进入受控Trace和脱敏日志;SDK使用有界BatchSpanProcessor,经OTLP发送Collector,Collector做内存保护、批处理、尾采样和路由。我们同时监控accepted、dropped、export failed与队列,Collector不可用时允许受控丢遥测但不能阻塞订单线程。告警先从错误率和P99指标发现,再通过Exemplar或traceId进入Trace与日志,最后以数据库状态和审计确认交易事实。
十、面试自检清单
- 能先说明JDK 8/Boot 2存量线与Java 17+/Boot 3现代线,而不是混用依赖和属性。
- 能画出Observation、Handler、OTel SDK、BatchSpanProcessor、OTLP、Collector和Backend的完整链。
- 能说清Scope关闭与Observation停止的区别。
- 能解释Receiver提取、Sender注入、W3C/B3迁移和MDC恢复。
- 能解释线程池、CompletableFuture、Reactor和MQ为什么断链。
- 能区分Head Sampling、Tail Sampling、Predicate和Metric聚合。
- 能说明队列满、关闭未flush、Exporter失败和Collector拒绝时丢什么。
- 能按应用、SDK、Collector、Backend、UI顺序排查无Trace。
- 能说明高基数、敏感数据和观测故障拖垮业务的风险。
- 能把标准回答跳转到知识点页继续解释源码和Demo。
本章小结
可观测性面试的分水岭不是会不会背traceId,而是能否基于明确版本讲清“Observation生命周期、跨边界传播、采样与有界导出、Collector数据链、失败证据和生产治理”。如果某个问题只能回答一个组件名,应回到对应原理锚点完整推演一次。
