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