Scala/Java - Redis 连接检测与重试

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 项目实现中需要连接 redis,为了防止因网络抖动或其他原因造成的客户端连接失败,一般需要增加重试机制判断 client 是否连接成功,之前写了一版重连代码发现有 bug,借此机会看下代码 bug 以及如何更好的重连 redis。...

一.引言

项目实现中需要连接 redis,为了防止因网络抖动或其他原因造成的客户端连接失败,一般需要增加重试机制判断 client 是否连接成功,之前写了一版重连代码发现有 bug,借此机会看下代码 bug 以及如何更好的重连 redis。

二.错误代码

def getRedisClient(host: String, port: Int, timeout: Int = 0): Jedis = {
    var redisClient: Jedis = null
    var attempt = 3
    while (attempt > 0 && redisClient == null) {
      try {
        if (!timeout.equals(0)) {
          redisClient = new Jedis(host, port, timeout)
        } else {
          redisClient = new Jedis(host, port)
        }
      } catch {
        case e: Throwable => {
          e.printStackTrace()
          println(s"redis连接异常 $host 重试次数: $attempt")
          Thread.sleep(1000)
        }
      } finally {
        attempt -= 1
      }
    }
    redisClient
  }

image.gif

该代码为了实现报错重连机制,上线后发现存在下述问题 (短短几行代码这么多 bug o(╥﹏╥)o):

A.host 正确,不抛异常,client 不可用

出现该情况可能是网络抖动造成,不会进入 try catch 逻辑,但是后续使用该 client 会报错,因为redisClient = new Jedis() 方法没有返回值,所以只初始化无法判断该连接是否有效:

public Jedis(final String host, final int port, final int timeout) {
    super(host, port, timeout);
  }

image.gif

B.host 错误,抛异常,client 不可用

host 错误情况下,直接报错 java.net.UnknownHostException

C.client == null 判断问题

while 循环 client == null 作为判断条件,但只在第一次生效,因为第一次初始化后,即使是错误的 host 和 ip,client 也会变为 Jedis 对象而不再是 null,从而不会执行后续重试逻辑:

image.gif编辑

三.修改版

上面A,B,C三个问题总结下来就是两个问题:

(1) Jedis 客户端连接失败时需要抛出异常进入 catch 逻辑

(2) while 循环判断时需要新的判断条件

这个时候 ping 方法满足我们的需求,该方法返回 String,client 正常时返回 "PONG"(新的判断条件),client 无效时则抛出异常(进入catch逻辑),所以满足了上述要求:

public String ping() {
    checkIsInMulti();
    client.ping();
    return client.getStatusCodeReply();
  }

image.gif

这里将重试次数增加为 10 次,将判断条件修改为 ping 后返回的结果,同时保证 ping 后出问题可以打出异常日志方便后续问题排查:

def getRedisClient(host: String, port: Int, timeout: Int = 0): Jedis = {
  var redisClient: Jedis = null
  var attempt = 10
  var ping = ""
  while (attempt > 0 && ping != "PONG") {
    try {
      if (!timeout.equals(0) && attempt >= 10) {
        redisClient = new Jedis(host, port, timeout)
      } else {
        redisClient = new Jedis(host, port)
      } 
      ping = redisClient.ping()
    } catch {
      case e: Throwable => {
        e.printStackTrace()
        println(s"redis连接异常 $host 重试次数: $attempt")
        Thread.sleep(1000)
      }
    } finally {
      attempt -= 1
    }
  }
  println(s"初始化成功 ${host} ping: $ping")
  redisClient
}

image.gif

四.能不能再改进一点

redis 配置为主从结构,上述方法针对单台 redis 进行 10 次连接,一般情况下不会出问题,但是极端情况下还是会导致服务异常,所以可以在代码中增加另一台 host 的连接,从而保证 slave or master 单点故障的情况下服务仍高可用:

def getRedisClient(host: String, port: Int, timeout: Int = 0, other_host: String = ""): Jedis = {
  var redisClient: Jedis = null
  var attempt = 20
  var ping = ""
  while (attempt > 0 && ping != "PONG") {
    try {
      if (!timeout.equals(0) && attempt >= 10) {
        redisClient = new Jedis(host, port, timeout)
      } else if (attempt >= 10) {
        redisClient = new Jedis(host, port)
      } else if (!timeout.equals(0)) {
        redisClient = new Jedis(other_host, port, timeout)
      } else {
        redisClient = new Jedis(other_host, port)
      }
      ping = redisClient.ping()
    } catch {
      case e: Throwable => {
        e.printStackTrace()
        if (attempt >= 10) {
          println(s"redis连接异常 $host 重试次数: $attempt")
        } else {
          println(s"redis连接异常 $other_host 重试次数: $attempt")
        }
        Thread.sleep(1000)
      }
    } finally {
      attempt -= 1
    }
  }
  if (attempt >= 10) {
    println(s"初始化成功 ${host} ping: $ping")
  } else {
    println(s"初始化成功 ${other_host} ping: $ping")
  }
  redisClient

image.gif

修改后的程序相比最一开始鲁棒性更好,服务也更加高可用,非常的奈斯 (^-^)V

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
21天前
|
存储 Java 关系型数据库
高效连接之道:Java连接池原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。频繁创建和关闭连接会消耗大量资源,导致性能瓶颈。为此,Java连接池技术通过复用连接,实现高效、稳定的数据库连接管理。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接池的基本操作、配置和使用方法,以及在电商应用中的具体应用示例。
40 5
|
11天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
33 6
|
10天前
|
存储 消息中间件 NoSQL
使用Java操作Redis数据类型的详解指南
通过使用Jedis库,可以在Java中方便地操作Redis的各种数据类型。本文详细介绍了字符串、哈希、列表、集合和有序集合的基本操作及其对应的Java实现。这些示例展示了如何使用Java与Redis进行交互,为开发高效的Redis客户端应用程序提供了基础。希望本文的指南能帮助您更好地理解和使用Redis,提升应用程序的性能和可靠性。
26 1
|
19天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
32 3
|
19天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
19 1
|
19天前
|
Java 数据库连接 数据库
Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销
本文深入探讨了Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销,显著提升了系统的响应速度和吞吐量。文章介绍了连接池的工作原理,并以HikariCP为例,展示了如何在Java应用中使用连接池。通过合理配置和优化,连接池技术能够有效提升应用性能。
33 1
|
24天前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
|
1月前
|
SQL 存储 Java
Java中使用ClickHouseDriver连接和基本操作
通过上述步骤,你可以轻松地在Java应用中集成ClickHouse数据库,执行基本的CRUD操作。需要注意的是,实际开发中应当根据实际情况调整数据库连接配置(如URL中的主机、端口、数据库名等),并根据应用需求选择合适的异常处理策略,确保代码的健壮性和资源的有效管理。此外,对于复杂查询和大批量数据处理,建议充分利用ClickHouse的特性(如分布式处理、列式存储优化等),以进一步提升性能。
79 2
|
23天前
|
存储 NoSQL Java
Java 使用 Redis
10月更文挑战第22天
27 0
|
1月前
|
安全 Java Python
基于python-django的Java网站全站漏洞检测系统
基于python-django的Java网站全站漏洞检测系统
34 0