Руководство по снятию снапшотов GlobalScheduler#
Назначение#
Процедура снятия снапшотов (thread dump) процесса GlobalScheduler позволяет диагностировать проблемы производительности, зависания потоков и дедлоки в системе Global ERP.
Примечание
Снятие снапшотов требуется при следующих симптомах:
Зависание задач планировщика.
Высокая нагрузка на CPU процесса GlobalScheduler.
Увеличивающееся потребление памяти.
Ошибки выполнения запланированных заданий.
Длительное выполнение задач без прогресса.
Общие предварительные требования#
Установленная Java Development Kit (JDK) той же версии, что используется для запуска GlobalScheduler.
Знание PID процесса Java GlobalScheduler.
Права доступа к процессу GlobalScheduler.
Часть 1: Снятие снапшотов в Kubernetes#
Предварительные требования для Kubernetes#
Доступ к кластеру Kubernetes с правами на выполнение
kubectl exec.Настроенный
kubeconfigдля namespacegs-ctk.
Шаг 1.1: Поиск пода GlobalScheduler#
kubectl get pods -n gs-ctk
Пример вывода:
NAME READY STATUS RESTARTS AGE
gs-cluster-1-global-scheduler-7d49bffb5f-d9llp 2/2 Running 2 (3m12s ago) 18h
gs-cluster-1-global-server-excl-cc4d9c647-jshzq 2/2 Running 5 (3m29s ago) 18h
gs-cluster-1-global-server-share-95bc95764-lql4x 2/2 Running 6 (3m12s ago) 18h
gs-cluster-1-grafana-0 1/1 Running 1 (3m12s ago) 18h
gs-cluster-1-haproxy-6d4b44cf94-lzlj2 2/2 Running 2 (3m29s ago) 18h
gs-cluster-1-rabbitmq-0 1/1 Running 1 (3m29s ago) 18h
nsctl-6f59bdc5c8-b8vsv 1/1 Running 1 (3m29s ago) 18h
Шаг 1.2: Определение PID процесса Java#
# Подключение к поду и поиск PID
kubectl exec -it gs-cluster-1-global-scheduler-7d49bffb5f-d9llp -n gs-ctk -c globalscheduler -- /bin/bash
# Поиск PID процесса Java (обычно дочерний от python3)
ps aux | grep java
# ИЛИ
pstree -p 1
Пример вывода:
python3(1)─┬─java(27)─┬─{java}(35)
В данном случае PID процесса Java = 27.
Шаг 1.3: Снятие thread dump в Kubernetes#
# Определение переменных
POD_NAME="gs-cluster-1-global-scheduler-7d49bffb5f-d9llp"
NAMESPACE="gs-ctk"
JAVA_PID=27
# Снятие одиночного дампа
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jstack $JAVA_PID" > thread_dump_k8s_$(date +%Y%m%d_%H%M%S).txt
# Снятие нескольких дампов с интервалом
for i in {1..3}; do
echo "Снятие дампа $i в $(date)"
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jstack $JAVA_PID" > thread_dump_k8s_${i}_$(date +%Y%m%d_%H%M%S).txt
sleep 10
done
Шаг 1.4: Снятие heap dump в Kubernetes#
# Снятие heap dump
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jmap -dump:live,format=b,file=/tmp/heap_dump.hprof $JAVA_PID"
# Копирование с пода
kubectl cp $NAMESPACE/$POD_NAME:/tmp/heap_dump.hprof ./heap_dump_k8s_$(date +%Y%m%d_%H%M%S).hprof -c globalscheduler
# Очистка
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "rm -f /tmp/heap_dump.hprof"
Часть 2: Снятие снапшотов на автономном сервере#
Предварительные требования для автономного сервера#
Права sudo пользователя или доступ под пользователем
global.Знание пути к установке GlobalScheduler (обычно
/opt/global/globalserver).
Шаг 2.1: Определение PID процесса GlobalScheduler#
# Определение пода через systemd
sudo systemctl status globalscheduler
PID=$(systemctl show globalscheduler --property=MainPID | cut -d= -f2)
# Проверка PID
echo "PID процесса GlobalScheduler: $PID"
# PID должен совпадать с тем что вывел syst
Шаг 2.2: Определение пути к Java#
# Проверка используемой Java
sudo ls -l /proc/$PID/exe
# Проверка установленных версий Java
update-alternatives --list java
Шаг 2.3: Снятие thread dump на автономном сервере#
# Снятие одиночного дампа
sudo $JAVA_HOME/bin/jstack $PID > /tmp/thread_dump_standalone_$(date +%Y%m%d_%H%M%S).txt
# Снятие нескольких дампов
for i in {1..3}; do
echo "Снятие дампа $i в $(date)"
sudo $JAVA_HOME/bin/jstack $PID > /tmp/thread_dump_standalone_${i}_$(date +%Y%m%d_%H%M%S).txt
sleep 10
done
Шаг 2.4: Снятие heap dump на автономном сервере#
# Снятие heap dump
sudo $JAVA_HOME/bin/jmap -dump:live,format=b,file=/tmp/heap_dump_standalone_$(date +%Y%m%d_%H%M%S).hprof $PID
# Проверка размера
ls -lh /tmp/heap_dump_*.hprof
Предупреждение
Снятие heap dump может занять значительное время и потребовать много места на диске (обычно несколько гигабайт). Убедитесь, что в /tmp достаточно свободного места.
Часть 3: Анализ полученных дампов#
Быстрый анализ через командную строку#
# Для анализа перейдите в директорию с дампами
cd /path/to/dumps
# Поиск заблокированных потоков
grep -c "BLOCKED" thread_dump_*.txt
# Поиск deadlock
grep -l "deadlock" thread_dump_*.txt
# Подсчет потоков по состоянию
grep "java.lang.Thread.State" thread_dump_*.txt | awk '{print $2}' | sort | uniq -c | sort -nr
# Анализ состояния всех потоков
for file in thread_dump_*.txt; do
echo "=== $file ==="
grep "java.lang.Thread.State" "$file" | awk '{print $2}' | sort | uniq -c
echo
done
Часть 4: Интерпретация результатов для GlobalScheduler#
Типичные сценарии проблем#
Сценарий 1: Дедлок в базе данных#
Found one Java-level deadlock:
=============================
"Thread-15":
waiting to lock monitor 0x00007f8c4c00b8e0 (object 0x00000000f8a8f8a0, a org.postgresql.core.v3.QueryExecutorImpl)
Решение: Проверка длительных транзакций в БД PostgreSQL.
Сценарий 2: Блокировка ресурсов планировщика#
"QuartzScheduler_Worker-10" #25 prio=5 os_prio=0 tid=0x00007f8c54010800 nid=0x5e3a waiting for monitor entry [0x00007f8c4a1fe000]
java.lang.Thread.State: BLOCKED (on object monitor)
Решение: Увеличение размера пула потоков в настройках Quartz.
Сценарий 3: Проблемы с подключением к Global ERP#
"Thread-8" #20 prio=5 os_prio=0 tid=0x00007f8c5420a800 nid=0x5f1c runnable [0x00007f8c4a3ff000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
Решение: Проверка подключения к SOAP-сервису Global ERP.
Часть 5: Устранение распространенных проблем#
Общие проблемы и решения#
Проблема: «Permission denied» при выполнении jstack/jmap#
В Kubernetes: Убедитесь, что у пользователя есть права на выполнение kubectl exec в namespace gs-ctk.
На автономном сервере: Выполняйте команды с правами пользователя global:
sudo -u global jstack $PID
Проблема: Процесс Java не найден#
В Kubernetes: Проверьте PID процесса Java командой внутри пода:
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- ps aux | grep java
На автономном сервере: Убедитесь, что GlobalScheduler запущен:
sudo systemctl status globalscheduler
Проблема: jstack/jmap не найдены#
Решение: Укажите полный путь к утилитам JDK:
# В Kubernetes
/root/globalserver/workspace/local/jdk21/bin/jstack $PID
# На автономном сервере
/usr/local/jdk-21/bin/jstack $PID
# ИЛИ
/usr/lib/jvm/java-21-openjdk-amd64/bin/jstack $PID
Проблема: Недостаточно места для heap dump#
Решение: Используйте директорию с достаточным местом:
# В Kubernetes
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jmap -dump:live,format=b,file=/opt/global/temp/heap_dump.hprof $JAVA_PID"
# На автономном сервере
sudo -u global jmap -dump:live,format=b,file=/opt/global/temp/heap_dump.hprof $PID
Часть 6: Дополнительные диагностические команды#
Для автономного сервера#
# Мониторинг системных ресурсов
top -p $PID
htop -p $PID
# Память процесса
pmap -x $PID | tail -1
# Открытые файлы
lsof -p $PID | wc -l
# Логи GlobalScheduler
sudo journalctl -u globalscheduler -f
sudo tail -f /opt/global/globalserver/logs/globalscheduler.log
Для Kubernetes#
# Логи пода
kubectl logs -n gs-ctk $POD_NAME -c globalscheduler -f
# Мониторинг ресурсов
kubectl top pod -n gs-ctk $POD_NAME
# Описание пода для диагностики
kubectl describe pod -n gs-ctk $POD_NAME
# Проверка состояния контейнера
kubectl get pod $POD_NAME -n gs-ctk -o jsonpath='{.status.containerStatuses[0].ready}'
Часть 7: Автоматический мониторинг и снятие дампов#
Скрипт для автоматического снятия дампов при высокой нагрузке (Kubernetes)#
#!/bin/bash
NAMESPACE="gs-ctk"
POD_LABEL="app=globalscheduler"
POD_NAME="gs-cluster-1-global-scheduler-7d49bffb5f-d9llp"
CPU_THRESHOLD=80
MEMORY_THRESHOLD=85
while true; do
if [ -z "$POD_NAME" ]; then
echo "$(date): Pod not found"
sleep 60
continue
fi
# Получаем использование CPU и памяти
POD_STATS=$(kubectl top pod -n $NAMESPACE $POD_NAME --no-headers 2>/dev/null)
if [ $? -eq 0 ]; then
CPU_USAGE=$(echo $POD_STATS | awk '{print $2}' | sed 's/%//')
MEMORY_USAGE=$(echo $POD_STATS | awk '{print $3}' | sed 's/%//')
echo "$(date): Pod $POD_NAME - CPU: ${CPU_USAGE}%, Memory: ${MEMORY_USAGE}%"
# Проверяем пороги
if [ $CPU_USAGE -gt $CPU_THRESHOLD ] || [ $MEMORY_USAGE -gt $MEMORY_THRESHOLD ]; then
echo "$(date): High resource usage detected! Taking emergency thread dump..."
# Получаем PID Java процесса
JAVA_PID=$(kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- ps aux | grep java | grep -v grep | awk '{print $2}')
if [ -n "$JAVA_PID" ]; then
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jstack $JAVA_PID" > emergency_dump_${TIMESTAMP}.txt
echo "$(date): Emergency dump saved to emergency_dump_${TIMESTAMP}.txt"
fi
fi
else
echo "$(date): Failed to get pod metrics"
fi
sleep 60
done
Часть 8: Дополнительные инструменты анализа#
# Визуальные инструменты (требуют GUI)
jvisualvm
jmc
Совет
Для комплексного анализа рекомендуется снимать несколько дампов с интервалом 10–15 секунд и сравнивать их для выявления изменяющихся состояний потоков. Всегда сохраняйте дампы с временными метками для последующего анализа.
При анализе обращайте внимание на:
Количество блокированных потоков (BLOCKED).
Наличие deadlock.
Потоки, находящиеся в состоянии RUNNABLE продолжительное время.
Рост количества потоков со временем.
Потоки, ожидающие доступа к одним и тем же мониторам.