OOM排查——opentelemetry 跨线程传播上下文
背景
OpenTelemetry是一个由两个流行的监控和跟踪框架OpenCensus和OpenTracing合并而来的开源项目,由云原生计算基金会(CNCF)托管。用于检测、生成、收集和导出遥测数据,例如 跟踪(trace)、指标、日志、 指标和日志,简化了应用性能分析和故障排查。
简单介绍
为了让读者便于理解后文,这里简单介绍下 trace 和span的概念
trace
trace为我们提供了向应用程序发出请求时全过程的总体情况。无论是一个访问数据库的简单服务,还是复杂的服务网格们都可以通过trace了解请求在应用程序中的完整路径。
span
span 代表一个工作或操作单元,他是trace的构建块。span可以嵌套,能够更准确的捕获应用程序中完整的工作。
问题
服务为了实现更好的性能,会将一个请求拆分成多个子任务,并在线程池中管理这些子任务,然而 opentelemetry 本身并不支持跨线程的传播上下文信息,这就让我们没有办法设置请求为子任务的父span,我们也就没有办法对方便的对应用进行性能分析。
有人说我把父span作为一个变量,传进去不就完了么?简单的传进去无法控制父span的生命周期,如果我们在父span被销毁后仍然设置子span,就会造成OOM
解决思路
首先,我们看到 tracer.start_as_current_span
api:
- 上下文管理器会创建一个新的span,并将其设置为trace的当前span
- 退出上下文管理器将调用span的结束方案(这也是我们父span被销毁的原因),并通过返回上一个上下文将当前span恢复到之前的值
我们注意到start_as_current_span
还接受一个可选参数context,他是一个包含了这个生成的新span的父span的上下文(有点拗口,但其实就是可以通过它来指定上下文)。
那解决思路就有了:
- 启动线程池任务之前,先获取当前span的上下文,并将span写入至上下文中
- 将上下文传递给每一个子任务
- 在子任务创建span时,提供该上下文用于绑定父子span
注意:这里面只说了包含父span的上下文,但具体的key其实也没有提供,所以我们得弄明白这个start_as_current_span是怎么从context获取span的
源码截图,节省字数:
只有start_span使用了context,所以我们去看start_span
看起来是这个trace_api.get_current_span
诶这里突然发现,其实 trace_api.get_current_span 还有一个对应的set_current_span,那思路就清晰了起来
通过set_current_span 将span写入至上下文中,并在后面获取它,作为子任务span的父span。
另一个解决思路
在github看其他人issue时,发现可以通过今年3月的新插件解决跨线程传播的问题:github.com/open-teleme…
from opentelemetry.instrumentation.threading import ThreadingInstrumentation
# 自动装载 threading 追踪
ThreadingInstrumentation().instrument()
转载自:https://juejin.cn/post/7368669650575867913