spring boot redis操作抛出断管错误

Posted

技术标签:

【中文标题】spring boot redis操作抛出断管错误【英文标题】:spring boot redis operation throw broken pipe error 【发布时间】:2017-03-28 07:08:26 【问题描述】:

我们在spring boot项目中使用redis。运行一段时间后,redis 操作可能会抛出破管错误,但有时会成功。重新启动服务将解决此问题,但这不是一个好主意。

我无法说出它发生的原因。似乎池中的某些 redis 连接不可用,但并未关闭并从池中逐出。

我的问题是:

导致管道损坏错误的可能原因是什么? 如果长时间没有redis操作,池中的空闲连接是否会变得不可用? 发生断管错误时,连接是否会关闭并从池中逐出?

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

【问题讨论】:

【参考方案1】:

回答我的问题:

为什么会发生断管错误?

TransactionSynchronizationManager 将 RedisConnection 保存在线程中,不会关闭它或将其返回到池中,请参阅 RedisTemplate.java 和 RedisConnectionUtils.java。重启redis服务器后,对线程中持有的RedisConnection进行操作会抛出破管错误。

如何解决?

为所有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 是否为您解决了这个问题?【参考方案2】:

发生这种情况的原因有很多,其中一个可能是您使用长期连接时(例如,在应用程序启动时连接到 Redis,然后反复使用该连接)。

要做的一些事情是:

    如果连接中断,请重新连接(需要一些 try/catch 魔法来防止错误传播到您的应用程序逻辑) 或者更好的是使用TestOnBorrow - 当您请求资源时发送 PING 请求。TestOnReturn - 当您返回资源时发送 PING到池。TestWhileIdle - 从池中的空闲资源发送定期 PIN。 在需要连接的时候连接,然后断开连接

关于如果长时间没有redis操作,池中的空闲连接是否会变得不可用?

maxidle 意味着在任何给定时间系统允许'maxIdle'许多连接处于空闲状态,其余的将不断检查、关闭并返回到池中。 我不知道空闲连接无法使用的原因。无论如何,这可以通过使用上面提到的方法来摆脱。

【讨论】:

Spring @ConfigurationProperties RedisProperties 中没有TestOnBorrow 配置,是否意味着需要创建客户JedisConnectionFactory 并在JedisPoolConfig 中添加TestOnBorrow? @geln 是的,您必须创建一个自定义 JedisConnectionFactory 并将其传入

以上是关于spring boot redis操作抛出断管错误的主要内容,如果未能解决你的问题,请参考以下文章

抛出异常时,Spring Boot 不显示自定义错误

Spring Boot Web 应用程序:JSP 页面抛出 404 错误

spring-boot Cache redis 类型转换错误

Intellij 运行相同的 Spring Boot 项目,社区版运行良好,但终极版抛出错误

为啥带有嵌入式 H2 的 Spring Boot 会抛出“org.h2.message.DbException”错误?

使用spring boot MongoTemplate删除mongodb记录会抛出错误:“Second is Null”