Java实现自定义重试工具类

 更新时间:2024年11月29日 11:29:08   作者:花开不识君  
这篇文章主要为大家详细介绍了如何基于Java实现自定义重试工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

Spring-retry、guava的Retry都提供有重试工具,但二者均存在一个确缺点,即如果重试等待过程中会一直阻塞工作线程,这对于在生产环境使用是存在风险的,如果存在大量长时间等待的重试任务将会耗尽系统线程资源,下文基于线程池来完成一个简易的重试工具类。

核心思想

将任务封装为一个task,将任务的重试放入可调度的线程池中完成执行,避免在重试间隔中,线程陷入无意义的等待,同时将重试机制抽象为重试策略。

代码实现

重试工具类

package com.huakai.springenv.retry.v2;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

@Slf4j
public class RetryUtil {

    public static ExecutorService EXECUTOR = Executors.newFixedThreadPool(1);
    private static final ScheduledExecutorService SCHEDULER_EXECUTOR = Executors.newScheduledThreadPool(20);


    /**
     * 任务重试
     * @param actualTaskFunction 执行的任务函数
     * @param resultHandler 任务结果处理器
     * @param maxRetry 最大重试次数
     * @param retryStrategy 重试策略
     */
    public static void retryTask(
            Function<Integer, String> actualTaskFunction,
            Function<String, Boolean> resultHandler,
            int maxRetry,
            RetryStrategy retryStrategy // 使用策略模式
    ) {
        Runnable runnable = new Runnable() {
            final AtomicInteger retryCount = new AtomicInteger(); // 当前重试次数
            final AtomicInteger maxRetryCount = new AtomicInteger(maxRetry); // 最大重试次数

            @Override
            public void run() {
                String taskResult = actualTaskFunction.apply(retryCount.get()); // 执行任务
                Boolean taskSuccess = resultHandler.apply(taskResult); // 处理任务结果
                if (taskSuccess) {
                    if (retryCount.get() > 1) {
                        log.info("任务重试成功,重试次数:{}", retryCount.get());
                    }
                    return; // 任务成功,不需要再重试
                }

                if (retryCount.incrementAndGet() == maxRetryCount.get()) {
                    log.warn("任务重试失败,重试次数:{}", retryCount.get());
                    return; // 达到最大重试次数,停止重试
                }

                // 获取重试间隔
                long delay = retryStrategy.getDelay(retryCount.get());
                TimeUnit timeUnit = retryStrategy.getTimeUnit(retryCount.get());

                // 安排下次重试
                SCHEDULER_EXECUTOR.schedule(this, delay, timeUnit);
                log.info("任务重试失败,等待 {} {} 后再次尝试,当前重试次数:{}", delay, timeUnit, retryCount.get());
            }
        };
        EXECUTOR.execute(runnable); // 执行任务
    }

    public static void main(String[] args) {
        // 使用指数退避重试策略
        RetryStrategy retryStrategy = new ExponentialBackoffRetryStrategy(1, TimeUnit.SECONDS);

        retryTask(
                retryCount -> "task result",
                taskResult -> Math.random() < 0.1,
                5,
                retryStrategy
        );
    }
}

重试策略

指数退避

package com.huakai.springenv.retry.v2;

import java.util.concurrent.TimeUnit;

/**
 * 指数退避重试策略
 */
public class ExponentialBackoffRetryStrategy implements RetryStrategy {
    private final long initialDelay;
    private final TimeUnit timeUnit;

    public ExponentialBackoffRetryStrategy(long initialDelay, TimeUnit timeUnit) {
        this.initialDelay = initialDelay;
        this.timeUnit = timeUnit;
    }

    @Override
    public long getDelay(int retryCount) {
        return (long) (initialDelay * Math.pow(2, retryCount - 1)); // 指数退避
    }

    @Override
    public TimeUnit getTimeUnit(int retryCount) {
        return timeUnit;
    }
}

自定义重试间隔时间

package com.huakai.springenv.retry.v2;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 自定义重试间隔时间的重试策略
 */
public class CustomerIntervalRetryStrategy implements RetryStrategy {
    // 配置重试间隔和时间单位
    List<RetryInterval> retryIntervals;


    public CustomerIntervalRetryStrategy(List<RetryInterval> retryIntervals) {
        this.retryIntervals = retryIntervals;
    }

    @Override
    public long getDelay(int retryCount) {
        return retryIntervals.get(retryCount).getDelay();
    }

    @Override
    public TimeUnit getTimeUnit(int retryCount){
        return retryIntervals.get(retryCount).getTimeUnit();
    }
}

固定间隔

package com.huakai.springenv.retry.v2;

import java.util.concurrent.TimeUnit;

/**
 * 固定间隔重试策略
 */
public class FixedIntervalRetryStrategy implements RetryStrategy {
    private final long interval;
    private final TimeUnit timeUnit;

    public FixedIntervalRetryStrategy(long interval, TimeUnit timeUnit) {
        this.interval = interval;
        this.timeUnit = timeUnit;
    }

    @Override
    public long getDelay(int retryCount) {
        return interval;
    }

    @Override
    public TimeUnit getTimeUnit(int retryCount) {
        return timeUnit;
    }
}

到此这篇关于Java实现自定义重试工具类的文章就介绍到这了,更多相关Java重试工具类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis使用Map与模糊查询的方法示例

    MyBatis使用Map与模糊查询的方法示例

    这篇文章主要给大家介绍了关于MyBatis使用Map与模糊查询的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • Java中的弗洛伊德(Floyd)算法

    Java中的弗洛伊德(Floyd)算法

    这篇文章主要介绍了Java中的弗洛伊德(Floyd)算法,Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似,需要的朋友可以参考下
    2024-01-01
  • MyBatis结果映射(ResultMap)的使用

    MyBatis结果映射(ResultMap)的使用

    在MyBatis中,结果映射是实现数据库结果集到Java对象映射的核心,它不仅支持简单的字段映射,还能处理字段名不一致、嵌套对象和集合映射等复杂场景,通过ResultMap,开发者可以灵活定义映射关系,以适应各种需求,感兴趣的可以了解一下
    2024-09-09
  • Scala中优雅的处理Null问题

    Scala中优雅的处理Null问题

    Spark 采用混合方式,大部分情况下使用 Option,但个别时候出于性能原因才使用了null。一个很好的习惯是当有方法返回值可能为null的时候,使用Option来代替,本文给大家介绍Scala处理Null的知识详解,一起看看吧
    2021-08-08
  • java中几种http请求方式示例详解

    java中几种http请求方式示例详解

    在日常工作和学习中有很多地方都需要发送HTTP请求,下面这篇文章主要给大家介绍了关于java中几种http请求方式的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • 浅谈java中字节与字符的区别

    浅谈java中字节与字符的区别

    这篇文章主要介绍了浅谈java中字节与字符的区别,字节是java中的基本数据类型,用来申明字节型的变量;字符是语义上的单位,它是有编码的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 简单谈谈Spring Ioc原理解析

    简单谈谈Spring Ioc原理解析

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解。
    2018-09-09
  • 详解Maven私服Nexus的安装与使用

    详解Maven私服Nexus的安装与使用

    这篇文章主要介绍了详解Maven私服Nexus的安装与使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • 浅析打开eclipse出现Incompatible JVM的解决方法

    浅析打开eclipse出现Incompatible JVM的解决方法

    本篇文章是对打开eclipse出现Incompatible JVM的解决方法进行了详细的分析介绍,需要的朋友参考下
    2013-07-07
  • 深入理解Java中观察者模式与委托的对比

    深入理解Java中观察者模式与委托的对比

    这篇文章主要介绍了Java中观察者模式与委托的对比,观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,委托的实现简单来讲就是用反射来实现的,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-05-05

最新评论