Перейти к основному содержанию
`Ctrl`+`K`

# Руководство по снятию снапшотов GlobalScheduler

## Назначение

Процедура снятия снапшотов (thread dump) процесса GlobalScheduler позволяет диагностировать проблемы производительности, зависания потоков и дедлоки в системе Global ERP.

Примечание

Снятие снапшотов требуется при следующих симптомах:

- Зависание задач планировщика.

- Высокая нагрузка на CPU процесса GlobalScheduler.

- Увеличивающееся потребление памяти.

- Ошибки выполнения запланированных заданий.

- Длительное выполнение задач без прогресса.

## Общие предварительные требования

- Установленная Java Development Kit (JDK) той же версии, что используется для запуска GlobalScheduler.

- Знание PID процесса Java GlobalScheduler.

- Права доступа к процессу GlobalScheduler.

## Часть 1: Снятие снапшотов в Kubernetes

### Предварительные требования для Kubernetes

- Доступ к кластеру Kubernetes с правами на выполнение `kubectl exec`.

- Настроенный `kubeconfig` для namespace `gs-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 секунд и сравнивать их для выявления изменяющихся состояний потоков. Всегда сохраняйте дампы с временными метками для последующего анализа.

При анализе обращайте внимание на:

1. Количество блокированных потоков (BLOCKED).

2. Наличие deadlock.

3. Потоки, находящиеся в состоянии RUNNABLE продолжительное время.

4. Рост количества потоков со временем.

5. Потоки, ожидающие доступа к одним и тем же мониторам.