Spring Boot Redis 操作抛出 Broken Pipe 错误

4
我们在Spring Boot项目中使用Redis。在运行一段时间后,Redis操作可能会抛出“broken pipe”错误,但有时它会成功。重启服务可以解决此问题,但这不是一个好主意。
我无法确定发生这种情况的原因。似乎池中的某些Redis连接无法使用,但未关闭并从池中清除。
我的问题是:
  • 导致“broken pipe”错误的可能原因是什么?
  • 如果长时间没有Redis操作,池中的空闲连接会变得无法使用吗?
  • 当“broken pipe”错误发生时,连接是否会被关闭并从池中清除?

pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml:

spring:
  redis:
    database: 0
    host: ${REDIS_HOST:127.0.0.1}
    password: ${REDIS_PASSWORD:password}
    port: ${REDIS_PORT:6379}
    timeout: ${REDIS_TIMEOUT:1000}
    pool:
      max-active: ${REDIS_MAX_ACTIVE:100}
      max-wait: ${REDIS_MAX_WAIT:500}
      max-idle: ${REDIS_MAX_IDLE:20}
      min-idle: ${REDIS_MIN_IDLE:5}

错误信息:

org.springframework.data.redis.RedisConnectionFailureException: java.net.SocketException: Broken pipe (Write failed); nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Broken pipe (Write failed)
    at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:67) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:41) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:37) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:37) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:212) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.connection.jedis.JedisConnection.hSet(JedisConnection.java:2810) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.core.DefaultHashOperations$9.doInRedis(DefaultHashOperations.java:173) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:204) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:166) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:88) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    at org.springframework.data.redis.core.DefaultHashOperations.put(DefaultHashOperations.java:170) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Broken pipe (Write failed)
    at redis.clients.jedis.Connection.flush(Connection.java:291) ~[jedis-2.8.2.jar!/:na]
    at redis.clients.jedis.Connection.getIntegerReply(Connection.java:220) ~[jedis-2.8.2.jar!/:na]
    at redis.clients.jedis.BinaryJedis.hset(BinaryJedis.java:749) ~[jedis-2.8.2.jar!/:na]
    at org.springframework.data.redis.connection.jedis.JedisConnection.hSet(JedisConnection.java:2808) ~[spring-data-redis-1.7.6.RELEASE.jar!/:na]
    ... 115 common frames omitted
Caused by: java.net.SocketException: Broken pipe (Write failed)
    at java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:1.8.0_111]
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) ~[na:1.8.0_111]
    at java.net.SocketOutputStream.write(SocketOutputStream.java:153) ~[na:1.8.0_111]
    at redis.clients.util.RedisOutputStream.flushBuffer(RedisOutputStream.java:52) ~[jedis-2.8.2.jar!/:na]
    at redis.clients.util.RedisOutputStream.flush(RedisOutputStream.java:216) ~[jedis-2.8.2.jar!/:na]
    at redis.clients.jedis.Connection.flush(Connection.java:288) ~[jedis-2.8.2.jar!/:na]
    ... 118 common frames omitted
2个回答

3

回答我的问题:

为什么会发生“broken pipe”错误?

TransactionSynchronizationManager会将RedisConnection保持在线程中,不会关闭它或将其返回到池中,请查看RedisTemplate.java和RedisConnectionUtils.java。在重新启动redis服务器后,在线程中保持的RedisConnection上操作会抛出“broken pipe”错误。

如何解决它?

为所有redis操作添加try/catch语句,如果出现错误,则从线程中取消绑定,并可以从池中获取新连接并再次执行redis操作。

private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION =
        new FallbackExceptionTranslationStrategy(JedisConverters.exceptionConverter());

public Object req(RedisRequest req) {
    try {
        return req.request();
    } catch (Exception ex) {
        if (ex instanceof NullPointerException) {
            throw ex;
        }
        DataAccessException exception = EXCEPTION_TRANSLATION.translate(ex);
        if (exception instanceof RedisConnectionFailureException) {
            RedisConnectionUtils.unbindConnection(factory);
            /** retry again */
            return req.request();
        } else {
            throw ex;
        }
    }
}

设置TestOnBorrow是否解决了您的问题? - madcolonel10

2
这可能是由于多种原因导致的,其中之一可能是当您使用长期连接时(例如,在应用程序启动时连接到Redis,然后反复使用该连接)。
一些要做的事情包括:
  1. 如果连接中断,请重新连接(需要一些try / catch魔法来防止错误传播到您的应用程序逻辑),或者更好的方法是使用
    TestOnBorrow - 当您请求资源时发送PING请求。
    TestOnReturn - 在将资源返回到池时发送PING。
    TestWhileIdle - 从池中空闲资源定期发送PING。
  2. 在需要连接时连接并在之后断开连接
关于“如果没有Redis操作很长时间,池中的空闲连接是否会变得无法使用?”
maxidle表示在任何给定时间,系统允许'maxIdle'数量的连接处于空闲状态,其余连接将不断进行检查,关闭并返回到池中。我不知道为什么空闲连接会变得无法使用。无论如何,可以通过使用上述方法来解决这个问题。

在Spring @ConfigurationProperties RedisProperties中没有TestOnBorrow配置,这是否意味着需要创建自定义的JedisConnectionFactory并在JedisPoolConfig中添加TestOnBorrow? - Geln Yang
@geln 是的,您需要创建一个自定义的JedisConnectionFactory并将其传递进去。 - StormeHawke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接