Здравствуйте, я криворукий.
Несколько дней назад, просматривая код, я обнаружил, что в проекте есть кусок логики, написанный очень плохо, на первый взгляд он выглядел крайне некрасиво.
Я не знаю, почему такой код существует в проекте, поэтому я заглянул в запись о представлении и попросил соответствующего коллегу спросить, почему такой код был написан.
Потом...
Этот фрагмент кода был отправлен мной в 2019 году.
Я тщательно все обдумал.В то время казалось, что поскольку я не был знаком с проектом, и в других проектах была подобная функция, я сразу придумал резюме Дафа, и я не стал внимательно рассматривать его. логика внутри.
Ну, это оказались исторические причины, понятные-понятные.
Код в основном состоит из множества логики повторных попыток, всех видов жесткого кодирования и всевозможных патчей с горячими глазами.
В частности, логика для повторной попытки есть везде. Поэтому я решил оптимизировать волну с компонентом повторных попыток.
Сегодня я покажу вам, как свернуть компонент Spring-retry.
уродливый код
Кратко поговорим о том, как выглядит уродливый код.
Чтобы дать вам сценарий, предполагая, что вы отвечаете за платежные услуги, вам нужно подключиться к внешнему каналу и вызвать их интерфейс запроса заказа.
Они сказали вам: из-за проблем с сетью, если время взаимодействия между нами истечет, и вы не получите никакого ответа от меня, то, согласно соглашению, вы можете повторить этот интерфейс три раза.После трех раз ответа все еще нет, тогда должно быть Если есть проблема, вы можете справиться с ней в соответствии с процессом исключения.
Предполагая, что вы не знаете компонент Spring-retry, вы, вероятно, напишете такой код:
Логика очень проста, она заключается в создании цикла for, а затем при возникновении исключения инициируется повторная попытка, и проверяется количество повторных попыток.
Затем создайте интерфейс для его вызова:
После инициации вызова вывод журнала выглядит следующим образом, что очень ясно с первого взгляда:
Вызовите его один раз в обычном режиме, повторите попытку три раза, и всего вы можете вызвать его 4 раза. Выдает исключение при пятом вызове.
Он полностью соответствует требованиям, и самопроверка завершена.Вы можете напрямую отправить код и передать его тестируемым.
Это прекрасно, но задумывались ли вы когда-нибудь, что такой код на самом деле очень неэлегантен.
Вы думаете, есть ли несколько подобных требований «повторить попытку после тайм-аута».
Тогда ваш цикл for не перемещается. Что-то вроде этого, уродливого, как ад:
Честно говоря, я уже писал такой уродливый код.
Но я теперь человек с чистотой кода, а такой код однозначно невыносим.
Retry должен быть общим методом наподобие класса инструмента, который можно извлечь и отделить от бизнес-кода, при разработке нужно только обращать внимание на бабу, подходящую для бизнес-кода.
Так как выйти?
Вы сказали, что это совпадение, я делюсь этим с вами сегодня, и очень хорошо извлечь функцию повтора:
После использования spring-retry наш приведенный выше код становится таким:
Просто добавьте аннотацию @Retryable, и это до смешного просто.
На первый взгляд очень элегантно!
Итак, я решил предложить всем взглянуть на эту аннотацию. Посмотрите, как другие абстрагировали функцию «повторить» в компонент, что более интересно, чем написание бизнес-кода.
Я не буду учить вас, как использовать spring-retry в этой статье, его функции очень богаты, и статей о том, как его использовать, уже много. Я хочу написать, как я это узнаю из исходного кода после того, как буду его использовать.
Как изменить его с вещи, которую можно использовать только на предложение в резюме: я прочитал соответствующий исходный код.
Но если вы вообще не умеете им пользоваться, что делать, если вы не слышали об этом компоненте?
Это не имеет значения, первым шагом для меня, чтобы понять техническую сторону, должно быть сначала создание очень простой демонстрации.
Те, кто не запустил демоверсию, будут считаться неосведомленными.
Сначала возьмите демоверсию
Сначала я ничего не знал об этой аннотации.
Итак, в данном случае, давайте не будем нести чушь, и это самый лучший способ создать демоверсию и сначала запустить ее.
Но вы помните, что создание демки — это тоже навык: просто зайдите на официальный сайт или github, чтобы найти ее, там самые авторитетные и лаконичные демки.
Например, Quick Start на github spring-retry очень лаконичен и прост для понимания.
В нем приведены примеры аннотированной разработки и программной разработки соответственно.
В основном мы рассматриваем случай разработки аннотаций здесь:
Здесь задействованы три аннотации:
- @EnableRetry: добавлен в класс запуска для поддержки функции повтора.
- @Retryable: при добавлении к методу он расширяет возможности метода и делает его полезным для повторной попытки.
- @Recover: если повторная попытка по-прежнему не удалась, будет выполнен метод, измененный этой аннотацией.
Прочитав Quick Start на git, я быстро создал демо.
Если вы раньше не знали, как использовать этот компонент, я настоятельно рекомендую вам создать его тоже, это очень просто.
Во-первых, ввести зависимости maven:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.1</version>
</dependency>
Поскольку этот компонент для вас зависит от АОП, вам также необходимо ввести эту зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.1</version>
</dependency>
Затем есть код, и этого достаточно:
Наконец, запустите проект и вызовите штрих, он действительно эффективен, и метод, модифицированный @Recover, выполняется:
Но в логе всего одна строчка, а повторной попытки нет, как-то слишком просто, да?
Раньше я думал, что это не имеет значения, мне не терпелось броситься в исходный код и просмотреть его, глядя налево и направо.
Как мне перейти к исходному коду, чтобы сделать это?
Просто посмотрите прямо на то место, где вызывается аннотация, вот так:
Мест для вызова не так много, и действительно легко найти следующий класс ключей:
org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor
Затем поставьте точку останова в соответствующей позиции, запустите программу и выполните отладку:
Но я не буду так париться сейчас.Как старый программист,я сейчас сильно повзрослел.Не буду спешить сначала исходники накатывать,а поковыряюсь сначала из лога.
Моей первой реакцией на эту проблему сейчас является настройка уровня журнала для отладки:
logging.level.root=debug
После изменения уровня журнала и перезапуска и повторного вызова вы можете увидеть много ценных журналов:
Судя по логам, это место можно найти напрямую:
org.springframework.retry.support.RetryTemplate#doExecute
Ставить здесь точку останова для отладки — самое подходящее место.
Это тоже отладочный трюк.Раньше я часто игнорировал вывод в лог, и чувствовал, что его плохо видно.На самом деле, внимательно проанализировав лог, вы обнаружите, что в нем много ценного, что гораздо эффективнее чем копаться в исходном коде.
Если не верите, то можете попробовать глянуть лог отладки, связанный с транзакцией Spring.Я думаю, это очень хороший случай, а распечатанный называется четким.
Из журнала вы можете продвигать процесс отладки на разных уровнях изоляции и поддерживать четкую связь без ощущения беспорядка.
Ладно, не будем заходить слишком далеко.
Давайте еще раз взглянем на этот журнал, не кажется ли вам знакомым этот вывод?
Разве это не похоже на картину, которую мы только что видели раньше?
Тут я увидел улыбку уголком рта: Sample, я слепо предполагаю, что вы, должно быть, написали цикл for в своем исходном коде. Если в цикле возникает исключение, проверьте, выполняются ли условия повтора, и если да, продолжайте повторять попытку. Если оно не выполняется, выполняется логика @Recover.
Если я ошибаюсь, я просто ем экран компьютера.
Хорошо, сначала стоит здесь флаг, затем перейдем к исходному коду.
Подождите, остановитесь на некоторое время.
Если мы нашли расположение первой точки останова в Debug, то есть очень критическая операция, прежде чем мы фактически войдем в отладку исходного кода Это то, что я неоднократно подчеркивал ранее, мы должны перейти к исходному коду с более конкретными вопросами. .
И флаг, который я поставил перед собой, на самом деле моя проблема: я сначала даю догадку, а потом узнаю, так ли это реализовано, и как это реализовано в коде.
Итак, чтобы снова разобраться в моем вопросе:
- 1. Найдите, где находится цикл for.
- 2. Как он принимает решение о повторной попытке?
- 3. Как он выполняет логику @Recover?
Теперь пришло время начать движение.
Перевернуть исходный код
Никаких секретов под исходным кодом нет.
Во-первых, давайте взглянем на запись Debug, которую мы нашли ранее:
org.springframework.retry.support.RetryTemplate#doExecute
Из лога интуитивно видно, что этот метод должен содержать искомый цикл for.
но...
К сожалению, это не цикл for, а цикл while. Это не большая проблема, смысл почти тот же:
Ударьте точку останова, а затем запустите проект.Когда я достигаю точки останова, меня больше всего беспокоит следующий стек вызовов:
Он состоит из двух частей: одна — содержимое пакета spring-aop, а другая — spring-retry.
Затем мы видим первый метод, связанный с spring-retry:
Поздравляю, если мы нашли расположение первой точки останова по логу, то через стек вызовов первой точки останова мы нашли начальную запись всей повторной попытки, а ниже на входе этого метода должна попасть еще одна точка останова:
org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor#invoke
Честно говоря, используется простейшая комбинация наблюдения за логами и стеками вызовов, и отладка большей части исходного кода не будет ощущаться особенно хаотичной.
Найдя вход, посмотрим на исходный код из интерфейса.
Как только вызывается метод, он сначала пытается получить из кеша, был ли метод успешно разрешен ранее, а если кеша нет, то решает, имеет ли текущий вызываемый метод аннотацию @Retryable.
Если он украшен @Retryable, возвращаемый объект делегата не будет нулевым. Таким образом, он перейдет к логике кода пакета повторной попытки.
Затем есть небольшая деталь в вызове здесь, если объект восстановления не пустой, выполнить с обратным вызовом. Если пусто, метод объекта recoveryCallback не выполняется.
Когда я увидел эти строки кода, я сделал дикую догадку: аннотация @Recover не требуется.
Поэтому я взволнованно аннотировал этот метод и снова запустил проект, и обнаружил, что это правда, немного по-другому:
После того, как я не читал другие статьи или официальное введение, я обнаружил одно из его применений только на простом примере, это неожиданный выигрыш, а также немного весело читать исходный код.
На самом деле исходный код не так уж и страшен.
Но когда я увидел это, последовала другая проблема:
Этот объект-восстановитель, по-видимому, является написанным мной методом channelNotResp, но когда он разрешается?
Нажмите на нет стола, об этом мы поговорим позже, самое главное найти место, чтобы попробовать еще раз.
Несколько шагов вниз по текущему методу, и вы скоро доберетесь до цикла while, о котором я упоминал ранее:
В основном сосредоточьтесь на этом методе canRetry:
org.springframework.retry.RetryPolicy#canRetry
После нажатия я обнаружил, что это интерфейс с несколькими реализациями:
Кратко объясните, что означают некоторые из них:
- AlwaysRetryPolicy: разрешить бесконечные повторные попытки до тех пор, пока они не будут успешными, неправильная логика в этом методе приведет к бесконечному циклу.
- NeverRetryPolicy: разрешен только один вызов RetryCallback, повторные попытки запрещены.
- SimpleRetryPolicy: политика фиксированного количества повторных попыток, максимальное количество повторных попыток по умолчанию равно 3, политика, используемая RetryTemplate по умолчанию.
- TimeoutRetryPolicy: политика повторных попыток тайм-аута, тайм-аут по умолчанию составляет 1 секунду, и повторная попытка разрешена в течение указанного периода тайм-аута.
- ExceptionClassifierRetryPolicy: Установите политику повтора для разных исключений, аналогичную комбинированной политике повтора, разница в том, что здесь различаются только повторы разных исключений.
- CircuitBreakerRetryPolicy: политика повторных попыток с функцией прерывателя цепи.Необходимо установить три параметра: openTimeout, resetTimeout и делегат.
- CompositeRetryPolicy: комбинированная политика повторных попыток, существует две комбинации: оптимистичная комбинированная политика повторных попыток означает, что пока одна политика разрешает это, вы можете повторить попытку, а пессимистическая комбинированная политика повторных попыток означает, что пока одна политика не разрешена, вы не можете повторить попытку, но Независимо от комбинации, каждая стратегия в комбинации будет выполняться
Итак, снова возникает проблема: когда мы отлаживаем исходный код, существует так много реализаций, как мне узнать, какой метод вводить?
Помните, что методы интерфейса также могут иметь точки останова. Вы не знаете, какая реализация будет использоваться, но идея знает:
Здесь используется стратегия SimpleRetryPolicy, то есть эта стратегия является стратегией повторных попыток по умолчанию для Spring-retry.
t == null || retryForException(t)) && context.getRetryCount() < this.maxAttempts
Логика этой стратегии также очень проста:
- 1. Если есть исключение, выполните метод retryForException, чтобы определить, можно ли повторить исключение.
- 2. Определите, превышает ли текущее количество попыток максимальное количество раз.
Здесь мы находим место для управления логикой повторных попыток.
Второй пункт выше легко понять. Первый пункт показывает, что эта аннотация, как и аннотация транзакции @Transaction, может обрабатывать указанные исключения. Вы можете взглянуть на поддерживаемые ею параметры:
Обратите внимание, что я пометил предложение в include, что означает, что это значение по умолчанию пусто. И когда exclude также пуст, по умолчанию используются все исключения.
Так что, хотя в демо ничего нет, генерация исключения TimeoutException также вызовет логику повторной попытки.
Это еще одна точка знаний, обнаруженная путем пролистывания исходного кода Это похоже на изучение пасхального яйца, и это удобно.
После прочтения логики оценки того, можно ли сделать повторный вызов, давайте взглянем на место, где фактически выполняется бизнес-метод:
org.springframework.retry.RetryCallback#doWithRetry
С первого взгляда видно, что имеется динамический прокси-механизм, с которым вы должны быть хорошо знакомы.Здесь вызывается наш метод callChannel:
Из кода мы знаем, что исключение, выброшенное методом callChannel, будет перехвачено в методе doWithRetry, а затем выброшено напрямую:
На самом деле это очень легко понять, потому что для запуска следующей повторной попытки необходимо создать исключение.
Но это также обнажает недостаток Spring-retry, то есть связанные службы должны запускаться путем создания исключений.
Похоже, в этом нет ничего плохого, но если подумать, предположим, что сторона канала говорит, что если я верну вам ErrorCode 500, то вы также можете попробовать еще раз.
Таких бизнес-сценариев должно быть много.
Что, если бы вы использовали Spring-retry?
Вам нужно написать такой код:
if(errorCode==500){
throw new Exception("手动抛出异常");
}
Это означает, что логика повтора запускается путем создания исключения, что не является особенно элегантным решением.
На самом деле нетрудно расширить эту структуру, чтобы определить, следует ли повторять попытку в соответствии со свойством в возвращаемом объекте.
Вы думаете, его можно было вернуть сюда. Просто предоставьте запись конфигурации, давайте скажем, какой объект должен повторить попытку, когда какое поле имеет определенное значение.
Конечно, у начальника должны быть свои идеи, я тут просто какое-то незрелое мнение. На самом деле другой фреймворк повторных попыток, Guava-Retry, поддерживает повторные попытки на основе возвращаемого значения.
Если это не является предметом этой статьи, она не будет расширена.
Затем перейдите к той части цикла while, которая перехватывает исключение.
Логика внутри не сложная, но можно обратить внимание на обрамленную ниже часть:
Здесь судят еще раз, можно ли повторить еще раз, для чего это нужно?
заключается в выполнении этой строки кода:
backOffPolicy.backOff(backOffContext);
Что оно делает?
Я не знаю, отладчик смотрит и, наконец, приходит к этому месту:
org.springframework.retry.backoff.ThreadWaitSleeper#sleep
Здесь выполняется операция перехода в спящий режим на 1000 мс.
Я так понимаю сразу, эта штука оставляет тебе тут хэндл, можешь выставить хэндл на интервал повтора. Затем по умолчанию для вас включена функция повторной попытки через 1000 мс.
Затем я нашел это внутри аннотации @Retryable:
Я не понимаю, как эта штука устроена на первый взгляд, но комментарии к ней говорят мне посмотреть на штуку Backoff.
Это выглядит так:
Эта штука вроде проще для понимания, давайте проигнорируем другие параметры, по крайней мере я видел, что по умолчанию значение value равно 1000.
Я подозревал, что заданный интервал повтора контролируется этим параметром, поэтому я попробовал:
Конечно же, это ты, малыш, и позволь мне выкопать еще одно пасхальное яйцо.
В @Backoff помимо параметра value есть много других параметров, и их значения таковы:
- delay: время ожидания между попытками (в миллисекундах)
- maxDelay: максимальное время ожидания между повторными попытками (в миллисекундах).
- multiplier: указывает кратность задержки
- delayExpression: выражение для времени ожидания между повторными попытками
- maxDelayExpression: выражение максимального времени ожидания между повторными попытками
- multiplierExpression: указывает множественное выражение для задержки
- random: случайным образом указать время задержки
Я не буду показывать вам одну за другой, если вам интересно поиграть в нее самостоятельно.
Из-за богатой стратегии конфигурации времени повтора разные реализации также написаны в соответствии с разными стратегиями:
Через Debug я знаю, что реализация по умолчанию — FixedBackOffPolicy.
Другие реализации подробно изучать не буду, в основном ориентируюсь на основную ссылку, сначала прохожу весь процесс, а потом смотрю на эти ветки, когда играю сам.
В демонстрационном сценарии после ожидания в течение одной секунды, чтобы инициировать повторную попытку, цикл while будет повторяться снова, и таким образом будет отсортирована основная ссылка повторной попытки.
На самом деле я свернул код, вы видите, что это просто блок кода try-catch внутри цикла while:
Это тот же скелет, что и уродливый код, который мы написали ранее, за исключением того, что Spring-retry расширяет и скрывает эту часть кода и предоставляет вам только аннотацию.
Когда вы получите только эту аннотацию, вы воскликнете, когда будете использовать ее как черный ящик: «Это действительно потрясающе».
Но теперь, когда вы пролистаете исходный код, вы скажете: Это все? Тем не менее, я думаю, что это может быть написано.
Первые два вопроса, поднятые здесь, ясны:
Проблема первая: найти, где находится цикл for для него.
Цикла for нет, но есть цикл while с try-catch.
Вопрос 2: Как он решает, что должен повторить попытку?
Логика принятия решения о запуске механизма повторных попыток очень проста, то есть он запускается путем создания исключения.
Но следует ли выполнять повторную попытку — это ключевой момент, который необходимо тщательно проанализировать.
Spring-retry имеет много политик повторных попыток, по умолчанию используется SimpleRetryPolicy, а количество повторных попыток равно 3.
Но следует отметить, что это «3 раза» — это общее количество вызовов за три раза. Вместо того, чтобы вызывать его три раза после неудачного первого вызова, это дает в общей сложности 4 раза. Что касается вопроса о том, сколько раз он вызывается, то все же необходимо четко забить.
И это не обязательно, что исключение будет повторяться, потому что Spring-retry поддерживает обработку или не обработку указанных исключений.
Настраиваемый, что является основной возможностью, которой должен обладать компонент.
Остался еще последний вопрос: как он выполняет логику @Recover?
Затем перейдите к исходному коду.
Восстановить логику
Первое, на что следует обратить внимание, это то, что аннотация @Recover не является обязательной, мы также анализировали ее ранее, поэтому я не буду повторяться.
Но эту функцию действительно удобно использовать, и большинство исключений должны иметь соответствующие восходящие меры.
Это вещь, чтобы выполнить практические действия.
Его исходный код также очень легко найти сразу после логики повторной попытки:
Отладьте несколько шагов вниз, и вы придете к этому месту:
org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler#recover
Еще один вызов отражения, метод здесь уже является методом channelNotResp.
Итак, вопрос: как Spring-retry узнает, что мой метод повторной попытки — это channelNotResp?
Присмотревшись к объекту метода на скриншоте выше, нетрудно заметить, что он генерируется первой строкой кода в методе:
Method method = findClosestMatch(args, cause.getClass());
Этот метод называется Найти ближайший метод с точки зрения имени и возвращаемого значения. Но я точно не знаю, что это значит.
Следите, чтобы увидеть, что он делает:
В этом есть две ключевые части информации, одна из которых называется recoveryMethodName, когда значение пусто и не пусто, берутся две разные ветки.
Другой параметр — это методы, которые представляют собой HashMap:
Внутри этой карты находится наш итоговый метод channelNotResp:
И эту Карту нужно пройти, в какую бы ветку она ни пошла.
Когда был вставлен канал NotResp в этой карте?
Это очень просто, просто посмотрите, где вызывается метод put этой карты, и все готово:
Что касается этих двух мест размещения, исходный код находится в следующем методе:
org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler#init
Как видно из скриншота, вот способ узнать, изменен ли класс аннотацией @Recover.
Ставлю точку останова на 172 строку, отлаживаю и смотрю конкретную информацию, вы будете знать, что тут происходит.
После того, как вы инициируете вызов, программа остановится в точке останова, а как сюда попасть, я уже говорил, посмотрите на стек вызовов, повторяться не буду.
Что касается этого метода doWith, мы делаем шаг вверх по стеку вызовов и знаем, что анализируем все методы в нашем классе RetryService:
При синтаксическом анализе метода channelNotResp он распознает, что метод помечен аннотацией @Recover.
Однако с точки зрения исходного кода для дальнейшего анализа должно выполняться условие if. В дополнение к Recover условие if также должно удовлетворять этому:
method.getReturnType().isAssignableFrom(failingMethod.getReturnType())
Метод isAssignableFrom предназначен для определения того, является ли он родительским классом для класса.
То есть метод и failingMethod следующие:
Это проверка того, соответствует ли возвращаемое значение метода, аннотированного с помощью @Retryable, методу, аннотированному с помощью @Recover Только если возвращаемое значение совпадает, это означает, что это пара и должна быть проанализирована.
Например, я изменил исходный код на это:
Когда он анализирует метод channelNotRespStr, он обнаружит, что хотя он и изменен аннотацией @Recover, возвращаемое значение не является согласованным, поэтому известно, что это не итоговый метод целевого метода callChannel.
Это просто обычные процедуры в исходном коде.
Добавьте метод callChannelSrt, и в приведенном выше исходном коде Spring-retry может помочь вам разобрать, кто с кем является парой:
Затем посмотрите, соблюдены ли условия, что происходит в этом случае?
Это входной параметр метода приобретения, но если присмотреться, то только для получения первого параметра, и этот параметр должен удовлетворять условию:
Throwable.class.isAssignableFrom(parameterTypes[0])
Должен быть подклассом Throwable, что означает, что он должен быть исключением. Используйте поле типа, чтобы принять его, а затем сохраните его ниже.
Когда первый раз смотрел, то точно не понял, что он делает. Это не имело значения. Несколько раз перечитал и понял. Поделюсь с вами. Вот за метод, который появился на начало этого раздела:
Тип получается здесь, и оценивается, что если тип имеет значение null, по умолчанию он равен Throwable.class.
Если есть значение, оценивается, является ли тип здесь тем же классом или родительским классом, что и причина, выброшенная текущей программой.
Чтобы еще раз подчеркнуть, из названия и возвращаемого значения этого метода мы знаем, что ищем наиболее похожий метод, я сказал ранее, что не совсем понял, что это значит, просто чтобы проложить путь для множества методов Что это за Карта?
На самом деле мое сердце подобно зеркалу, и я давно хотел сорвать с него пелену.
Ну, следи за ходом моих мыслей, и ты сразу увидишь, какое вино продается в тыкве.
Думаете, findClosestMatch, это Closest — высший уровень Close, а значит, ближайший.
Так как есть самое близкое, должно быть сложено несколько вещей, и только одна из них самая подходящая.
В исходном коде этим требованием является «причина», которая в настоящее время является исключением.
И «несколько вещей» относятся к атрибуту типа в вещах, установленных этими методами.
Еще немного закружилась голова, правильно, не паникуйте, как только выйдет следующая картинка, у вас не сразу закружится голова:
Возьмите этот код и используйте «Ближайшую» вещь.
Во-первых, причиной является выброшенное исключение TimeoutException.
Карта методов содержит три метода, модифицированные аннотацией @Recover.
Почему их три?
Хороший вопрос, значит, то, что я написал при себе, очень плохо, значит, вы плохо это понимаете. Все в порядке, я покажу вам код для части помещения вещей в методы:
Все эти три метода удовлетворяют условию аннотации @Recover, а также удовлетворяют условию, согласно которому возвращаемое значение соответствует возвращаемому значению целевого метода callChannel. Затем вы должны поместить их в методы, так что их три.
Это также объясняет, почему метод нижней строки загружается с помощью Map?
Сначала я подумал, что это стратегия «сделай все», потому что всегда так обращайся с пользователем, ты не знаешь, какой волшебный код он напишет.
Например, в моем примере выше, по сути, этот метод должен сработать в конце:
@Recover
public void channelNotResp(TimeoutException timeoutException) throws Exception {
log.info("3.没有获取到渠道的返回信息,发送预警!");
}
Потому что это самое близкое.
Я сделаю скриншот для вас, чтобы показать, что я не говорю глупостей:
Однако, когда я вычитывал, я обнаружил, что это место было неправильным, это не вина пользователя, но действительно возможно, что появится модифицированный метод @Retryable, и для разных исключений были разные восходящие методы.
Например следующее:
Когда num=1, срабатывает стратегия тайм-аута, и журнал выглядит следующим образом:
Когда num>1, срабатывает стратегия нулевого указателя, и журнал выглядит следующим образом:
Замечательно, действительно удивительно.
Видя это, я думаю, что компонент Spring-retry является отправной точкой. Обладая базовым пониманием, я хорошо разбираюсь в основном процессе и могу использовать «мастер» в своем резюме.
В последующем вам нужно только коснуться больших ветвей и деталей, и вы можете изменить «мастер» на «знакомый».
немного испорчен
Наконец, добавьте еще один недостаток.
Давайте посмотрим на метод, который он обрабатывает @Recover. Здесь он просто обрабатывает возвращаемое значение метода:
Когда я увидел первый взгляд здесь, я почувствовал, что что-то не так, и было меньше суждений по одной ситуации, а именно: дженерики.
Например, я делаю это:
Само собой разумеется, что основная стратегия, на которую я надеюсь, — это метод channelNotRespInt.
Но после выполнения вы обнаружите, что есть определенный шанс выбрать метод channelNotRespStr:
Это неправильно, я явно хочу, чтобы метод channelNotRespInt докопался до сути, почему я не выбрал правильный?
Потому что общая информация ушла, старое железо:
Предположим, мы хотим поддерживать дженерики?
Из описания на гитхабе автор стал акцентировать внимание на исследовании этого метода:
Дженерики будут поддерживаться, начиная с версии 1.3.2.
Но в настоящее время самая высокая версия в репозитории maven — 1.3.1:
Хотите увидеть код?
Просто вытащите исходный код и посмотрите.
Посмотрите непосредственно на запись представления этого класса:
org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler
Видно, что условия суждения изменились, а обработка дженериков увеличилась.
Я просто указываю путь. Если вы заинтересованы в исследовании, откройте исходный код и посмотрите. О том, как этого добиться, писать не буду, это слишком длинно и никто не будет читать, оставим здесь дырку.
В основном потому, что когда я это писал, моя девушка уговаривала меня сыграть в настольный теннис. Она относится к тому типу людей, которые сильно пристрастились к овощам.Вчера ее отдали в церковь,но сегодня она реально пригрозила избить меня со счетом 11-0,чтобы посмотреть,если я не дам ей хорошую оплеуху и не убью ее не покидая ее.
Эта статья была включена в мой личный блог, который полон высококачественных оригиналов.Приглашаю всех взглянуть: