我对 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 实例。这个信息对象有两个方法,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 使用率占总时间的百分比。我假设您可以将此值转换为百分比,并每隔 5 秒左右将其提供给 Dropwizard 的 Gauge 实例。在我的项目中,这是我们几年来估算 CPU 使用率的方法,它对我们来说非常有效。
关于这一点的几点说明 - 我们实际上不需要在此对象中显式存储总 CPU 时间,因为任何未花费在阻塞或等待上的时间要么是执行时间,要么是在上下文切换期间花费的时间。我们无法知道上下文切换时间,但可以肯定的是,在 99.9% 的情况下,总的上下文切换时间可以忽略不计。
请注意,我们并未真正测量 CPU 使用率。
如果您仔细阅读,您会注意到我说过我们正在“估计”CPU 使用率。我这么说的原因是我们正在测量特定 Java Thread 的总执行时间。 Java 没有提供实际 CPU 硬件使用率的概念——它只是线程执行所花费的总时间。诸如超线程之类的事情进一步混淆了这一点,其中“执行”所花费的时间实际上可能意味着等待其他线程离开 ALU 或内存总线的时间。我认为这可以很好地衡量代码何时在物理硬件线程上运行,但如果你想衡量实际的 CPU 使用率,你将无法在纯 Java 中做到这一点。