我有JobRequest
个对象的集合,这些对象包含在Worker
个对象中:
public class Worker implements Runnable {
private final ProcExecutor executor;
private final JobRequest request;
public Worker(ProcExecutor executor, JobRequest request) {
this.executor = executor;
this.request = request;
}
@Override
public void run() {
// this might throw an SQLException
executor.execute(request);
}
}
这些是针对具有8个核心线程和无限制任务队列的ThreadPoolExecutor
执行的。
ProcExecutor
打开OracleConnection,从OracleConnection
和JobRequest
创建CallableStatement,执行CallableStatement
,然后关闭所有内容。创建连接,创建语句和执行语句都可以抛出SQLExceptions
。
我遇到的问题是我希望Worker
重试作业(使用ExponentialBackoffPolicy中的Spring-Retry,初始时间为500毫秒,最终时间为30,000 ms和无限次重试 - 如果异常是暂时的,用户可以干预并取消重试循环)如果异常是暂时的,则记录异常并在异常是致命的情况下退出Worker
。目前我正在使用pingDatabase来测试瞬态故障:
public class DatabasePingException extends SQLException {
public DatabasePingException(SQLException cause) {
super("Ping Exception", cause);
}
}
public class ProcExecutor {
public void execute(JobRequest request) throws SQLException {
try(OracleConnection connection = // create connection) {
try {
if (oracleConnection.pingDatabase() != OracleConnection.DATABASE_OK) {
throw new SQLException("Invalid database connection");
}
} catch(SQLException e) {
throw new DatabasePingException(e);
}
// create and execute CallableStatement
}
}
}
Worker
然后在DatabasePingException
上重试,否则会记录异常并退出普通SQLException
。
(至少)两个问题:
有没有更好的方法来区分瞬态和非瞬态故障?我意识到SQLException
有transient和non-transient个错误的子类,但我不清楚我应该如何对待SQLRecoverableException这样的子类,例如在固定的重试周期内重试固定次数,或者将其视为非暂时性故障,或者甚至是否需要重试SQLWarning。
如果所有Workers
都遇到瞬态故障,那么理想情况下我想暂停其中一个,直到问题清除为止。我提出的唯一解决方案涉及使用共享Worker
之类的内容打破Semaphore
封装,或者使用ThreadPoolExecutor
重量级内容,例如调用shutDownNow()
并重新启动所有已被取消或从未执行的Workers
。如果我使用指数退避,即使有8个线程,我怀疑所有这些都是过度的,这只会是每个线程在退避达到最大值时每30秒对数据库执行一次ping操作,但是如果有一个简单的解决方案那么我应该使用它。