
# GeminiDB Redis连接检测机制和客户端重连重试建议
#### GeminiDB Redis连接检测机制
在网络状况良好且客户端连接正常的前提下，GeminiDB Redis不会主动断开用户的连接。和开源Redis连接机制相同，服务端启用了 TCP Keepalive 机制，可以检测并识别由于网络中断、客户端崩溃或NAT超时等原因导致的异常连接，并及时清理这些无效连接，以防止服务端的连接资源被耗尽，从而保障服务的稳定性与可用性。
#### 参数解释
表1参数说明 
| 参数            | 说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
|:---|:---|
| ClientTimeout | - 空闲超时参数，表示用于判断服务端检测到该连接空闲多久后，主动断开。  - 默认值为0，表示GeminiDB不会主动关闭空闲连接。  - 只作用于普通客户端连接，对Pub/Sub连接和阻塞命令（如brpop）连接不生效。   |
| tcp-keepalive | 表示GeminiDB向空闲连接发送TCP Keepalive探测包的频率，默认为120s。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| TCP_KEEPINTVL | 表示探测包发送的时间间隔（秒），默认为40s。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| TCP_KEEPCNT   | 表示发送多少次探测包，默认为3次。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
   
#### 检测机制
1. 当连接空闲时间超过tcp-keepalive设置的时间后，GeminiDB会开始发送Keepalive探测包，以确认连接是否仍然有效。
2. 如果对方没有响应，GeminiDB会每隔TCP_KEEPINTVL秒重试一次探测。
3. 如果连续TCP_KEEPCNT次探测均无响应，则判定该连接已失效，GeminiDB会主动断开该连接，以释放服务端的连接资源，保障整体服务的稳定性和可用性。
![](https://support.huaweicloud.com/redisug-nosql/public_sys-resources/note_3.0-zh-cn.png)
如果客户使用的GeminiDB Redis实例内核小版本低于5.0.6.4，其tcp-keepalive设置较为敏感，且不支持动态调整，建议升级至最新版本。新版本不仅优化了探活机制，降低了因网络波动导致连接误断的小概率问题，还增强了连接管理的灵活性与稳定性，有助于提升整体服务的可靠性。
#### 客户端连接重连和命令重试使用介绍
在实际应用中，客户端与服务端之间的网络通信可能会受到各种因素的影响，如网络丢包、抖动异常或临时性故障等。为提升系统的健壮性和用户体验，建议客户端引入连接重连和命令重试机制，以降低网络抖动的影响。
- [连接重连]
  在网络出现丢包、抖动等情况，或是进行GeminiDB在线滚动节点规格变更、版本升级等操作时，可能会导致客户端与GeminiDB实例之间的连接中断。为了确保操作能够成功执行，客户端应当具备自动重连机制。推荐采用主流的Redis客户端，比如Jedis，在获取连接之前先进行连接检测，或者定期检查空闲连接的状态，以此来防止因连接问题而导致的操作超时或失败。这样可以有效提升系统的连接性和可靠性。
  - 设置testonBorrow，在获取连接时进行有效性检测。
    ```
    JedisPoolConfig poolConfig = new JedisPoolConfig();
    // 设置从连接池获取连接时进行有效性检查
    poolConfig.setTestOnBorrow(true);
    // 其他配置...
     
    // 创建连接池时使用配置
    JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);
    ```
    
  
  
  当testOnBorrow设置为true时，每次从连接池获取连接时会进行连接有效性检测（即执行一次ping操作）。如果检测发现连接已失效，当前连接会被丢弃，并尝试获取另一个可用连接。这个设置可以确保每次获取到的连接都是有效的，但会带来一次额外的ping检测开销。在业务并发量较高的场景下，可以考虑启用空闲连接检测（如testWhileIdle），这样可以在连接未被使用时提前检测其有效性，减少每次获取连接时的检测开销，从而提升整体性能。
  - 空闲连接检测，通过异步线程来探测连接是否正常，相比testonBorrow开销小一些。
  
  
  ```
  JedisPoolConfig poolConfig = new JedisPoolConfig();
  poolConfig.setTestWhileIdle(true);
  // 空闲连接检测周期（30-60秒）
  poolConfig.setTimeBetweenEvictionRunsMillis(30000);
  // 每次检查的连接数（建议为负值，表示检查全部空闲连接）
  poolConfig.setNumTestsPerEvictionRun(-1);
  // 连接最小空闲时间（超过此值才开始检测）
  poolConfig.setMinEvictableIdleTimeMillis(60000); // 60秒
  JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);
  ```
  表2参数说明 
  | **名称**                        | **说明**                              | **默认值**          | **建议**                              |
  |:---|:---|:---|:---|
  | testWhileIdle                 | 是否在空闲资源监测时通过ping命令监测连接有效性，无效连接将被销毁。 | false            | true                                |
  | timeBetweenEvictionRunsMillis | 空闲资源的检测周期（单位为毫秒）                    | -1（不检测）          | 建议设置，周期自行选择，可设置30000。               |
  | minEvictableIdleTimeMillis    | 资源池中资源的最小空闲时间（单位为毫秒），达到此值后空闲资源将被移除。 | 1,800,000（即30分钟） | 建议设置为默认值。                           |
  | numTestsPerEvictionRun        | 做空闲资源检测时，每次检测资源的个数。                 | 3                | 可根据自身应用连接数进行微调，建议设置为 -1，对所有连接做空闲监测。 |
     
- [命令重试]
  在Redis命令执行失败时，例如由于网络抖动导致的超时，对于像SET key value这类具有幂等性的操作，可以考虑进行重试，从而避免短暂网络问题带来的影响。而对于LPUSH、LPOP等非幂等操作，重试可能会导致数据被多次插入或删除，进而引发业务逻辑错误。因此，在使用重试策略时，需结合具体业务场景进行分析，判断是否适合重试，并合理设置重试次数与重试间隔，以保障操作的成功执行。
  ```
  public <T> T executeWithRetry(RedisCommand<T> command, int maxAttempts) {
      JedisException lastException = null;
      
      for (int attempt = 1; attempt <= maxAttempts; attempt++) {
          try (Jedis jedis = jedisPool.getResource()) {
              return command.execute(jedis);
          } catch (JedisConnectionException e) {
              lastException = e;
              log.warn("Redis connection failed (attempt {}/{}): {}", 
                  attempt, maxAttempts, e.getMessage());
             
                  // 可通过sleep或者指数退避等待策略，间隔一段时间再重试
                 Thread.sleep(waitTime);
          }
      }
      throw new RedisOperationException("Max retry attempts reached", lastException);
  }
  ```
  
