线程池一切换 traceId 就丢,我在 gRPC 的拦截器和异步调用链上做了三层上下文固定
gRPC 链路追踪中 traceId 丢失的核心原因是线程切换导致基于 ThreadLocal 的上下文断链。解决方案分三层:拦截器提取元数据写入 gRPC Context;线程池切换时用 Context.wrap() 显式传递;异步回调通过 attach/detach 重新绑定。同时将 traceId 打入 MDC 并处理客户端拦截器,确保全链路日志可追踪。
共 3 篇文章
gRPC 链路追踪中 traceId 丢失的核心原因是线程切换导致基于 ThreadLocal 的上下文断链。解决方案分三层:拦截器提取元数据写入 gRPC Context;线程池切换时用 Context.wrap() 显式传递;异步回调通过 attach/detach 重新绑定。同时将 traceId 打入 MDC 并处理客户端拦截器,确保全链路日志可追踪。
gRPC 服务每隔两分钟规律性断连,根源是客户端 keepalive 发送频率(30秒)远高于服务端允许的最小间隔(120秒),导致服务端主动踢掉连接。核心约束是客户端 `keepalive_time_ms` 必须大于等于服务端 `permit-keepalive-time`,否则必然触发 `ENHANCE_YOUR_CALM` 错误。调参需先摸清网络中间件 idle timeout,确保客户端心跳周期小于该值,再让服务端容忍度匹配客户端频率,并显式开启空闲连接心跳。
gRPC 四种服务端类型各有适用场景,选错代价高昂。Unary 适合一问一答,天然支持背压控制;Server Streaming 用于数据推送,但必须配合分页和超时机制防止内存溢出;Client Streaming 适合批量上传,需在收到 EOF 后统一事务处理;Bidirectional Streaming 用于实时双向通信,要严格管理协程生命周期。决策关键在于匹配业务的数据流向和速率特征,避免过度设计。