You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Jedis执行MGET命令时遇SocketException: Broken pipe异常求助

问题分析与解决方案

核心问题根源

你遇到的SocketException: Broken pipe和Socket is closed异常,本质是Jedis连接复用失效:

  • 手动通过isConnected()判断连接状态不可靠,Redis服务器可能已主动关闭空闲连接(比如配置了timeout参数),但客户端还认为连接可用。
  • 代码中重复获取连接的逻辑可能导致连接池资源泄漏,或者拿到的是已经失效的连接。

解决方案1:重构连接使用逻辑,避免手动管理连接状态

不要自己判断连接是否可用,直接从连接池获取新连接(连接池会自动管理连接有效性),并使用try-with-resources确保连接正确归还:

private Boolean isTokenExpired(long programId, String token, Jwt jwt) {
    int dbIndex = generalConfigHelper.getInt(REDIS_DB_INDEX);
    // 直接从连接池获取连接,try-with-resources自动关闭归还
    try (Jedis jedis = redisHelper.getConnection()) {
        jedis.select(dbIndex);
        
        Claims claims = (Claims) jwt.getBody();
        String tokenCacheKey = programId + ":Tokens:" + token;
        String expiryTimeKey = programId + ":InvalidateTokensBefore:" + claims.get("user_id");
        
        List<String> values = jedis.mget(tokenCacheKey, expiryTimeKey);
        if (values.get(0) != null) {
            return true;
        } else if (values.get(1) != null) {
            log.info("redis: exp value:" + values.get(1));
            long exp = Long.parseLong(claims.get("exp_ms").toString());
            return exp < Long.parseLong(values.get(1));
        }
    } catch (Exception ex) {
        log.error("Error while checking token expiration", ex);
        // 可选:添加重试逻辑,比如重试1-2次
        // return retryIsTokenExpired(programId, token, jwt);
    }
    return false;
}

关键改进点:

  • 移除手动判断连接状态的逻辑,交给连接池管理连接生命周期。
  • 使用try-with-resources(Jedis实现了Closeable接口),确保无论是否异常,连接都会正确归还到池里,避免泄漏。

解决方案2:优化Jedis连接池配置,添加连接有效性检测

在你的连接池配置中添加以下参数,让连接池自动剔除失效连接:

// 从连接池获取连接时,先验证连接有效性
config.setTestOnBorrow(true);
// 连接空闲时,定期验证有效性
config.setTestWhileIdle(true);
// 空闲连接检测的时间间隔
config.setTimeBetweenEvictionRunsMillis(30000); // 30秒
// 每次空闲检测的连接数
config.setNumTestsPerEvictionRun(10);
// 连接最小空闲时间,超过则被剔除
config.setMinEvictableIdleTimeMillis(60000); // 1分钟

参数说明:

  • testOnBorrow:获取连接时执行PING命令验证,确保拿到的是可用连接。
  • testWhileIdle:后台线程定期检测空闲连接,剔除失效的连接。
  • 配合timeBetweenEvictionRunsMillis和minEvictableIdleTimeMillis,可以及时清理Redis服务器主动关闭的空闲连接。

解决方案3:处理连接失效的重试逻辑

如果偶尔还是会遇到连接失效,可以添加简单的重试机制,避免单次异常导致业务失败:

private Boolean isTokenExpired(long programId, String token, Jwt jwt) {
    return retryIsTokenExpired(programId, token, jwt, 2);
}

private Boolean retryIsTokenExpired(long programId, String token, Jwt jwt, int retryCount) {
    int dbIndex = generalConfigHelper.getInt(REDIS_DB_INDEX);
    try (Jedis jedis = redisHelper.getConnection()) {
        jedis.select(dbIndex);
        
        Claims claims = (Claims) jwt.getBody();
        String tokenCacheKey = programId + ":Tokens:" + token;
        String expiryTimeKey = programId + ":InvalidateTokensBefore:" + claims.get("user_id");
        
        List<String> values = jedis.mget(tokenCacheKey, expiryTimeKey);
        if (values.get(0) != null) {
            return true;
        } else if (values.get(1) != null) {
            log.info("redis: exp value:" + values.get(1));
            long exp = Long.parseLong(claims.get("exp_ms").toString());
            return exp < Long.parseLong(values.get(1));
        }
    } catch (SocketException | JedisConnectionException ex) {
        log.warn("Redis connection failed, retrying...", ex);
        if (retryCount > 0) {
            return retryIsTokenExpired(programId, token, jwt, retryCount - 1);
        }
        log.error("Redis connection failed after retries", ex);
    } catch (Exception ex) {
        log.error("Error while checking token expiration", ex);
    }
    return false;
}

额外注意事项

  • 确保Redis服务器的timeout配置(如果有)不要短于连接池的minEvictableIdleTimeMillis,避免服务器提前关闭连接。
  • 不要在多个线程间复用同一个Jedis实例,Jedis不是线程安全的,必须每个线程从池里单独获取。

内容的提问来源于stack exchange,提问作者Jayanth Naidu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:15:59