Scala中JSON转换异常重试实现正确性及优化方案咨询
嘿,我来帮你捋捋这个Jackson JSON序列化重试的问题哈~
一、先说说你的实现是否正确
首先假设你是用手动递归/循环的方式实现的(比如下面这种常见写法):
import com.fasterxml.jackson.databind.ObjectMapper import scala.util.{Try, Success, Failure} import com.fasterxml.jackson.core.JsonProcessingException object JsonRetryManual { private val mapper = new ObjectMapper() private val maxRetries = 3 def serializeWithRetry[T](obj: T): Try[String] = { def attempt(remainingRetries: Int): Try[String] = { Try(mapper.writeValueAsString(obj)) match { case success @ Success(_) => success case Failure(ex: JsonProcessingException) if remainingRetries > 0 => // 可选:加个短延迟,比如Thread.sleep(100) attempt(remainingRetries - 1) case failure => failure } } attempt(maxRetries) } }
这种实现基本是正确的,但有两个细节要注意:
- 别捕获所有
Throwable!只针对Jackson的JsonProcessingException重试就行,不然会把OOM、空指针这类严重错误也重试,反而出问题 - 如果Jackson的偶发问题是瞬时的,加个100ms左右的重试间隔,能降低连续失败的概率(不过如果是异步场景,别用
Thread.sleep,得用非阻塞的延迟方式)
二、更优雅的实现方案
手动写重试逻辑虽然能用,但重复造轮子容易漏细节,推荐用Scala生态里成熟的重试库,省心又灵活:
方案1:用retry库(纯函数式,无额外依赖)
首先在sbt里加依赖:
libraryDependencies += "com.github.cb372" %% "retry" % "0.15.0"
然后实现代码:
import com.fasterxml.jackson.databind.ObjectMapper import com.github.cb372.retry.RetryPolicy import scala.util.Try import com.fasterxml.jackson.core.JsonProcessingException object JsonRetryWithLib { private val mapper = new ObjectMapper() // 定义重试策略:最多3次,只重试Jackson序列化异常 private val retryPolicy = RetryPolicy.limitRetries(3) .onlyIf { case Failure(ex: JsonProcessingException) => true case _ => false } // 要是需要指数退避间隔(比如第一次等100ms,第二次200ms),改成这样: // private val retryPolicy = RetryPolicy.limitRetries(3) // .exponentialBackoff(100) // .onlyIf { case Failure(ex: JsonProcessingException) => true } def serializeWithRetry[T](obj: T): Try[String] = { com.github.cb372.retry.Retry(retryPolicy)(Try(mapper.writeValueAsString(obj))) } }
这个方案的好处是:语义清晰,策略配置超灵活,纯函数式风格也贴合Scala的习惯,不用自己写重复的递归代码。
方案2:异步场景用cats-retry(配合Cats Effect)
如果你的项目用了Cats Effect这类异步IO框架,那cats-retry更适合,支持非阻塞的异步重试:
libraryDependencies += "com.github.cb372" %% "cats-retry" % "3.1.0"
示例代码:
import cats.effect.IO import com.fasterxml.jackson.databind.ObjectMapper import cats.retry.RetryPolicy import com.fasterxml.jackson.core.JsonProcessingException object JsonRetryWithCats { private val mapper = new ObjectMapper() private val retryPolicy = RetryPolicy.limitRetries(3) .onlyIf { case ex: JsonProcessingException => true } def serializeWithRetry[T](obj: T): IO[String] = { val serializeAction = IO(mapper.writeValueAsString(obj)) .recoverWith { case ex: JsonProcessingException => IO.raiseError(ex) } retryPolicy.retry(serializeAction) } }
三、Scala标准库有预定义的重试逻辑吗?
很遗憾,Scala标准库(比如scala.util、scala.concurrent这些包)没有内置的重试工具类,所以要么自己手动实现,要么用上面说的第三方库。
当然,如果是基于Future的异步场景,你也可以自己写递归的重试逻辑,但同样需要自己处理异常过滤、间隔这些细节,不如第三方库来得省心。
内容的提问来源于stack exchange,提问作者user3407267
相关产品推荐
相关产品推荐

