Запишите процесс устранения неполадок с низкой загрузкой ЦП и высокой нагрузкой

Java

1. Предпосылки

По историческим причинам в настоящее время существует служба, предназначенная для обработки сообщений mq.У облачного сервиса Alibaba robotmq, используемого mq, есть SDK версии 1.2.6 (2016 г.). С развитием бизнеса потребителей на приложение становится все больше, приближается к 200+, из-за чего экз, где находится приложение, имеет высокую нагрузку в течение длительного времени и частые срабатывания сигнализации.


2. Анализ явлений

Нагрузка на сервер ecs, на котором расположено приложение, долгое время была высокой (на ecs есть только один сервис), но использование ресурсов, таких как процессор, ввод-вывод и память, низкое. следующий рисунок:

image

ECS配置:4核8G
物理cpu个数=4
单个物理CPU中核(core)的个数=1
单核多处理器

С точки зрения загрузки системы эффект многоядерного процессора аналогичен эффекту многопроцессорного.При рассмотрении нагрузки системы разделите загрузку системы на общее количество ядер.При условии, что нагрузка каждого ядра не превышает 1,0. , это означает нормальную работу. Обычно, когда n-ядерный ЦП загружается

Примените вышеуказанные правила: Сначала обратите внимание на load_15m и load_5m, нагрузка в основном поддерживается между 3-5, что указывает на то, что долгосрочная нагрузка в системе поддерживается на высоком уровне. Снова взглянув на load_1m, можно увидеть, что флуктуация очень велика, и она намного больше, чем количество ядер ЦП за многие периоды времени. Занятый в краткосрочной перспективе и нервный в среднесрочной и долгосрочной перспективе, это, вероятно, будет началом заторов.


В-третьих, найдите

Выяснить причину высокой нагрузки

tips:系统load高,不代表cpu资源不足。Load高只是代表需要运行的队列累计过多。但队列中的任务实际可能是耗cpu的,也可能是耗i/0及其他因素的

image
На рисунке load_15, load_5 и load_1 больше, чем количество ядер 4.перегрузка

  • Пользовательские процессы = 8,6%
  • Процесс ядра = 9,7%
  • бездействие=80%
  • Процент времени ЦП, занятого ожиданием ввода-вывода = 0,3%

image

Судя по использованию ЦП, памяти и ввода-вывода на приведенном выше рисунке, эти три значения невелики. Использование ЦП низкое, а нагрузка высокая, что исключает возможность высокой нагрузки, вызванной недостатком ресурсов ЦП.

Затем используйте vmstat для просмотра общего рабочего состояния процесса, памяти, ввода-вывода и других систем, как показано ниже:

image
Судя по результатам, блокировка ввода-вывода и блокировка ввода-вывода происходят нечасто, но количество прерываний (вход) и переключение контекста (cs) в системе особенно часто.Когда количество переключений контекста процесса велико, это легко чтобы заставить ЦП использовать большое количество времени. Время тратится на сохранение и восстановление ресурсов, таких как регистры, стеки ядра и виртуальная память, что, в свою очередь, сокращает время фактического запуска процесса, что приводит к высокой нагрузке.

CPU寄存器,是CPU内置的容量小、但速度极快的内存。程序计数器,则是用来存储CPU正在执行的指令的位置,或者即将执行的下一条指令的位置。
他们都是CPU在运行任何任务前,必须依赖的环境,因此也被叫做CPU上下文。

CPU上下文切换,就是先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文,到这些寄存器和程序计数器,
最后再跳转到程序计数器所指的新位置,运行新任务。

Общее направление исследования: частые прерывания и переключение потоков (поскольку на этом ecs всего один java-сервис, в основном исследуем процесс)

Через vmstate можно просмотреть только общее переключение контекста ЦП, а информацию о переключении контекста на уровне потока можно просмотреть с помощью команды pidstat. pidstat -wt 1 (на рисунке ниже показаны данные 9s, всего 36w раз, в среднем 4w раз в секунду)

image
Наблюдая за приведенным выше рисунком, легко обнаружить два явления:

  1. Во-первых, существует множество java-потоков.
  2. Второй — это поток, который регулярно переключает контекст более 100 раз в секунду. (Конкретные причины будут проанализированы позже)

Подтвердите источник этих потоков Java и проверьте количество потоков в процессе приложения. кошка /proc/17207/статус

image
Количество потоков 9749 (непиковое время)

Направление расследования:

  • Слишком много потоков
  • Количество переключений контекста в секунду для некоторых потоков слишком велико.

Сначала проверьте основную причину, то есть количество переключений контекста некоторых потоков слишком велико. Получите информацию о стеке процесса в Интернете, а затем найдите идентификатор потока, время переключения которого достигает 100+/секунду, преобразуйте идентификатор потока в шестнадцатеричный формат и получите его в журнале стека.

image
image

На приведенном выше рисунке вы можете увидеть статус процесса TIME_WAITING, код проблемы, com.alibaba.ons.open.trace.core.dispatch.impl.AsyncArrayDispatcher и проверить несколько других потоков с частым переключением контекста.Информация о стеке в основном то же.

Затем проверьте проблему слишком большого количества потоков и проанализируйте информацию о стеке, чтобы обнаружить большое количество потоков ConsumeMessageThread (сначала игнорируются потоки связи, мониторинга, сердцебиения и другие потоки).

image

По имени потока поиск в исходном коде RocketMQ может в основном найти следующую часть кода.

image
С помощью двух частей кода в принципе можно найти проблему в процессе инициализации и запуска потребителя mq, а затем проанализировать ее в соответствии с кодом.


В-четвертых, анализ кода

  1. Проверьте на уровне кода слишком много потоков.На приведенном выше скриншоте кода видно, что ConsumeMessageThread_ управляется пулом потоков, а затем посмотрите ключевые параметры пула потоков, количество основных потоков this.defaultMQPushConsumer.getConsumeThreadMin( ), максимальное количество потоков this.defaultMQPushConsumer .getConsumeThreadMax(), неограниченная очередь LinkedBlockingQueue
ps:由于线程池队列用的LinkedBlockingQueue无界队列,LinkedBlockingQueue的容量默认大小是Integer.Max,在任务没有填满这个容量之前不会创建新的工作线程,因此最大线程数没有任何作用。

Посмотрите на конфигурацию message-consumer для количества основных потоков и максимального количества потоков и обнаружите, что на уровне кода нет специальной конфигурации, поэтому используется системное значение по умолчанию, то есть следующий рисунок

image
image

Пока что причину чрезмерного количества потоков можно примерно локализовать:

Поскольку количество потоков-потребителей (ConsumeThreadNums) не указано, количество основных потоков системы по умолчанию равно 20, а максимальное количество потоков равно 64. При инициализации каждого потребителя создается пул потоков с количеством основных потоков, равным 20. будет создано, то есть велика вероятность, что у каждого потребителя будет 20 потоков, потоки потребляют сообщения, В результате количество потоков резко возрастает (20*число потребителей), но обнаруживается, что большинство этих потоков-потребителей находятся в состоянии сна/ожидания, что мало влияет на переключение контекста.

Количество переключений контекста потока слишком велико. Устранение неполадок на уровне кода: Этот код нельзя искать в исходном коде RocketMQ. Приложение использует Alibaba Cloud SDK.Поищите в SDK, проверьте контекст и вызовите ссылку, и вы обнаружите, что этот код принадлежит модулю возврата трека.

image

В сочетании с кодом для анализа процесса модуля возврата дорожки (AsyncArrayDispatcher) итог выглядит следующим образом:

Найдите код в журнале стека потоков в исходном коде SDK следующим образом:

image

Из этого кода и информации о стеке мы видим, что проблема возникает в traceContextQueue.poll(5, TimeUnit.MILLISECONDS); где traceContextQueue — ограниченная блокирующая очередь. При опросе, если очередь пуста, она будет блокироваться на определенное время. time, поэтому поток будет заблокирован на определенное время.Частое переключение между run и time_wait.

Что касается того, почему poll(5, TimeUnit.MILLISECONDS) используется вместо take(), я лично думаю, что это может быть связано с сокращением количества операций ввода-вывода в сети. одиночный трек?

poll()方法会返回队列的第一个元素,并删除;如果队列为空,则返回null,并不会阻塞当前线程;出队的逻辑调用的是 dequeue()方法,此外,它还有一个重载的方法,poll(long timeout, TimeUnit unit),如果队列为空,则会等待一段时间

image

Очередь трассировки traceContextQueue использует ArrayBlockingQueue, ограниченную очередь блокировки, внутри используется массив для хранения элементов, а параллельный доступ достигается за счет блокировок, а элементы также располагаются по принципу FIFO.

image
Как видно из приведенного выше кода, он использует reentrantLock для достижения контроля параллелизма.ReentrantLock обеспечивает реализацию честных и несправедливых блокировок, но ArrayBlockingQueue по умолчанию использует несправедливые блокировки, что не гарантирует, что потоки могут получить доступ к очереди честно.

所谓的公平是指阻塞的线程,按照阻塞的先后顺序访问队列,非公平是指当队列可用的时候,阻塞的线程都可以有争夺线程访问的资格,有可能先阻塞的线程最后才能访问队列。

Поскольку каждый потребитель открывает только один поток распределения треков, в этой части нет конкуренции.

Рассмотрим принцип реализации блокировки ArrayBlockingQueue.

image
image
image

Из приведенной выше части кода видно, что блокировка наконец-то реализована методом park, unsafe.park — нативным методом

Метод park заблокирует текущий поток, и метод вернется только при выполнении одного из следующих четырех условий.

  • Когда unpark соответствует парковке, выполняется или уже выполнено.
  • когда нить прерывается
  • После ожидания количества миллисекунд, указанного параметром времени
  • происходят аномалии

На данный момент причины переключения системных потоков и частых прерываний сводятся к следующему:

  • В модуле отслеживания обратной передачи в Alibaba Cloud SDK потребитель имеет поток распределения, очередь отслеживания и пул потоков обратной передачи данных отслеживания. Поток распределения извлекается из очереди отслеживания. Если его невозможно получить, он будет заблокирован на 5 мс. , и данные дорожки будут возвращены, если они будут извлечены. Пул потоков отправляется, а затем сообщается о данных. Слишком много потоков распределения часто переключаются между состояниями выполнения и time_wait, что приводит к высокой нагрузке на систему.
  • Поскольку максимальное и минимальное количество потоков для каждого потребления сообщения потребителем не установлено на уровне кода, каждый потребитель будет открывать 20 основных потоков для обработки потребления сообщений, что приводит к чрезмерному количеству потоков, потребляющих системные ресурсы, и пустому запуску.

V. План оптимизации

Сочетая вышеперечисленные причины, провести целенаправленную оптимизацию

  • На уровне кода для каждого потребителя задается количество элементов конфигурации потоков.Потребитель может установить количество основных потоков в соответствии с реальной ситуацией, такой как бизнес, который он выполняет, чтобы уменьшить общее количество потоков и избежать большое количество потоков, работающих вхолостую.
  • В приведенном выше анализе используется версия Alibaba Cloud ons 1.2.6, которая была обновлена ​​до версии 1.8.5. Анализируя исходный код модуля возврата дорожки версии 1.8.5, было обнаружено, что переключатель был добавлен в возврат дорожки для настройки возврата дорожки. Используйте один поток (singleton), то есть все потребители используют для обработки поток распределения, ограниченную очередь траектории и пул потоков отчетов о траектории, и вы можете рассмотреть возможность обновления версии после прохождения проверка.

image
image
image