Посмотрите, как программисты Али говорят об ограничении тока

Java

Некоторые читатели сказали, что проект, который он подготовил, когда участвовал в осеннем наборе, был системой seckill, Он много готовил в дизайне Redis и MySQL, но в каждом интервью интервьюер спрашивал его, как ограничить ток. Он был не готов к текущему лимиту, и его ответы были очень неорганизованными, в начале интервью он запаниковал.

На самом деле, в реальной системе seckill лимит тока особенно важен, поэтому интервьюер также уделяет этому аспекту особое внимание. Сегодня я увидел очень систематизированную статью, объясняющую ограничение тока, давайте учиться вместе.

Зачем ограничивать ток

В повседневной жизни, в каких местах нужно ограничивать поток?

Например, рядом со мной находится национальная живописная площадка, обычно ее посещает не так много людей, но 1 мая или во время праздника Весны она будет переполнена, в это время персонал, управляющий сценической площадкой, реализовать ряд политик по ограничению потока людей. Зачем ограничивать поток? Если живописное место может вместить 10 000 человек. Теперь, когда 30 000 человек вошли, оно обязательно будет одно за другим. Если его не отремонтировать, там Будут несчастные случаи Результатом является то, что опыт у всех не очень хороший Если произойдет несчастный случай, живописное место может быть закрыто Недоступно, и следствием этого является то, что опыт ужасен для всех.

Идея ограничения потока состоит в том, чтобы максимально увеличить количество входящих людей при обеспечении доступности, а остальные люди будут ждать в очереди снаружи, чтобы убедиться, что 10 000 человек внутри могут нормально играть.

Возвращаясь к Интернету, то же самое, например, если некая звезда объявляет об отношениях, количество посещений увеличилось с обычных 500 000 до 5 миллионов, а система может поддерживать до 2 миллионов посещений. Текущее правило ограничения должно быть реализовано, чтобы гарантировать, что одно состояние доступно, так что сервер не выйдет из строя, что приведет к недоступности всех запросов.

Текущие ограничивающие идеи

Обычно существуют следующие режимы ограничения тока системных служб:

предохранитель

Предохранительные меры учитываются в начале проектирования системы. Когда возникает проблема с системой, если ее нельзя устранить за короткий промежуток времени, система должна автоматически принять решение, включить предохранитель, запретить доступ к трафику и избежать перегруженных запросов к серверной части из-за большого трафика. .

Система также должна иметь возможность динамически отслеживать восстановление серверной программы.Когда программа вернется к стабильной работе, автоматический выключатель можно отключить для восстановления нормального обслуживания. Общие компоненты предохранителей включают Hystrix и Ali's Sentinel, которые имеют свои преимущества и недостатки и могут быть выбраны в соответствии с реальной ситуацией в бизнесе.

640.png

понижение уровня обслуживания

Классифицируйте все функциональные службы системы.Когда возникает проблема с системой и требуется аварийное ограничение тока, менее важные функции могут быть понижены, а службы могут быть остановлены, что может высвободить больше ресурсов для использования основных функций.

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

Задержка обработки

Этот режим требует установки буферного пула трафика во внешнем интерфейсе системы, буферизации всех запросов в этот пул, а не их немедленной обработки. Затем реальная программа бизнес-обработки на бэкенде берет запросы из этого пула и обрабатывает их последовательно, что можно реализовать в режиме общей очереди. Это эквивалентно уменьшению нагрузки на серверную часть асинхронным способом, однако при большом трафике вычислительная мощность серверной части ограничена, и запросы в пуле буферов могут не обрабатываться вовремя. , и будет определенная степень задержки. Идея состоит в следующем конкретном алгоритме дырявого ведра и алгоритме ведра с токенами.

Управление привилегиями

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

Разница между кэшированием, понижением версии и ограничением тока

тайник, используется для увеличения пропускной способности системы, повышения скорости доступа и обеспечения высокого параллелизма.

понижение рейтинга, когда некоторые сервисные компоненты системы недоступны, всплески трафика, исчерпаны ресурсы и т. д., чтобы временно заблокировать рассматриваемую услугу, продолжать предоставлять услуги с пониженной версией, давать пользователям как можно более дружественные подсказки, возвращать нижние данные, делать не повлияет на общий бизнес-процесс, и сервис будет повторно запущен после решения проблемы.

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

алгоритм ограничения тока

Существует много алгоритмов ограничения тока, и есть три распространенных типа, а именно алгоритм счетчика, алгоритм дырявого ведра и алгоритм ведра с маркерами, которые по одному объясняются ниже.

Алгоритм счетчика

Простые и грубые, такие как указание размера пула потоков, указание размера пула соединений с базой данных, количество соединений nginx и т. д., все это относится к алгоритму счетчика.

Алгоритм счетчика является самым простым и легко реализуемым в алгоритме ограничения тока. Например, оговариваем, что для интерфейса А количество посещений, которые мы можем совершить за одну минуту, не может превышать 100. Тогда мы можем сделать так: в начале мы можем установить счетчик счетчика, и каждый раз, когда приходит запрос, счетчик увеличивается на 1, если значение счетчика больше 100 и интервал между запросом и первым запросом все еще В течение 1 минуты, это означает, что слишком много запросов и доступ запрещен; если интервал между запросом и первым запросом больше 1 минуты, а значение счетчика все еще находится в пределах текущего предела, то сбрасывается счетчик, вот он простой и сырой.640 (1).png

алгоритм дырявого ведра

Идея алгоритма дырявого ведра очень проста.Вода (запрос) сначала поступает в дырявое ведро, а дырявое ведро с определенной скоростью сбрасывает воду.Когда скорость притока воды слишком высока, она будет переливаться сразу, когда она превышает допустимую емкость ведра.Видно, что алгоритм дырявого ведра можно принудительно ограничить Ограничить скорость передачи данных.640 (2).png

Преимущества этого:

отсечение пика: При поступлении большого количества трафика произойдет переполнение, чтобы была доступна служба защиты с ограничением тока.

буфер: он не будет напрямую запрашивать сервер, скорость потребления буферного давления фиксирована, потому что производительность вычислений фиксирована.

Алгоритм ведра токенов

Токен ведро похоже на дырявое ведро.Разница в том, что некоторые токены помещаются в токен ведро.После того как придет запрос на обслуживание, услуга будет получена только после получения токена.Например мы обычно идем в столовую есть в столовой Очередь перед окном в столовой похожа на алгоритм дырявого ведра Большое количество людей собирается за окном в столовой, чтобы насладиться обслуживанием с определенной скоростью Если слишком много людей вливается и столовая не может поместиться,могут быть некоторые из них.Когда люди стоят вне столовой,они не пользуются услугой столовой,что называется переполнением.Переполнение может продолжать просить,то есть продолжать выстраиваться в очередь,поэтому в чем проблема?

Если в это время есть особые обстоятельства, например, какие-то добровольцы спешат, или третьекурсники старшей школы собираются сдавать вступительные экзамены в колледж, эта ситуация является чрезвычайной ситуацией.Если мы также используем алгоритм дырявого ведра, мы должны стоять в очереди медленно, что не решает нашу проблему.Требования, для многих сценариев приложений, в дополнение к требованию иметь возможность ограничить среднюю скорость передачи данных, также требуется разрешить некоторую степень пакетной передачи. В настоящее время алгоритм дырявого ведра может не подходить, а алгоритм ведра с токеном больше подходит. Как показано на рисунке, принцип алгоритма корзины токенов заключается в том, что система будет помещать токены в корзину с постоянной скоростью, и если запрос необходимо обработать, ей нужно сначала получить токен из корзины. карта желательна, в услуге отказано.640 (3).png

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

Ограничение параллелизма

Проще говоря, это установка общего количества запросов в секунду для системного порога.Это также довольно распространено.Возьмем, к примеру, Tomcat, многие параметры основаны на этом соображении, например

настроенacceptCountУстановите количество ответных подключений,maxConnectionsУстановите мгновенное максимальное количество подключений,maxThreadsУстановите максимальное количество потоков.В каждом фреймворке или компоненте ограничение параллелизма теперь находится в следующих аспектах:

  • Ограничьте общее количество одновременных (например, пул соединений с базой данных, пул потоков)

  • Ограничить количество мгновенных одновременных подключений (модуль nginx limit_conn, используемый для ограничения количества мгновенных одновременных подключений)

  • Ограничьте среднюю скорость в пределах временного окна (например, RateLimiter в Guava, модуль limit_req в nginx, ограничьте среднюю скорость в секунду)

  • Другие включают ограничение скорости вызовов удаленных интерфейсов и ограничение скорости потребления MQ.

  • Кроме того, вы можете ограничить ток в зависимости от количества сетевых подключений, сетевого трафика, загрузки ЦП или памяти и т. д.

С ограничением тока параллелизма это означает, что существует дополнительный механизм защиты при работе с высоким параллелизмом.Нет необходимости беспокоиться о мгновенном трафике, вызывающем зависание системы или лавину и в конечном итоге повреждающем службу, а не отсутствие службы; ограничение должно быть хорошо оценено, не может использоваться без разбора, в противном случае у некоторого нормального трафика будут некоторые странные проблемы, что приведет к ухудшению пользовательского опыта и потере пользователей.

Ограничение тока интерфейса

Ограничение тока интерфейса разделено на две части: одна предназначена для ограничения количества вызовов интерфейса в течение периода времени, относится к алгоритму счетчика предыдущего алгоритма ограничения тока, а другая предназначена для установки алгоритма скользящего временного окна.

Общее количество интерфейсов

Для контроля общего количества обращений к интерфейсу за период времени можно обратиться к предыдущему алгоритму счетчика, который здесь повторяться не будет.

временное окно интерфейса

Проблема с алгоритмом фиксированного временного окна (то есть вышеупомянутым алгоритмом счетчика) заключается в том, что статистический интервал слишком велик, текущий предел недостаточно точен, а связь и влияние с предыдущим статистическим интервалом не учитываются во втором статистический интервал (первый. Вторая половина интервала + первая половина второго интервала тоже одна минута). Чтобы решить упомянутую выше проблему критичности, мы пытаемся разделить каждый статистический интервал на меньшие статистические интервалы, более точные статистические подсчеты.640 (4).png

В приведенном выше примере, предполагая, что QPS может принимать 100 запросов в секунду, первые 40 секунд доступа в первую минуту очень низки, а вторые 20 секунд внезапно увеличиваются, и это длится некоторое время до 40-й секунды второго. минута.Внизу, по предыдущему методу подсчета, QPS первой секунды 94, а QPS второй секунды 92, так что не превышает установленных параметров, но!Но в средней области QPS достигает 142, что явно больше нашего допустимого.Количество запросов на обслуживание, поэтому счетчики фиксированного окна менее надежны и требуются счетчики скользящего окна.

Алгоритм счетчика на самом деле является алгоритмом фиксированного окна, но он не делит дополнительно временное окно, поэтому имеется только 1 сетка, видно, что когда сетка скользящего окна делится больше, то есть вторая является точной до миллисекунд или наносекунд, то чем плавнее будет прокрутка скользящего окна, тем точнее будет текущая ограничивающая статистика.

Обратите внимание, что больше места потребляется.

Реализация ограничения тока

Эта часть является конкретной реализацией ограничения тока, проще говоря, никто не хочет читать длинный код.

реализация гуавы

импортный пакет

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version>
</dependency>

основной код

  LoadingCache<Long, AtomicLong> counter = CacheBuilder.newBuilder().
    expireAfterWrite(2, TimeUnit.SECONDS)
    .build(new CacheLoader<Long, AtomicLong>() {

     @Override
     public AtomicLong load(Long secend) throws Exception {
      // TODO Auto-generated method stub
      return new AtomicLong(0);
     }
    });
  counter.get(1l).incrementAndGet();

Реализация Token Bucket

Стабильный режим (SmoothBursty: постоянная скорость генерации токенов)

 public static void main(String[] args) {
  // RateLimiter.create(2)每秒产生的令牌数
  RateLimiter limiter = RateLimiter.create(2);
        // limiter.acquire() 阻塞的方式获取令牌
  System.out.println(limiter.acquire());;
  try {
   Thread.sleep(2000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
  
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
 }

`RateLimiter.create(2)Емкость и пакетный объем, алгоритм корзины для токенов позволяет временно хранить токены, которые не использовались в течение определенного периода времени, в корзине для импульсного потребления.

Прогрессивный режим (SmoothWarmingUp: скорость генерации токенов медленно увеличивается, пока не будет поддерживать стабильное значение)

 // 平滑限流,从冷启动速率(满的)到平均消费速率的时间间隔
  RateLimiter limiter = RateLimiter.create(2,1000l,TimeUnit.MILLISECONDS);
  System.out.println(limiter.acquire());;
  try {
   Thread.sleep(2000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;
  
  System.out.println(limiter.acquire());;
  System.out.println(limiter.acquire());;

тайм-аут

boolean tryAcquire = limiter.tryAcquire(Duration.ofMillis(11));

Можно ли получить токен в течение времени ожидания и выполнить его асинхронно.

Ограничение тока распределенной системы

Реализация Nginx + Lua

Resty.lock можно использовать для поддержки атомарных свойств, и между запросами не будет повторного входа в блокировку.

GitHub.com/open остальное имеет/приходит…

Используйте lua_shared_dict для хранения данных

local locks = require "resty.lock"

local function acquire()
    local lock =locks:new("locks")
    local elapsed, err =lock:lock("limit_key") --互斥锁 保证原子特性
    local limit_counter =ngx.shared.limit_counter --计数器

    local key = "ip:" ..os.time()
    local limit = 5 --限流大小
    local current =limit_counter:get(key)

    if current ~= nil and current + 1> limit then --如果超出限流大小
       lock:unlock()
       return 0
    end
    if current == nil then
       limit_counter:set(key, 1, 1) --第一次需要设置过期时间,设置key的值为1,
--过期时间为1秒
    else
        limit_counter:incr(key, 1) --第二次开始加1即可
    end
    lock:unlock()
    return 1
end
ngx.print(acquire())