JedisPool - Java.net.SocketException: tubería rota (error de escritura)

I. Introducción

Cuando se utiliza la escritura de subprocesos múltiples JedisPoll, la tubería rota se informa periódicamente, y la tarea es normal después de reiniciar, y el error vuelve a ocurrir después de un período de tiempo.

JedisPool está configurado de la siguiente manera:

  val config = new JedisPoolConfig
  config.setMaxIdle(20)
  config.setMinIdle(20)
  config.setNumTestsPerEvictionRun(-2)
  config.setTimeBetweenEvictionRunsMillis(30000)
  config.setSoftMinEvictableIdleTimeMillis(3600000)
  config.setMinEvictableIdleTimeMillis(-1)
  config.setTestOnBorrow(false)
  config.setTestOnReturn(false)
  config.setTestWhileIdle(false)

2. Análisis y solución de problemas

1. Escenario del problema

La tarea es muy simple. Al usar JedisPool para escribir datos en varios subprocesos en el clúster, el error se generará después de que la tarea se ejecute durante un período de tiempo. Después de hacer una captura Try, se descubre que la tarea es un error ocasional, no toda la escritura falla; se informa el error. El análisis preliminar es que el método getResource puede obtener conexiones no válidas, lo que genera fallas de escritura ocasionales, y la tarea se ejecuta normalmente después de reiniciar porque las conexiones redis adquiridas por la tarea recién iniciada son válidas, por lo que no hay tubería rota.

2. Resolución de problemas

A través de la configuración, puede ver que las siguientes tres configuraciones de parámetros son todas falsas:

  config.setTestOnBorrow(false)
  config.setTestOnReturn(false)
  config.setTestWhileIdle(false)

Establezca TestOnBorrow en verdadero o testWhileIdle en verdadero, si eso no funciona, configure ambos en verdadero.

 

3. Significado del parámetro

· establecer TestOnBorrow

préstamo es obtener la conexión del conjunto de conexiones de JedisPool. Este parámetro controla la validez de la conexión al obtener la conexión redis. Si se comprueba que el enlace no es válido, se limpiará y se volverá a adquirir para una nueva conexión. Aquí puede consultar el método getResource en el código fuente, este método ejecuta el método de préstamo de objetos del grupo para obtener la conexión. La ubicación de esta clase es redis.client.util.Pool, y la implementación subyacente se refiere a org.apache .commons.pool2.impl.GenericObjectPool.

  public T getResource() {
    try {
      return internalPool.borrowObject();
    } catch (Exception e) {
      throw new JedisConnectionException("Could not get a resource from the pool", e);
    }
  }

Después de configurar este parámetro, cuando se pide prestado para obtener una nueva conexión, si la conexión falla, se llamará al método activeObject para restablecer el estado interno de la conexión, lo que equivale a obtener una nueva conexión:

    try {
        this.factory.activateObject(p);
    } catch (Exception var15) {
        try {
            this.destroy(p);
        } catch (Exception var14) {
        }

        p = null;
        if (create) {
            NoSuchElementException nsee = new NoSuchElementException("Unable to activate object");
            nsee.initCause(var15);
            throw nsee;
        }
    }

 

· Establecer prueba al regresar

return es un parámetro configurado cuando se llama a returnResource. Este parámetro controla la validez de la conexión cuando la conexión del recurso se devuelve al grupo de conexiones. Si la conexión no es válida, se limpiará. Este método ejecuta el método returnResourceObject del grupo para devolver la conexión. La ubicación de esta clase es redis.client.util.pool, y la implementación subyacente también hace referencia a GenericObjectPool.

  public void returnResourceObject(final T resource) {
    if (resource == null) {
      return;
    }
    try {
      internalPool.returnObject(resource);
    } catch (Exception e) {
      throw new JedisException("Could not return the resource to the pool", e);
    }
  }

Si este parámetro está configurado, el código llamará al método ValidateObject en la conexión para juzgar la validez al devolverObjectResource y, si no es válido, destruirá:

        if (this.getTestOnReturn() && !this.factory.validateObject(p)) {
            try {
                this.destroy(p);
            } catch (Exception var10) {
                this.swallowException(var10);
            }

            try {
                this.ensureIdle(1, false);
            } catch (Exception var9) {
                this.swallowException(var9);
            }

            this.updateStatsReturn(activeTime);
        }

· Establecer prueba mientras está inactivo

Este parámetro controla la prueba de conexiones inactivas. Este parámetro corresponde a config.setTimeBetweenEvictionRunsMillis(30000), donde el juicio inactivo se basa en el tiempo de milisegundos establecido en este parámetro. La validez de la conexión inactiva se comprueba aquí, y el método de desalojo es lo que realmente realiza la limpieza:

    public void evict() throws Exception {
        this.assertOpen();
        if (this.idleObjects.size() > 0) {
            PooledObject<T> underTest = null;
            EvictionPolicy<T> evictionPolicy = this.getEvictionPolicy();
            synchronized(this.evictionLock) {
                EvictionConfig evictionConfig = new EvictionConfig(this.getMinEvictableIdleTimeMillis(), this.getSoftMinEvictableIdleTimeMillis(), this.getMinIdle());
                boolean testWhileIdle = this.getTestWhileIdle();
                int i = 0;
                int m = this.getNumTests();

                while(true) ...

La línea 7 aquí también implica dos parámetros de tiempo, MinEvictableIdleTimeMillis y SoftMinEvictableIdleTimeMillis. Esto implica la selección del grupo de subprocesos idleEvictTime. El primero se lee primero. Si el primero es negativo, el valor predeterminado es maxLong. Si el primero no está configurado, se lee el último. , por lo que el grupo de subprocesos aparecerán algunos parámetros de configuración de -1, -2, y en realidad no hay tiempo de -1 ms.

    public EvictionConfig(long poolIdleEvictTime, long poolIdleSoftEvictTime, int minIdle) {
        if (poolIdleEvictTime > 0L) {
            this.idleEvictTime = poolIdleEvictTime;
        } else {
            this.idleEvictTime = 9223372036854775807L;
        }

        if (poolIdleSoftEvictTime > 0L) {
            this.idleSoftEvictTime = poolIdleSoftEvictTime;
        } else {
            this.idleSoftEvictTime = 9223372036854775807L;
        }

        this.minIdle = minIdle;
    }

El juicio real para comenzar a ejecutar la lógica de desalojo está en la siguiente función:

    public boolean evict(EvictionConfig config, PooledObject<T> underTest, int idleCount) {
        return config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() && config.getMinIdle() < idleCount || config.getIdleEvictTime() < underTest.getIdleTimeMillis();
    }

El desalojo se lleva a cabo si se cumple alguna de las siguientes condiciones:

A. El tiempo de inactividad de la conexión es mayor que softMinEvictableIdleTimeMillis y el número de inactividad del conjunto de conexiones es menor que minIdleNum, que es el tercer parámetro en EvictionConfig.

B. El tiempo de inactividad de la conexión es mayor que minEvictableIdleTimeMillis 

Por supuesto, no solo será desalojado, sino que, hasta cierto punto, el grupo de subprocesos también ejecutará sureIdle para crear una nueva conexión y garantizar que haya suficientes conexiones disponibles en el grupo de subprocesos.

3. Resumen

El informe de error anterior y la configuración de escena TestOnBorrow = true y TestWhileIdle = true están perfectamente resueltos, pero también traerán una cierta pérdida de rendimiento. Puede ajustarlo de acuerdo con su propia escena. En general, puede intentar activar solo TestOnBorrow.

Supongo que te gusta

Origin blog.csdn.net/BIT_666/article/details/123499279
Recomendado
Clasificación