1. Предпосылки
Эта статья была одновременно опубликована вПубличный аккаунт Prodesire,а такжеБлог Prodesire. Недавно в процессе выполнения проекта тестирования API облачных служб я обнаружил, что иногда API вызывается в больших количествах, что приводит к ошибке ограничения тока. При обнаружении такого рода ошибок традиционная стратегия повторных попыток заключается в повторных попытках время от времени. Однако, поскольку он повторяется в фиксированное время, одновременно при повторной попытке будет поступать большое количество запросов, что будет постоянно вызывать ограничение тока.
Это напоминает мне о поиске два года назадДокументация по задачам сельдереяКогда я обнаружил, что могу установить его для задачиretry_backoff
опыт, который позволяет выполнять задачи с指数退避
способ повторить попытку. Итак, как именно выглядит экспоненциальная отсрочка?
2. Экспоненциальный откат
Согласно викиExponential backoffОбъяснение: экспоненциальная отсрочка — это алгоритм, который экспоненциально снижает скорость процесса с помощью обратной связи, чтобы постепенно найти подходящую скорость.
В Ethernet этот алгоритм обычно используется для запланированных повторных передач после коллизий. Отложенная повторная передача определяется на основе временного интервала и количества попыток повторной передачи.
существуетc
После коллизии (например, неудачного запроса) в качестве количества слотов выбирается случайное значение от 0 до $2^c-1$.
- Для первой коллизии каждый отправитель будет ждать 0 или 1 временной интервал для отправки.
- И после 2-го столкновения отправитель будет ждать от 0 до 3 (рассчитывается как $ 2 ^ 2-1 $) временных интервалов для отправки.
- И после 3-го столкновения отправитель будет ждать от 0 до 7 (рассчитывается как $ 2 ^ 3-1 $) временных интервалов для отправки.
- И так далее...
По мере увеличения количества повторных передач степень задержки возрастает экспоненциально.
Проще говоря, временной интервал между каждой повторной попыткой в два раза превышает предыдущий.
3. Ожидаемое значение экспоненциального отката
Математическое ожидание времени отсрочки — это среднее значение всех возможностей при равномерном распределении времени отсрочки. То есть вc
После коллизий количество слотов отсрочки равно[0,1,...,N]
, где $N=2^c-1$ , то математическое ожидание времени отсрочки (в тайм-слотах) равно
?E(c)=frac{1}{N+1}sum_{i=0}^{N}{i}=frac{1}{N+1}frac{N(N+1)}{2}=frac{N}{2}=frac{2^c-1}{2}?
Итак, для предыдущего примера:
- После первого столкновения ожидаемое время задержки составляет $E(1)=frac{2^1-1}{2}=0,5$.
- После второго столкновения ожидаемое время задержки равно $E(2)=frac{2^2-1}{2}=1,5$.
- После третьего столкновения ожидаемое время задержки равно $E(3)=frac{2^3-1}{2}=3,5$.
В-четвертых, применение экспоненциальной отсрочки
4.1 Экспоненциальный алгоритм отсрочки в Celery
посмотриcelery/utils/time.pyФункция для получения экспоненциального времени отсрочки:
def get_exponential_backoff_interval(
factor,
retries,
maximum,
full_jitter=False
):
"""Calculate the exponential backoff wait time."""
# Will be zero if factor equals 0
countdown = factor * (2 ** retries)
# Full jitter according to
# https://www.awsarchitectureblog.com/2015/03/backoff.html
if full_jitter:
countdown = random.randrange(countdown + 1)
# Adjust according to maximum wait time and account for negative values.
return max(0, min(maximum, countdown))
здесьfactor
— коэффициент отсрочки, который влияет на общее время отсрочки. а такжеretries
соответствует вышеуказанномуc
(то есть количество столкновений). основной контентcountdown = factor * (2 ** retries)
Это та же идея, что и алгоритм экспоненциальной отсрочки, упомянутый выше. Исходя из этого,full_jitter
Установить какTrue
, смысл в том, чтобы сделать "джиттер" на время отсрочки, чтобы иметь некую случайность. Наконец, это ограничение заданного значения, чтобы оно не превышало максимальное значение.maximum
, чтобы избежать бесконечного времени ожидания. Однако после достижения максимального времени отсрочки это может привести к повторному одновременному выполнению нескольких задач. узнать большеTask.retry_jitter.
4.2 Пример подключения в "Расширенном программировании в среде UNIX"
В главе 16.4 Advanced Programming in the UNIX Environment, 3-е издание, также есть пример использования экспоненциальной отсрочки для установления соединения:
#include "apue.h"
#include <sys/socket.h>
#define MAXSLEEP 128
int connect_retry(int domain, int type, int protocol,
const struct sockaddr *addr, socklen_t alen)
{
int numsec, fd;
/*
* 使用指数退避尝试连接
*/
for (numsec = 1; numsec < MAXSLEEP; numsec <<= 1)
{
if (fd = socket(domain, type, protocol) < 0)
return (-1);
if (connect(fd, addr, alen) == 0)
{
/*
* 连接接受
*/
return (fd);
}
close(fd);
/*
* 延迟后重试
*/
if (numsec <= MAXSLEEP / 2)
sleep(numsec);
}
return (-1);
}
В случае сбоя соединения процесс приостанавливается на короткое время (numsec
), затем перейдите к следующему циклу и повторите попытку. Каждый цикл спит в два раза дольше, чем предыдущий, до максимальной задержки более 1 минуты, после чего он больше не будет пытаться.
Суммировать
Возвращаясь к вопросу в начале, при обнаружении ошибки ограничения тока мы можем повторить попытку с помощью алгоритма экспоненциальной отсрочки, и мы можем снова избежать ограничения тока в наибольшей степени. По сравнению с повторными попытками с фиксированным временем экспоненциальная отсрочка добавляет увеличение времени и случайность, делая ее более «умной». На данный момент нам больше не нужно беспокоиться о том, что ограничение тока прервет всю тестовую программу~