Отчет об использовании ЦП JVM с помощью метрик Dropwizard
Я использую метрики Dropwizard для измерения различных метрик в моем приложении. Они являются несколькими предопределенными репортерами в инструментарии JVM, но странным образом я не смог найти никаких отчетов об использовании процессора.
Я мог бы создать свою собственную Gauge (используя getThreadCpuTime или аналогичную), но я думаю, что мне чего-то не хватает.
Я пропустил это в текущей реализации, или это более сложно, чем мне кажется?
1 ответ
Я не знаю много о Dropwizard, но я использовал ThreadMXBean
в прошлом, чтобы дать оценки загрузки ЦП в масштабируемых распределенных вычислительных системах, поэтому я поделюсь тем, что я думаю, имеет отношение к этому вопросу. Все определенно сложнее, чем может показаться на первый взгляд:
ThreadMxBean несколько вводит в заблуждение...
ThreadMxBean.getThreadCpuTime(id)
возвращает только общее время, потраченное конкретным потоком на выполнение кода в ЦП, измеренное в наносекундах с момента запуска потока. Он не предоставляет информации о том, как долго ваш поток мог быть заблокирован или находился в режиме ожидания (спит), поэтому он действительно не дает вам хорошего представления об использовании процессора. Вам также нужно измерить общее время блокировки / ожидания, а затем отслеживать все три из этих значений в течение времени выполнения вашей программы, чтобы отслеживать использование процессора. Довольно странно, 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, указывающее использование ЦП в процентах от общего времени с момента последнего обновления метрик. Я предполагаю, что вы можете преобразовать это значение в процент и передать его экземпляру Dropwizard's Gauge
каждые 5 секунд или около того. В моем проекте так мы оценивали загрузку процессора в течение нескольких лет, и он отлично сработал для нас.
Пара замечаний по этому поводу: нам на самом деле не нужно явно хранить общее время ЦП в этом объекте, потому что любое время, не затраченное на блокировку или ожидание, является либо временем выполнения, либо потрачено во время переключения контекста. У нас нет способа узнать время переключения контекста, но можно с уверенностью предположить, что общее время переключения контекста незначительно в 99,9% всех случаев.
Вот предостережение - мы не измеряем нагрузку на процессор.
Если вы внимательно прочитаете, вы заметите, что я сказал, что мы "оцениваем" использование процессора. Я говорю это потому, что мы измеряем общее время выполнения конкретной Java Thread
, Java не дает представления о фактическом использовании аппаратного обеспечения процессора - это всего лишь общее время, потраченное потоком на выполнение. Это еще более осложняется такими вещами, как Hyper Threading, где время, потраченное на "выполнение", может фактически означать время, потраченное на ожидание выхода другого потока из ALU или шины памяти. Я думаю, что это обеспечивает хорошую меру того, когда код выполняется в физическом аппаратном потоке, но если вы хотите измерить фактическое использование процессора, вы не сможете сделать это на чистой Java.