我使用Dropwizard metrics来衡量应用中的各种指标。他们是JVM instrumentation中的几个预定义记者,但奇怪的是我找不到任何报告CPU使用情况。
我可以创建自己的Gauge(使用getThreadCpuTime或类似的),但我最好的猜测是我错过了一些东西。
我在当前的实施中是否错过了它,还是比我初想的更复杂?
答案 0 :(得分:5)
我对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使用率占自上次度量标准更新以来总时间的百分比。我假设您可以将此值转换为百分比,并每隔5秒左右将其提供给Dropwizard的Gauge
实例。在我的项目中,这就是我们估计几年CPU使用率的方式,它对我们来说非常有用。
关于这一点的几点注意事项 - 我们实际上并不需要在此对象中明确存储总CPU时间,因为任何不花时间阻塞或等待的时间都是执行时间,或者是在上下文切换期间花费的。我们无法知道上下文切换时间,但可以安全地假设在99.9%的情况下总上下文切换时间可以忽略不计。
以下是警告 - 我们并未真正衡量CPU使用率。
如果您仔细阅读过,您会注意到我说我们正在“估算”CPU使用率。我这说的原因是我们正在测量特定Java Thread
的总执行时间。 Java没有提供实际CPU硬件使用的概念 - 它只是线程执行所花费的总时间。超级线程之类的东西更加混乱,“执行”所花费的时间实际上意味着等待另一个线程从ALU或内存总线上下来所花费的时间。我认为这可以很好地衡量代码在物理硬件线程上运行的时间,但是如果你想测量实际的CPU使用率,你将无法用纯Java来实现。