java-retry实现
有这样一个需求,当调用某个方法抛出异常,比如通过 HttpClient 调用远程接口时由于网络原因报 TimeOut 异常;或者所请求的接口返回类似于“处理中”这样的信息,需要重复去查结果时,我们希望当前方法能够在这种特定的情况下,重复执行,如果达到了我们的期望,则不重复执行。而且,我们希望能够控制重试次数,不希望无限期执行下去。
Java 中有各种定时任务的实现,如 Spring 的 Schedule,Quartz 等,稍微想一下,显然不符合我们的需求。递归倒是可以,但是有些问题,先看下递归的实现:

private int retryTimes = 3; @Test
public void upperMethod() {
method("123", "456");
}
public void method(String param1, String param2) {
System.out.println(param1 + param2); // 其他一些操作,但是没有得到预期的返回结果,或者抛出异常
boolean isException = true;
if(isException && retryTimes > 0){
retryTimes --;
method(param1, param2);
}
}

method 方法是需要重复执行的,重复执行 3 次,加上第一次执行,一共 4 次。如果异常了,则在 catch 里面递归调用 method。如果返回“处理中”等情况,则进行判断,是否需要递归调用。
这里的问题是定义了 retryTimes 这样一个全局变量,不优雅,如果需要重复执行的方法较多,而且重复次数不一样,则需定义多个全局变量。递归可以优化一下:

@Test
public void upperMethod() {
method(3, "123", "456");
} public void method(int retryTimes,String param1, String param2) {
System.out.println(param1 + param2); // 其他一些操作,但是没有得到预期的返回结果,或者抛出异常
boolean isException = true;
if(isException && retryTimes > 0){
method(--retryTimes, param1, param2);
}
}

这里去掉了全局变量,但是 method 方法多了一个和自身逻辑无关的 retryTimes 变量,还不优雅。如果参数较多,还会显得混乱。
下面做了一个还算优雅的方法:

@Test
public void mainMethod() {
subMethod("123", "456");
} public void subMethod(String param1, String param2) {
System.out.println(param1 + param2);
RetryUtil.setRetryTimes(3).retry(param1, param2);
}

增加了一个 RetryUtil 的工具类,设置重试次数,然后传入当前方法的参数,进行重复执行。这里的重点就是 RetryUtil 的实现:

public class RetryUtil {
private static ThreadLocal<Integer> retryTimesInThread = new ThreadLocal<>(); /**
* 设置当前方法重试次数
*
* @param retryTimes
* @return
*/
public static RetryUtil setRetryTimes(Integer retryTimes) {
if (retryTimesInThread.get() == null)
retryTimesInThread.set(retryTimes);
return new RetryUtil();
} /**
* 重试当前方法
* <p>按顺序传入调用者方法的所有参数</p>
*
* @param args
* @return
*/
public Object retry(Object... args) {
try {
Integer retryTimes = retryTimesInThread.get();
if (retryTimes <= 0) {
retryTimesInThread.remove();
return null;
}
retryTimesInThread.set(--retryTimes);
String upperClassName = Thread.currentThread().getStackTrace()[2].getClassName();
String upperMethodName = Thread.currentThread().getStackTrace()[2].getMethodName(); Class clazz = Class.forName(upperClassName);
Object targetObject = clazz.newInstance();
Method targetMethod = null;
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals(upperMethodName)) {
targetMethod = method;
break;
}
}
if (targetMethod == null)
return null;
targetMethod.setAccessible(true);
return targetMethod.invoke(targetObject, args);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

为了防止多线程情况下出现并发问题,这里定义了一个 ThreadLocal 变量来存储当前线程的重试次数。然后通过 setRetryTimes ,一个静态方法来设置这个重试次数,并返回一个 RetryUtil 对象。
调用者通过返回的 RetryUtil 对象调用 retry 方法实现重试。retry 方法接收一个可变参数,因为调用者实际的参数不确定,这里要求按顺序传入调用者方法的所有参数。
接下来判断 ThreadLocal 变量是否小于等于 0 ,如果是,则说明重复次数已达到,返回 null;如果不是,则让 ThreadLocal 变量减一。接下来:
String upperClassName = Thread.currentThread().getStackTrace()[2].getClassName();
String upperMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();
来获取当前方法(retry)的上层方法名和上层类名。Thread.currentThread().getStackTrace() 得到线程的方法栈数组,数组的第二个元素 Thread.currentThread().getStackTrace() [1] 为当前方法栈,第三个元素 Thread.currentThread().getStackTrace() [2] 为上层方法栈,通过上层方法的栈帧得到上层方法的方法名和类名。
下面就是通过反射获取该类的所有方法,循环判断方法名是否等于所要重复执行的方法,如果是的话,执行该方法,参数就是传入可变参数。
可能大家会说反射会耗时,但我认为对于上述这种需求的情况,重试次数也不会太多,因此性能可以接受。
java-retry实现的更多相关文章
- Java Retry implement
There are many cases in which you may wish to retry an operation a certain number of times. Examples ...
- java retry:详解
发现 今天在探秘线程池原理知识点,在阅读JDK源码时遇到程序代码中出现如下代码,因为之前没有遇到过,于是特地记录下来并谷歌了一番,后面我自己做了一些简要的验证和分析. 验证 网上溜达一番发现,这ret ...
- 更好的 java 重试框架 sisyphus 入门简介
What is Sisyphus sisyphus 综合了 spring-retry 和 gauva-retrying 的优势,使用起来也非常灵活. 为什么选择这个名字 我觉得重试做的事情和西西弗斯很 ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- java function retry wrapper
import java.util.concurrent.Callable; /** * Created by huahui.yang on 1/29/16. */ public class Retry ...
- 【java基础】Java的retry标记的使用
说明:其实retry就是一个标记,标记程序跳出循环的时候从哪里开始执行,功能类似于goto.retry一般都是跟随者for循环出现,第一个retry的下面一行就是for循环,而且第二个retry的前面 ...
- retry.RetryInvocationHandler (RetryInvocationHandler.java:invoke(140)) - Exception while invoking getFileInfo of class ClientNamenodeProtocolTranslatorPB over bdata236/192.168.1.236:9000 after 3 fail
报错信息如下 -- ::, INFO [main]: retry.RetryInvocationHandler (RetryInvocationHandler.java:invoke()) - Exc ...
- Java之Retry重试机制详解
应用中需要实现一个功能: 需要将数据上传到远程存储服务,同时在返回处理成功情况下做其他操作.这个功能不复杂,分为两个步骤:第一步调用远程的Rest服务上传数据后对返回的结果进行处理:第二步拿到第一步结 ...
- 非root用户执行java进程报错:fork: retry:资源暂时不可用
vim /etc/security/limits.conf # End of file * soft nproc 65535 * hard ...
- Java集合---ConcurrentHashMap原理分析
集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构的支持.比如两个线程需要同时访问一个中间临界区(Queue),比如常会用缓存作为外部文件的副本(HashMap).这篇文章主 ...
随机推荐
- WinServer设置多用户登录
1.运行 gpedit.msc →computer configuration→administrative templates→windows componets→Remote Desktop→Re ...
- pytest之收集用例规则与运行指定用例
前言 上篇文章相信大家已经了解了pytest在cmd下结合各种命令行参数如何运行测试用例,并输出我们想要看到的信息.那么今天会讲解一下pytest是如何收集我们写好的用例?我们又有哪些方式来运行单个用 ...
- javascript面向对象理解的补充
<html> <head> <title>js inherit demo</title> <meta http-equiv="pragm ...
- Linux vi/vim编辑器常用命令与用法总结
(一)vi/vim是什么?Linux世界几乎所有的配置文件都是以纯文本形式存在的,而在所有的Linux发行版系统上都有vi编辑器,因此利用简单的文字编辑软件就能够轻松地修改系统的各种配置了,非常方便. ...
- Identity Server 4 预备知识 -- OAuth 2.0 简介
OAuth 2.0 简介 OAuth有一些定义: OAuth 2.0是一个委托协议, 它可以让那些控制资源的人允许某个应用以代表他们来访问他们控制的资源, 注意是代表这些人, 而不是假冒或模仿这些人. ...
- 机器学习之KNN原理与代码实现
KNN原理与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/9670187.html 1. KNN原理 K ...
- SmartSql 动态代理仓储
SmartSql 动态代理仓储,一个高生产力的组件.该组件看似很难懂,实际上仅做了映射Statement,转发请求的功能.但却意义重大. SmartSql提供了一个通用泛型仓储接口 SmartSql. ...
- 浅谈SQL Server内部运行机制
对于已经很熟悉T-SQL的读者,或者对于较专业的DBA来说,逻辑的增删改查,或者较复杂的SQL语句,都是非常简单的,不存在任何挑战,不值得一提,那么,SQL的哪些方面是他们的挑战 或者软肋呢? 那就是 ...
- [TCP/IP] 数据链路层-ethereal 抓包分析数据帧
1.下载 http://dx1.pc0359.cn/soft/e/ethereal.rar 2.打开软件,指定抓取的网卡,下面是我抓取自己的主要网卡数据 3.开启个ping命令 , 不停的ping一台 ...
- 戏说程序猿之cannot find the object
“别开玩笑了,程序员哪里需要对象!” 程序员难找对象原因无非如下: 1.工作时间长,恋爱时间少 2.性格偏于内向,不主动 3.不注意个人形象 程序员爱情观: 爱情就是死循环,一旦执行就陷进去了: 爱上 ...