开销和性能调优编辑

代理开销编辑

任何 APM 代理都会产生开销。以下是一些可能会看到开销的不同领域。

延迟编辑

我们非常谨慎地保持关键路径上的代码尽可能轻量级。例如,事件的实际报告是在后台线程上完成的。

平均延迟和较高百分比的延迟都很低,这一点非常重要。这是因为,如果 1% 的请求性能非常差,那么低平均延迟就毫无意义。高延迟峰值的主要来源是垃圾收集暂停和锁争用。

我们非常谨慎地尽量减少在 Java 代理中进行的内存分配。例如,我们不分配新对象,而是从对象池中获取对象,并在不再使用时将它们返回到池中。有关此过程的更多详细信息,请参阅此处。在报告记录的事件时,我们将它们直接序列化到发送到 APM 服务器的请求的输出流中,同时只依赖于可重用缓冲区。这样,我们就可以在不分配任何对象的情况下报告事件。我们这样做是为了不为 GC 增加额外的工作,因为它已经忙于清理应用程序正在分配的内存。

当我们在线程之间传输事件时,Java 代理还使用专门的数据结构(LMAX Disruptor 和 JCTools 的队列)。例如,从记录事务的应用程序线程到后台报告线程。这是为了避免您在使用标准 JDK 数据结构(如 ArrayBlockingQueue)时遇到的锁争用和伪共享等问题。

在单线程基准测试中,我们的 Java 代理在高达 99.99% 的情况下产生的开销都在个位数微秒 (µs) 的范围内。这些基准测试是在一台运行 Oracle JDK 10 的 i7-7700 (3.60GHz) Linux 机器上运行的。我们目前正在进行多线程基准测试。在禁用标头记录的情况下,代理分配的内存不到一个字节,用于记录一个 HTTP 请求和一个 JDBC (SQL) 查询,包括在后台向 APM 服务器报告这些事件。

CPU编辑

尽管代理的大部分工作都是在后台完成的,但序列化和压缩事件以及将它们发送到 APM 服务器实际上也会增加一些 CPU 开销。如果您的应用程序不受 CPU 限制,那么这应该无关紧要。如果您的应用程序执行(阻塞)网络 I/O(例如与数据库或外部服务通信),则它可能不受 CPU 限制。

如果 APM 服务器无法处理所有事件,代理将丢弃数据,以免应用程序崩溃。

内存编辑

除非您的堆非常小,否则通常不必为 Java 代理增加堆大小。它对对象池的内存开销相当小且是静态的,还有一些大小为几兆字节的小缓冲区。

网络编辑

代理需要一定的网络带宽,因为它需要将记录的事件发送到 APM 服务器。在这里,了解您的应用程序处理多少请求以及您希望记录和存储多少请求非常重要。这可以使用采样率进行调整。

调优代理启动编辑

当 Java 代理启动时,它需要初始化代理的各个组件,连接到 APM 服务器,并检测已配置为跟踪的任何已加载类。这需要一些时间和资源,如果在主线程上同步完成(使用 -javaagent 时的默认行为),则会延迟应用程序的启动,直到完成为止。

我们提供了几种调优启动的选项,针对三种启动用例

  1. 立即同步代理启动
    应用程序需要立即应用检测,而不管启动时间成本如何 - 通常是因为您不想错过应用程序一开始的任何跟踪/事务,或者某些类型的操作只在初始化时发生,并且需要在创建第一个实例之前进行检测(例如设置预处理语句)。在这种用例中,请按照使用 -javaagent 标志进行手动设置中的说明使用 -javaagent 命令行标志
  2. 最快启动(异步)
    应用程序可以接受在启动之前缺少检测,也可以接受缺少一些初始跟踪和事务。在这种用例中,您可以在启动后使用使用 apm-agent-attach-cli.jar 进行自动设置附加到应用程序,或者如果您正在使用 -javaagent 命令行标志,则可以通过设置 elastic.apm.start_async 属性(自 1.29.0 版本起)异步启动代理,例如 java -Delastic.apm.start_async ...(在早期版本中,您可以使用 elastic.apm.delay_agent_premain_ms=0
  3. 最小化同步启动
    应用程序需要立即应用检测,但需要最大限度地缩短应用程序启动之前的时间。这需要一些权衡:为了减少同步启动时间,需要通过 enable_instrumentations 选项最大限度地减少应用的检测数量。在这种用例中,您应该确定可以接受的用于应用程序监控的最小检测组集,并使用配置指南中详细介绍的 enable_instrumentations 配置选项。可以在应用程序正常终止后(自 1.29.0 版本起)的代理日志中找到最小的检测集。除此之外,您还可以运行日志级别设置为 DEBUG 的代理,并在应用程序正常终止时查看代理生成的统计信息。

调优代理编辑

Java 代理提供了各种配置选项,其中一些选项可能会对性能产生重大影响。为了便于确定哪些选项会影响性能,我们在文档中使用*(性能)*标记了某些配置选项。

采样率编辑

*采样率*是应记录并发送到 APM 服务器的请求的百分比。(对于 8.0 之前的服务器,未采样的请求会在不包含上下文信息的情况下发送,从而减少传输和存储大小;从 8.0 开始,根本不会发送未采样的请求。)什么是理想的采样率?遗憾的是,这个问题没有万能的答案。采样取决于您的偏好和应用程序。您希望采样的内容越多,您需要的网络带宽和磁盘空间就越多。

需要注意的是,即使您以 100% 的采样率进行采样,应用程序的延迟也不会受到代理的太大影响。但是,后台报告线程在序列化和 gzip 压缩事件时需要做一些工作。

可以通过更改transaction_sample_rate (性能)来更改采样率。

堆栈跟踪收集编辑

如果某个时间段(例如,捕获的 JDBC 查询)花费的时间超过 5 毫秒,我们会捕获堆栈跟踪,以便您轻松找到导致查询的代码路径。堆栈跟踪可能会很长,占用带宽和磁盘空间,并且还需要分配对象。但由于我们异步处理堆栈跟踪,因此它增加的延迟非常少。如果需要,提高 span_stack_trace_min_duration(性能) 或完全禁用堆栈跟踪收集可以提高一些性能。

记录标头和 Cookie编辑

默认情况下,Java 代理会记录所有请求和响应标头,包括 Cookie。禁用 capture_headers(性能) 可以节省分配、网络带宽和磁盘空间。

断路器编辑

启用后,代理会定期轮询压力监视器以检测系统/进程/JVM 压力状态。如果任何监视器检测到压力指示,代理将变为非活动状态,就像 recording 配置选项已设置为 false 一样,从而将资源消耗降至最低。当处于非活动状态时,代理会继续轮询相同的监视器,以检测压力状态是否已解除。如果所有监视器都确认系统/进程/JVM 不再处于压力状态,代理将恢复并完全正常运行。有关细粒度断路器配置,请参阅断路器