如果出现问题并且我想重试,使用同一个 Executor 重新排队 Runnable 是不是安全或可取?

Posted

技术标签:

【中文标题】如果出现问题并且我想重试,使用同一个 Executor 重新排队 Runnable 是不是安全或可取?【英文标题】:Is it safe or advisable to re-enqueue a Runnable with the same Executor if a problem occurs and I want to retry?如果出现问题并且我想重试,使用同一个 Executor 重新排队 Runnable 是否安全或可取? 【发布时间】:2011-06-29 09:55:42 【问题描述】:

我刚刚在我的runnable的run()方法中写了这段代码:

try 
    dbConnection = MyApp.datasource.getConnection();
 catch (SQLException e) 
    logger.log(Level.SEVERE, "Could not obtain a DB connection! Re-enqueuing this task. Message: " + e.getMessage(), e);
    MyApp.executor.execute(this);
    return;

如您所见,如果任务无法获得数据库连接,它应该重新排队,进入它运行前所在的同一队列。

我认为这可能是安全的,但感觉很有趣,我只是想确保没有任何我遗漏的问题。

谢谢!

【问题讨论】:

也许你想在重新排队之前添加一些Thread.sleep()。否则,如果 getConnection() 快速失败,您将产生大量 CPU 负载(并且可能由于日志记录导致 I/O 负载)。 【参考方案1】:

就执行者而言,这很好。

但请记住,失败可能会很快发生,然后执行器可能会快速重新运行您的代码。这可能会导致消耗大量 CPU 而没有任何结果。

内置强制重试延迟和最大循环计数。

【讨论】:

【参考方案2】:

    有发生所谓的中毒消息的风险:如果SQLException 无法逃脱,任务将无限重复。您必须提供某种计数器或计时器。

    根据执行程序的占用情况(已经调度了多少并发任务),重试之间的间隔可能会有很大差异。您可能会使用 100% 的 CPU,或者等待重试很长时间。

    如果偶然地,您的 任务(重新调度自身的任务)等待 (重新调度)调用的结果,当执行器仅在一个线程上运行时,您可能会遇到死锁。

    您正在使用 MyApp 的原始字段,这似乎是一个糟糕的模式。

至于总体思路:为什么不在run() 中设置一个循环?您想对正在执行的其他任务更加“公平”吗?

【讨论】:

以上是关于如果出现问题并且我想重试,使用同一个 Executor 重新排队 Runnable 是不是安全或可取?的主要内容,如果未能解决你的问题,请参考以下文章

Restkit:重试失败的请求

改造:如何从 okhttp 拦截器再次重试请求?

如何在 graphql_flutter 中重试对 GraphQLError 的请求

运行 1000 个请求,这样一次只运行 10 个

使用指数回退重试 Celery 任务

如果映射器在中途失败并且 Hadoop 重试该映射器,自定义计数器会发生啥