La solicitud no ha regresado después de 10 segundos, cómo solucionar problemas

prefacio

La capacidad del programador para localizar el problema es muy importante, si hay un problema en la línea de producción y la solicitud enviada por el cliente no ha sido devuelta después de 10 segundos, ¿cómo lo investigaría?

jstack

jstack es un comando muy poderoso que puede generar una instantánea del hilo del momento actual del proceso especificado.Antes de hablar sobre cómo usar jstack para verificar, necesitamos algunos conocimientos previos

Transiciones de estado de subprocesos
Expresar ilustrar
NUEVO En el estado inicial, el hilo está construido, pero no se ha llamado al método start()
FUNCIONABLE Estado operativo
ESPERANDO Estado de espera, ingresar a este estado significa que el subproceso actual debe esperar a que otros subprocesos realicen una acción específica (notificación o interrupción)
TIMED_ESPERANDO Estado de espera de tiempo de espera, volverá por sí mismo dentro del tiempo especificado
OBSTRUIDO Estado bloqueado, lo que indica que el hilo está bloqueado en el candado
TERMINADO Estado terminado, que indica que el subproceso actual ha completado la ejecución

imagen.png

Después de conocer los estados anteriores, será mucho más fácil para nosotros entender la información de volcado de jstack.En la información de volcado, debemos prestar atención a los siguientes estados

  1. punto muerto, punto muerto
  2. Esperando recursos, Esperando en condición
  3. Esperando para obtener el monitor, Esperando la entrada del monitor (generalmente esto es un problema)
  4. bloqueado, bloqueado
  5. En ejecución, Runnable
  6. Suspendido
Simular espera en condición
public class ThreadSleepTest {

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(10000000);
    }

}
复制代码

imagen.png

Simular interbloqueo
public class ThreadDeadLockTest {

    public static void main(String[] args) {

        StringBuilder a = new StringBuilder();
        StringBuilder b = new StringBuilder();

        Thread thread1 = new Thread(() -> {
            synchronized (a) {
                a.append("a");
                b.append("b");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (b) {
                    b.append("c");
                    a.append("d");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (b) {
                b.append("b");
                a.append("a");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a) {
                    a.append("c");
                    b.append("d");
                }
            }
        });
        thread1.setName("线程1");
        thread2.setName("线程2");

        thread1.start();
        thread2.start();
    }
}
复制代码

Se puede ver que tanto el subproceso 1 como el subproceso 2 están esperando recursos de bloqueo y están en estado BLOQUEO

imagen.png

Mire más abajo, puede ver el indicador de interbloqueo

imagen.png

ejecutable simulado
public class ThreadRunningTest {

    public static void main(String[] args) throws InterruptedException {
        int sum = 0;
        while (true) {
            sum += 1;
        }
    }

}
复制代码

imagen.png

Cómo encontrar hilos problemáticos

Nuestro programa simula todo lo anterior, y el hilo del problema se puede encontrar fácilmente, pero en el entorno de producción real, cuando la solicitud es muy grande, ¿cómo encontrar el hilo del problema?

El método más clásico son los siguientes cuatro pasos, averigüe el subproceso que ocupa la CPU más alta e imprima la información de la pila

// 找出占用cpu最高的进程
top
// 找出该进程下占用cpu最高的线程
top -Hp pid
// 打印该线程的16进制数据
printf "%x\n" 线程id
// 打印堆栈信息
jstack pid | grep nid=16进制
复制代码

Time+: el tiempo acumulado que el hilo ocupa la CPU

imagen.png

También hay un método más ingenioso: después de volcar una pila de subprocesos, volcar una copia después de un período de tiempo.

imagen.png

Use Comparar archivos en IDEA para comparar y analizar las similitudes y diferencias entre los dos archivos de volcado. Si hay una solicitud que no ha regresado por más de 10 segundos, teóricamente existirá en ambos archivos. En este momento, puede averiguar el ID de hilo Es un método más clásico, y este método también se puede usar para solicitudes de captura de paquetes.

imagen.png

base de datos

Si la solicitud no ha sido devuelta por mucho tiempo, generalmente primero verifico la base de datos.Si la base de datos es penetrada, las consecuencias serán muy graves.

Ahora verifique si el registro de consultas lentas está habilitado

mysql> show variables like '%slow_query_log%';
+---------------------+----------------------------------------------------------+
| Variable_name       | Value                                                    |
+---------------------+----------------------------------------------------------+
| slow_query_log      | ON                                                       |
| slow_query_log_file | /usr/local/mysql/data/zhangxiaobindeMacBook-Pro-slow.log |
+---------------------+----------------------------------------------------------+
2 rows in set (0.01 sec)
复制代码

Ejecutar un sql lento

mysql> select sleep(100), id from user where id = 1;
+------------+----+
| sleep(100) | id |
+------------+----+
|          0 |  1 |
+------------+----+
1 row in set (1 min 40.24 sec)
复制代码

Podemos consultar este sql en el registro de consultas lentas y saber cuánto tiempo tardó en ejecutarse

/usr/local/mysql/bin/mysqld, Version: 5.7.27-log (MySQL Community Server (GPL)). started with:
Tcp port: 3306  Unix socket: /tmp/mysql.sock
Time                 Id Command    Argument
# Time: 2021-07-03T08:33:40.680324Z
# User@Host: root[root] @ localhost []  Id:     7
# Query_time: 100.238479  Lock_time: 0.168766 Rows_sent: 1  Rows_examined: 1
use test;
SET timestamp=1625301220;
select sleep(100), id from user where id = 1;
复制代码

Después de encontrar el sql, es fácil de hacer. Puede usar la palabra clave de explicación para ver el proceso de ejecución del sql

mostrar lista de procesos

También es posible que el sql no haya ingresado al registro de consultas lentas. En este momento, puede usar el comando show processlist para verificar si hay algún sql que tarde demasiado en ejecutarse y luego localizar el sql problemático más rápidamente.

mysql> show processlist;
+----+------+-----------------+------+---------+------+------------+----------------------------------------------+
| Id | User | Host            | db   | Command | Time | State      | Info                                         |
+----+------+-----------------+------+---------+------+------------+----------------------------------------------+
|  2 | root | localhost:54718 | test | Sleep   |   35 |            | NULL                                         |
|  3 | root | localhost:54725 | NULL | Sleep   |   42 |            | NULL                                         |
|  4 | root | localhost:51794 | test | Sleep   |   62 |            | NULL                                         |
|  5 | root | localhost:51795 | NULL | Sleep   |   12 |            | NULL                                         |
|  7 | root | localhost       | test | Query   |   23 | User sleep | select sleep(100), id from user where id = 1 |
|  9 | root | localhost       | NULL | Query   |    0 | starting   | show processlist                             |
+----+------+-----------------+------+---------+------+------------+----------------------------------------------+
6 rows in set (0.00 sec)
复制代码

Referencias

"Programación Java Concurrente"

Supongo que te gusta

Origin juejin.im/post/6980666498151006245
Recomendado
Clasificación