Как заставить поток попытаться повторно подключиться к Базе данных x раз, используя JDBCTemplate - PullRequest
4 голосов
/ 19 мая 2010

У меня есть один поток, пытающийся подключиться к базе данных, используя JDBCTemplate следующим образом:

JDBCTemplate jdbcTemplate =  new JdbcTemplate(dataSource); 

try{
    jdbcTemplate.execute(new CallableStatementCreator() {
        @Override
        public CallableStatement createCallableStatement(Connection con)
        throws SQLException {
            return con.prepareCall(query);
        }
    }, new CallableStatementCallback() {
        @Override
        public Object doInCallableStatement(CallableStatement cs)
        throws SQLException {
            cs.setString(1, subscriberID);
            cs.execute();
            return null;
        }
    });
 } catch (DataAccessException dae) {
     throw new CougarFrameworkException(
             "Problem removing subscriber from events queue: "
             + subscriberID, dae);
 }

Я хочу убедиться, что если вышеприведенный код выдает исключение DataAccessException или SQLException, поток ждет несколько секунд и пытается повторно подключиться, скажем еще 5 раз, а затем сдается. Как мне этого добиться? Кроме того, если во время выполнения база данных отключается и снова запускается, как я могу гарантировать, что моя программа восстанавливается после этого и продолжает работать, а не выдавать исключение и завершать работу?

Заранее спасибо.

Ответы [ 4 ]

2 голосов
/ 19 мая 2010

Попробуй это. Мои соображения: запустить цикл, пока операторы не будут выполнены успешно. Если есть сбой, допустите сбой 5 раз, и каждый раз он будет ждать 2 секунды для следующего выполнения.

JDBCTemplate jdbcTemplate =  new JdbcTemplate(dataSource); 
boolean successfullyExecuted = false;
int failCount = 0;
while (!successfullyExecuted){
 try{
    jdbcTemplate.execute(new CallableStatementCreator() {
        @Override
        public CallableStatement createCallableStatement(Connection con)
        throws SQLException {
            return con.prepareCall(query);
        }
    }, new CallableStatementCallback() {
        @Override
        public Object doInCallableStatement(CallableStatement cs)
        throws SQLException {
            cs.setString(1, subscriberID);
            cs.execute();
            return null;
        }
    });
    successfullyExecuted = true;
 } catch (DataAccessException dae) {
     if (failedCount < 5){
        failedCount ++;
        try{java.lang.Thread.sleep(2 * 1000L); // Wait for 2 seconds
        }catch(java.lang.Exception e){}
     }else{
     throw new CougarFrameworkException(
             "Problem removing subscriber from events queue: "
             + subscriberID, dae);
     }
 } catch (java.sql.SQLException sqle){
     if (failedCount < 5){
        failedCount ++;
     }else{
     try{java.lang.Thread.sleep(2 * 1000L); // Wait for 2 seconds
     }catch(java.lang.Exception e){}
     throw new CougarFrameworkException(
             "Problem removing subscriber from events queue: "
             + subscriberID, dae);
     }
 }
}
1 голос
/ 20 мая 2010

Возможно, вам стоит взглянуть на поддержку Spring Aspect. То, что вы описываете, это повторная попытка с (постоянным) откатом, и есть вероятность, что он вам в конечном итоге понадобится где-то еще, будь то общение с веб-службой, сервером электронной почты или любой другой сложной системой, подверженной временным сбоям.

Например, этот простой метод вызывает базовый метод до maxAttempts раз всякий раз, когда генерируется исключение, если только он не является подклассом Throwable, перечисленным в noRetryFor.

private Object doRetryWithExponentialBackoff(ProceedingJoinPoint pjp, int maxAttempts,
        Class<? extends Throwable>[] noRetryFor) throws Throwable {
    Throwable lastThrowable = null;

    for (int attempts = 0; attempts < maxAttempts; attempts++) {
        try {
            pauseExponentially(attempts, lastThrowable);
            return pjp.proceed();
        } catch (Throwable t) {
            lastThrowable = t;

            for (Class<? extends Throwable> noRetryThrowable : noRetryFor) {
                if (noRetryThrowable.isAssignableFrom(t.getClass())) {
                    throw t;
                }
            }
        }
    }

    throw lastThrowable;
}


private void pauseExponentially(int attempts, Throwable lastThrowable) {
    if (attempts == 0)
        return;

    long delay = (long) (Math.random() * (Math.pow(4, attempts) * 100L));
    log.warn("Retriable error detected, will retry in " + delay + "ms, attempts thus far: "
            + attempts, lastThrowable);

    try {
        Thread.sleep(delay);
    } catch (InterruptedException e) {
        // Nothing we need to do here
    }
}

Этот совет может быть применен к любому бобу, который вы пожелаете, с помощью поддержки Aspect Spring. Подробнее см. http://static.springsource.org/spring/docs/2.5.x/reference/aop.html.

0 голосов
/ 03 января 2011

Как насчет записи аспекта (DBRetryAspect) поверх него, он будет более прозрачным.

0 голосов
/ 19 мая 2010

как то так:

private int retries;

/**
 * Make this configurable.
 */
public void setRetries(final int retries) {
    Assert.isTrue(retries > 0);
    this.retries = retries;

}

public Object yourMethod() {

    final int tries = 0;
    Exception lastException = null;
    for (int i = 0; i < this.retries; i++) {
        try {

            return jdbcTemplate.execute ... (your code here);

        } catch (final SQLException e) {
            lastException = e;
        } catch (final DataAccessException e) {
            lastException = e;
        }
    }
    throw lastException;

}
...