JetBrains 于 2026 年 9 月 15 日发布了一篇技术说明,介绍 OpenTelemetry 插件中的Service Map功能如何工作。该功能会在应用于开发环境中运行期间,绘制微服务之间的实际关系。该说明由 Nikita Dukin 和 Egor Klimov 撰写,属于 Rider Execution 团队与 Software Engineering Research 团队之间的合作成果。
这一理念源于一个众所周知的实际问题:架构图可能看起来井然有序,但它反映的或许是数月前的系统状态,也无法展示新服务或尚未更新文档的消息队列。JetBrains 认为,单靠静态代码分析并不总是足够,因为它描述的是源代码中可能发生的事情,而不是服务在运行期间实际发生的交互。
追踪数据是地图的数据源
该插件依赖 OpenTelemetry 数据,而这些数据收集日志、指标和追踪信息。日志说明发生了什么,指标展示现象的规模,而追踪信息揭示请求在系统中的路径。每个追踪由称为spans的工作单元组成,OpenTelemetry 为这些工作单元提供统一的语义约定,例如 HTTP Client 和 HTTP Server 追踪。
该插件利用这些追踪信息了解运行时架构,而不会将算法绑定到某一种框架或语言。如果应用和库按照 OpenTelemetry 的预期发送追踪信息,该功能就能描绘通信关系,而不受所用技术影响。JetBrains 表示,同一套逻辑可以用于 JVM、.NET、Python、Go 等应用,也可以在 IntelliJ IDEA、GoLand、PyCharm、WebStorm 和 Rider 中使用。
地图如何在开发环境中构建?
在启用 OpenTelemetry 插件的开发环境启动时,该插件会启动一个轻量级本地服务器,用于接收遥测数据。应用启动后,插件会设置标准的 OpenTelemetry 环境变量,使应用将追踪数据发送到该本地服务器。
服务器会异步处理传入的追踪数据,构建内部架构模型并持续更新。当打开 Service Map 标签页时,插件会获取最新的结构模型,并以可视化图表的形式显示。这样,地图并不是手动创建的静态文档,而是系统运行期间观测到的通信关系的直接结果。
挑战不在于绘图,而在于解释数据
追踪数据会独立到达,且无法保证到达顺序。子服务的追踪可能早于父服务的追踪到达,而且追踪不会声明一个能够保证不再有延迟追踪到达的最终结束时刻。此外,OpenTelemetry 不会为每种追踪提供严格且独立的类型;追踪携带的是由键值对组成的映射,用于描述操作的语义。
因此,JetBrains 将架构重建归类为一种数据流处理算法。插件不会等待追踪完成,而是在每条追踪到达后立即检查。它会使用http.request.method和http.response.status_code等属性,判断操作是否为 HTTP 通信;其他属性则可能指向数据库查询或与消息系统的交互。
追踪分类后,算法会确定产生该追踪的服务。如果服务是新的,就将其添加到地图中;如果服务已经存在,就将新数据与其合并并更新统计信息。对于 HTTP 通信,插件会寻找调用服务发出的 CLIENT 追踪与接收服务产生的 SERVER 追踪之间的关系。追踪上下文会随请求传递,因此 SERVER 追踪是 CLIENT 追踪的子追踪。如果另一端已经存在,关系会立即绘制;如果不存在,则将该追踪保留在内存中,直到对应数据到达。
其他依赖关系使用不同的规则。数据库调用通常由一个 CLIENT 追踪表示,插件会根据语义属性从中推断数据库节点。消息系统则需要更大的灵活性,因为生产者与消费者之间的关系可能通过父子关系显示,也可能通过追踪链接显示,具体取决于消息系统以及所使用的插桩方式。
这在实践中为何重要?
这里的核心价值在于,地图揭示的是观测到的行为,而不仅仅是预期架构。如果系统在开发期间显示出多个 HTTP 通信或多个数据库查询,而预期只有一次通信,就可以在发布前发现这一问题。地图还可以帮助理解未被记录的依赖关系,或随时间发生变化的依赖关系。
不过,结果的准确性仍取决于遥测数据的质量。该算法需要正确的追踪信息、服务之间的追踪上下文传播,以及对预期语义属性的使用。因此,仅仅安装插件,并不会自动提供任何系统的完整 Service Map;测量工具未发送的数据,或在没有上下文的情况下到达的数据,可能不会出现在推断出的关系中。文章还说明,随着新证据的到达,模型会持续演进,使地图成为不断更新的运行时表示,而不是对架构设计作出的最终判断。