探针概念 (Probe Concepts)
为 JProfiler 开发自定义探针时,您需要了解一些基本概念和术语。JProfiler 所有探针的共同基础是它们拦截特定方法,并使用拦截的方法参数和其他数据源构建一个包含您希望在 JProfiler UI 中看到的有趣信息的字符串。
定义探针时的初始问题是如何指定拦截的方法,并获得一个可以使用方法参数和其他相关对象来构建字符串的环境。在 JProfiler 中,有三种不同的方法可以做到这一点:
- 脚本探针 (script probe) 完全在 JProfiler UI 中定义。您可以在调用树中右键单击一个方法,选择脚本探针操作,并在内置代码编辑器中输入字符串表达式。这对于实验探针非常有用,但仅暴露了自定义探针功能的一个非常有限的部分。
- 嵌入探针 (embedded probe) API 可以从您自己的代码中调用。如果您编写一个库、数据库驱动程序或服务器,您可以随产品一起提供探针。任何使用 JProfiler 分析您的产品的人,都会自动将您的探针添加到 JProfiler UI 中。
- 使用 注入探针 (injected probe) API,您可以在 IDE 中为第三方软件编写探针,充分利用 JProfiler 的探针系统。该 API 使用注解来定义拦截并注入方法参数和其他有用对象。
下一个问题是:JProfiler 应该如何处理您创建的字符串?有两种不同的策略可用:有效负载创建或调用树拆分。
有效负载创建 (Payload creation)
探针构建的字符串可用于创建一个 探针事件 (probe event)。该事件具有设置为该字符串的描述、等于拦截方法调用时间的持续时间以及相关的调用栈。在其相应的调用栈中,探针描述和时间被累积并作为 有效负载 (payloads) 保存到调用树中。当事件在达到某个最大数量后被合并时,调用树中累积的有效负载显示整个记录期间的总数。如果同时记录了 CPU 数据和您的探针,探针调用树视图将显示合并的调用栈,其中有效负载字符串作为叶节点,CPU 调用树视图将包含指向探针调用树视图的 注释链接 (annotated links)。
就像 CPU 数据一样,有效负载可以在调用树或热点视图中显示。热点显示哪些有效负载负责大部分消耗的时间,反向跟踪显示哪些代码部分负责创建这些有效负载。为了获得良好的热点列表,有效负载字符串不应包含任何唯一 ID 或时间戳,因为如果每个有效负载字符串都不同,就不会有累积,也没有明确的热点分布。例如,在准备好的 JDBC 语句的情况下,参数不应包含在有效负载字符串中。
脚本探针会自动从配置的脚本返回值创建有效负载。注入探针类似,它们从带有 PayloadInterception
注解的拦截处理方法返回有效负载描述,作为字符串或
Payload
对象以实现高级功能。另一方面,嵌入探针通过调用 Payload.exit
并将有效负载描述作为参数来创建有效负载,其中
Payload.enter
和 Payload.exit
之间的时间被记录为探针事件持续时间。
如果您正在记录在不同调用点发生的服务调用,有效负载创建最为有用。一个典型的例子是数据库驱动程序,其中有效负载字符串是某种形式的查询字符串或命令。探针从调用点的角度出发,测量的工作由另一个软件组件执行。
调用树拆分 (Call tree splitting)
探针也可以从执行站点的角度出发。在这种情况下,重要的不是拦截方法如何被调用,而是拦截方法之后执行了哪些方法调用。一个典型的例子是用于 servlet 容器的探针,其中提取的字符串是一个 URL。
比创建有效负载更重要的是现在能够为探针构建的每个不同字符串拆分调用树。对于每个这样的字符串,将在调用树中插入一个拆分节点,其中包含所有相应调用的累积调用树。否则只有一个累积调用树,现在有一组拆分节点将调用树分割成不同的部分,可以分别进行分析。
多个探针可以产生嵌套拆分。默认情况下,单个探针仅产生一个拆分级别,除非它被配置为 可重入 (reentrant),这不支持脚本探针。
在 JProfiler UI 中,调用树拆分不与脚本探针功能捆绑在一起,而是一个 单独的功能 (separate feature),称为
"Split methods"。它们仅拆分调用树而不创建有效负载,因此不需要带有名称和描述的探针视图。注入探针从带有 SplitInterception
注解的拦截处理方法返回拆分字符串,而嵌入探针调用 Split.enter
并传入拆分字符串。
遥测 (Telemetries)
自定义探针有两个默认遥测:事件频率和平均事件持续时间。注入和嵌入探针支持通过探针配置类中的注解方法创建的附加遥测。在 JProfiler UI 中,脚本遥测独立于脚本探针功能,并在 "Telemetries" 部分中找到,位于工具栏中的 Configure Telemetries 按钮下。
遥测方法每秒轮询一次。在 Telemetry
注解中,您可以配置单位和比例因子。使用 line
属性,可以将多个遥测组合成一个遥测视图。使用
TelemetryFormat
的 stacked
属性,您可以使线条累加并将其显示为堆叠线图。嵌入和注入探针中的遥测相关 API 是等效的,但仅适用于各自的探针类型。
控制对象 (Control objects)
有时将探针事件与相关的长生命周期对象关联是很有趣的,这些对象在 JProfiler 中称为 "控制对象 (control objects)"。例如,在数据库探针中,该角色由执行查询的物理连接承担。这些控制对象可以通过嵌入 API 和注入探针 API 打开和关闭,从而在探针事件视图中生成相应的事件。当创建探针事件时,可以指定控制对象,以便探针事件贡献于探针的 "控制对象" 视图中显示的统计信息。
控制对象具有在打开时必须指定的显示名称。如果在创建探针事件时使用了新的控制对象,探针必须在其配置中提供名称解析器。
此外,探针可以通过枚举类定义自定义事件类型。当创建探针事件时,可以指定这些类型之一,并在事件视图中显示,您可以在其中筛选单个事件类型。更重要的是,显示控制对象作为时间轴上线条的探针时间线视图根据事件类型进行着色。对于没有自定义事件类型的探针,着色显示空闲状态,其中没有记录事件,以及探针事件持续时间的默认事件状态。使用自定义类型,您可以区分状态,例如 "读取 (read)" 和 "写入 (write)"。
记录 (Recording)
像所有探针一样,自定义探针默认不记录数据,但您必须根据需要启用和禁用记录。虽然您可以在探针视图中使用手动开始/停止操作,但通常需要在开始时打开探针记录。因为 JProfiler 事先不知道自定义探针,记录配置文件中有一个 Custom probes 复选框,适用于所有自定义探针。
同样,您可以为开始和停止探针记录的触发器操作选择 All custom probes。
对于编程记录,您可以调用
Controller.startProbeRecording(Controller.PROBE_NAME_ALL_CUSTOM, ProbeRecordingOptions.EVENTS)
来记录所有自定义探针,或者传递探针的类名以更具体。