Как бороться со смертью RedisMessageListenerContainer - PullRequest
0 голосов
/ 11 апреля 2019

Я сталкивался со случаем, когда redis pubsub RedisMessageListenerContainer в моем приложении для весенней загрузки умерло с

ERROR .RedisMessageListenerContainer: SubscriptionTask aborted with exception:
 org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is com.lambdaworks.redis.RedisCommandTimeoutException: Command timed out
  at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:66)
  at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
  at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:37)
  at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:37)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:330)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.subscribe(LettuceConnection.java:3179)
  at org.springframework.data.redis.listener.RedisMessageListenerContainer$SubscriptionTask.eventuallyPerformSubscription(RedisMessageListenerContainer.java:790)
  at org.springframework.data.redis.listener.RedisMessageListenerContainer$SubscriptionTask.run(RedisMessageListenerContainer.java:746)
  at java.lang.Thread.run(Thread.java:748)
 Caused by: com.lambdaworks.redis.RedisCommandTimeoutException: Command timed out
  at com.lambdaworks.redis.LettuceFutures.await(LettuceFutures.java:113)
  at com.lambdaworks.redis.LettuceFutures.awaitOrCancel(LettuceFutures.java:92)
  at com.lambdaworks.redis.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:63)
  at com.lambdaworks.redis.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
  at com.sun.proxy.$Proxy156.subscribe(Unknown Source)
  at org.springframework.data.redis.connection.lettuce.LettuceSubscription.doSubscribe(LettuceSubscription.java:63)
  at org.springframework.data.redis.connection.util.AbstractSubscription.subscribe(AbstractSubscription.java:142)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.subscribe(LettuceConnection.java:3176)
  ... 3 common frames omitted
..

Я думаю, что это не должно быть неисправимой ошибкой, потому что это временная ошибка.проблема соединения (и TransientDataAccessException), но приложение, очевидно, должно иметь дело с исключениями в этих случаях.

В настоящее время это оставляет приложение в состоянии, которое является неприемлемым.Он просто регистрирует ошибку, но мне нужно либо убить приложение, чтобы его заменили, либо лучше попытаться перезапустить этот контейнер и в идеале сообщить через /health, что на приложение воздействуют, пока не все хорошо.

Есть ли что-то, что я пропускаю, менее неловко, чем пытаться start() контейнер каждые x секунд или создавать его подкласс, перезаписывать handleSubscriptionException() и пытаться действовать оттуда?Последний нуждается в гораздо более глубокой интеграции с внутренними компонентами, чем я хотел бы иметь в своем коде, но это то, с чем я до сих пор шел:

    RedisMessageListenerContainer container = new RedisMessageListenerContainer() {
        @Override
        protected void handleSubscriptionException(Throwable ex) {
            super.handleSubscriptionException(ex); // don't know what actually happened in here and no way to find out :/
            if (ex instanceof RedisConnectionFailureException) {
                // handled by super hopefully, don't care
            } else if (ex instanceof InterruptedException){
                // can ignore those I guess
            } else if (ex instanceof TransientDataAccessException || ex instanceof RecoverableDataAccessException) {
                // try to restart in those cases?
                if (isRunning()) {
                    logger.error("Connection failure occurred. Restarting subscription task manually due to " + ex, ex);
                    sleepBeforeRecoveryAttempt();
                    start(); // best we can do
                }
            } else {
                // otherwise shutdown and hope for the best next time
                if (isRunning()) {
                    logger.warn("Shutting down application due to unknown exception " + ex, ex);
                    context.close();
                }
            }
        }
    };
...