我使用Dropwizard 指标来衡量我的应用程序中的各种指标。它们是JVM 检测中的几个预定义报告器,但奇怪的是我找不到任何报告 CPU 使用情况的报告。
我可以创建自己的 Gauge(使用getThreadCpuTime或类似工具),但我最好的猜测是我遗漏了一些东西。
我在当前的实现中错过了它,还是比我最初想象的更复杂?
我使用Dropwizard 指标来衡量我的应用程序中的各种指标。它们是JVM 检测中的几个预定义报告器,但奇怪的是我找不到任何报告 CPU 使用情况的报告。
我可以创建自己的 Gauge(使用getThreadCpuTime或类似工具),但我最好的猜测是我遗漏了一些东西。
我在当前的实现中错过了它,还是比我最初想象的更复杂?
我对 Dropwizard 了解不多,但我过去曾用ThreadMXBean
它来提供可扩展分布式计算系统中 CPU 利用率的估计值,因此我将分享我认为与该问题相关的内容。事情肯定比最初看起来要复杂得多:
ThreadMxBean 有点误导......
ThreadMxBean.getThreadCpuTime(id)
仅返回自线程启动以来特定线程在 CPU 上执行代码所花费的总时间,以纳秒为单位。它没有提供有关您的线程可能被阻塞或等待(睡眠)多长时间的信息,因此它确实不能让您很好地了解 CPU 使用情况。您还需要测量总阻塞/等待时间,然后在程序运行时跟踪所有这三个值以跟踪 CPU 使用情况。奇怪的是,ThreadMXBean
没有直接获取阻塞/等待时间的方法,所以你可能会想放弃。
...但是您可以使用它来获取ThreadInfo
对象...
首先,要启用此功能,请调用以下两行代码(如果您的 JVM 不支持,这可能会引发异常):
ManagementFactory.getThreadMXBean().setThreadCpuTimeEnabled(true);
ManagementFactory.getThreadMXBean().setThreadContentionMonitoringEnabled(true);
现在您可以调用ThreadMXBean.getThreadInfo(threadId)
以获取ThreadInfo
对应于特定线程的实例。这个 info 对象有两个方法,getBlockedTime()
和getWaitedTime()
,它们返回你的线程在这两种状态中花费的总毫秒数。没有getCpuTime()
方法(如果你问我,这是这个对象的一个非常愚蠢的缺点),但如果你知道你的线程何时开始,你可以做这样的事情:
//Initialized somewhere else:
ThreadMXBean bean = ...
long threadStartTime = System.currentTimeMillis();
Thread myThread = ...
//Inside your metrics-gathering code:
long now = System.currentTimeMillis();
ThreadInfo info = bean.getThreadInfo(myThread.getId());
long totalCpuTime = now - (info.getBlockedTime()+info.getWaitedTime()+threadStartTime);
现在您可以将线程利用率计算为百分比。
我们快到了,但我们还没有完成。每次我们浏览我上面发布的最后三行代码时,我们只收集线程执行/阻塞/等待状态的总时间。为了计算百分比,我们需要跟踪收集这些指标的时间,这样我们就可以知道自上次指标更新以来线程在每个状态中花费了多少时间。所以,做这样的事情:
class ThreadUsageMetrics{
long timestamp, totalBlockedTime, totalWaitTime;
ThreadUsageMetrics(long ts, long blocked, long wait){
timestamp = ts;
totalBlockedTime = blocked;
totalWaitTime = wait;
}
double computeCpuUsageSince(ThreadUsageMetrics prev){
long time = timestamp - prev.timestamp;
long blocked = totalBlockedTime - prev.totalBlockedTime;
long waited = totalWaitTime - prev.totalWaitTime;
return (time-(blocked+waited))/(double)time;
}
}
这将为我们提供从 0.0 到 1.0 范围内的两倍,表明 CPU 使用率占自上次指标更新以来总时间的百分比。我假设您可以将此值转换为百分比,然后Gauge
每 5 秒左右将其提供给 Dropwizard 的一个实例。在我的项目中,这就是我们几年来估计 CPU 使用率的方式,它对我们来说效果很好。
关于这一点的几点说明 - 我们实际上不需要在此对象中显式存储总 CPU 时间,因为任何不花费阻塞或等待的时间要么是执行时间,要么是在上下文切换期间花费的时间。我们无法知道上下文切换时间,但可以肯定的是,在 99.9% 的情况下,总的上下文切换时间可以忽略不计。
需要注意的是——我们并没有真正测量 CPU 使用率。
如果你仔细阅读,你会注意到我说过我们正在“估计”CPU 使用率。我这样说的原因是我们正在测量特定 Java 的总执行时间Thread
。Java 没有提供实际 CPU 硬件使用率的概念——它只是线程执行所花费的总时间。诸如超线程之类的事情进一步混淆了这一点,其中“执行”所花费的时间实际上可能意味着等待其他线程离开 ALU 或内存总线的时间。我认为这可以很好地衡量代码何时在物理硬件线程上运行,但是如果您想衡量实际的 CPU 使用率,您将无法在纯 Java 中进行。