Для удобства выезда на работу я в прошлом году сдала свой дом в северном пригороде и переехала в южный пригород, который ближе к месту моей работы.Это экономит мне много времени и денег, и я могу использовать его для делать много значимой работы, по крайней мере, меня не будут беспокоить пробки, и мое чувство счастья резко возросло.
Но даже в этом случае в жизни есть и другие неприятности. Плотность застройки в южном пригороде относительно высока, поэтому парковки стали головной болью.Я арендую нефиксированные парковочные места по обеим сторонам дороги.Каждый раз, когда я возвращаюсь с работы, парковочных мест не должно быть, поэтому Я могу парковаться только рядом с чужими машинами, но проблема в том, что каждое утро меня нужно будить звонком, чтобы переставить машину, поэтому мне не нужно беспокоиться о своем настроении.
Но через несколько дней я постепенно стал умнеть: когда я парковал машину накануне вечером, я находил машины с ограничением движения на следующий день и ставил их рядом, чтобы мне не приходилось передвигать машину на следующий день "Большой бонус".
иОграничение транспортного средства — очень распространенная стратегия ограничения тока в жизни.Он привел меня в дополнение к вышеупомянутым преимуществам снаружи и дал нам лучшую среду на протяжении всей жизни привезли проблеск улучшения, а быстрый рост частного автомобильного движения дал нам огромное «нагрузку», если нет, тогда линия, может быть все Автомобили застряли в трафике, что является огромным преимуществом нашей жизни.
Возвращаясь к программе из жизни, предположим, что система может обслуживать только людей мощностью 10 Вт.Внезапно однажды из-за горячего события количество посещений системы за короткий промежуток времени резко увеличилось до 50 Вт, тогда прямым результатом было системный сбой. Никто больше не может использовать систему,Очевидно, что только небольшое количество людей может использовать его, что больше соответствует нашим ожиданиям, чем не все могут его использовать, поэтому в настоящее время мы должны использовать «ограничение тока»..
Класс ограничения тока
Есть много способов реализовать ограничение тока, Брат Лэй немного объяснил это здесь.Классификация ограничения токаСледующее:
- Текущий лимит проверки легальности: такие как проверочный код, черный список IP-адресов и т. д., эти методы могут эффективно предотвращать вредоносные атаки и сбор данных поисковыми роботами;
- Дросселирование контейнера: Например, Tomcat, Nginx и другие методы ограничения тока, в которых Tomcat может установить максимальное количество потоков (maxThreads), и когда параллелизм превысит максимальное количество потоков, он будет поставлен в очередь на выполнение; в то время как Nginx предоставляет два текущих методы ограничения: один для контроля скорости, а другой для контроля количества одновременных подключений;
- Текущий лимит на стороне сервера: Например, мы реализуем ограничение тока с помощью алгоритма ограничения тока на стороне сервера, чему также посвящена эта статья.
Наиболее распространенный бизнес-код — это юридический код проверки и система черного списка IP-адресов.В этой статье мы не будем слишком подробно описывать ее.Мы сосредоточимся на реализации последних двух решений ограничения тока: ограничение тока контейнера и дросселирование на стороне сервера.
Дросселирование контейнера
Томкэт дросселирует
Максимальное количество потоков для Tomcat версии 8.5 указано в конфигурации conf/server.xml следующим образом:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="150"
redirectPort="8443" />
вmaxThreads
Это максимальное количество потоков Tomcat. Когда параллелизм запроса превышает это значение (maxThreads), запрос ставится в очередь на выполнение, тем самым выполняя цель ограничения тока.
Советы: Значение maxThreads можно соответствующим образом увеличить. Значение по умолчанию равно 150 (версия Tomcat 8.5.42), но это значение не настолько велико, насколько это возможно. Это зависит от конкретной конфигурации оборудования. Потоки должны потреблять 1 МБ памяти JVM. пространство для стеков потоков, и чем больше потоков, тем больше нагрузка на сборщик мусора. Наконец, следует отметить, что операционная система имеет определенные ограничения на количество потоков в процессе: количество потоков в каждом процессе Windows не может превышать 2000, а количество потоков в каждом процессе Linux не разрешено превышать 1000.
Регулировка Nginx
Nginx предоставляет два метода ограничения тока: один — контролировать скорость, а другой — контролировать количество одновременных подключений.
скорость контроля
нам нужно использоватьlimit_req_zone
Он используется для ограничения количества запросов в единицу времени, то есть ограничения скорости, пример конфигурации выглядит следующим образом:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
location / {
limit_req zone=mylimit;
}
}
В приведенной выше конфигурации указано, что ограничение скорости каждого IP-доступа составляет 2 об / с, поскольку текущая статистика ограничения Nginx основана на миллисекундах, мы устанавливаем скорость 2 об / с, и если вы преобразуете ее, один IP-адрес может проходить только 1 запрос в течение 500 мс. Второй запрос может пройти только после 501 мс.
Мы используем один IP для отправки и отправки 6 запросов в течение 10 мс.Результаты выполнения следующие:
Из вышеприведенных результатов видно, что его выполнение соответствует нашим ожиданиям: только одно выполнение успешно, а остальные 5 отклонены (второе будет выполнено нормально через 501 мс).
Повышение лимита скорости
Хотя приведенный выше контроль скорости очень точен, он слишком суров для применения в реальной среде.На самом деле мы должны контролировать общее количество доступов на IP-единицу в общем времени, а не так точно, как указано выше, но в миллисекундах. мы можем использовать ключевое слово Burst для включения. Для этой настройки пример конфигурации выглядит следующим образом:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
location / {
limit_req zone=mylimit burst=4;
}
}
Burst=4 означает, что каждый IP-адрес допускает пакетные запросы до 4. Если один IP-адрес отправляет 6 запросов в течение 10 мс, результат будет следующим:
Как видно из приведенных выше результатов, 1 запрос был обработан сразу, 4 запроса были помещены в очередь на выполнение, а еще 1 запрос был отклонен.
параллелизм управления
использоватьlimit_conn_zone
иlimit_conn
Две инструкции могут управлять числом параллелизма.Пример конфигурации выглядит следующим образом:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
Где limit_conn perip 10 означает, что один IP-адрес может поддерживать максимум 10 подключений одновременно, а limit_conn perserver 100 означает, что сервер может обрабатывать до 100 одновременных подключений одновременно.
Совет: это соединение учитывается только тогда, когда заголовок запроса обрабатывается серверной частью.
Текущий лимит на стороне сервера
Ограничение тока на стороне сервера должно быть реализовано с помощью алгоритма ограничения тока, и этот алгоритм эквивалентен «мозгу», реализующему ограничение тока, который используется для управления реализацией схемы ограничения.
У некоторых людей может закружиться голова, когда они увидят слово «алгоритм» и посчитают его очень эзотерическим, но это не так. Алгоритм эквивалентен краткому изложению конкретных шагов реализации операции определенной транзакции, на самом деле его несложно понять, не пугайтесь его внешнего вида~
Общие алгоритмы ограничения токаЕсть следующие три:
- алгоритм временного окна
- алгоритм дырявого ведра
- алгоритм токена
Далее мы рассмотрим их отдельно.
1. Алгоритм временного окна
Так называемый алгоритм скользящего времени относится к выбору текущего времени в качестве крайнего срока и перемещению на определенное время вперед, например, переводу времени на 60 секунд вперед, и максимальное количество обращений, выполняемых в течение этих 60 секунд, составляет 100. В это время логика выполнения алгоритма следующая: сначала очистить все записи запросов до 60 с, а затем вычислить, превышает ли количество запросов в текущем наборе установленное максимальное количество запросов 100, если оно больше текущего ограничить и отклонить политику, в противном случае вставить запись запроса и вернуть клиенту идентификатор, который может быть выполнен в обычном режиме.
Скользящее временное окно показано на следующем рисунке:
Каждое маленькое значение представляет 10 секунд, а период времени, обведенный красной пунктирной линией, – это интервал времени, который необходимо оценить. Например, 60 сек – это 100 запросов, тогда красная пунктирная линия – 60 сек.
Мы можем использовать упорядоченный набор ZSet Redis для реализации текущего ограничения алгоритма временного окна.Процесс реализации заключается в том, чтобы сначала использовать ключ ZSet для хранения идентификатора текущего ограничения и счет для хранения времени запрос.Очистить объем доступа предыдущего временного окна, сравнить номер текущего временного окна с максимально допустимым объемом доступа и вернуть false, если он больше или равен максимальному объему доступа.Выполнить текущую операцию ограничения, которая отвечает за разрешение выполнения бизнес-логики и добавление действительной записи в ZSet.Access Records, конкретный код реализации выглядит следующим образом.
Мы используем пакет Jedis для работы с Redis и добавляем ссылку на платформу Jedis в pom.xml, Конфигурация выглядит следующим образом:
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
Конкретный код реализации Java выглядит следующим образом:
import redis.clients.jedis.Jedis;
public class RedisLimit {
// Redis 操作客户端
static Jedis jedis = new Jedis("127.0.0.1", 6379);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 15; i++) {
boolean res = isPeriodLimiting("java", 3, 10);
if (res) {
System.out.println("正常执行请求:" + i);
} else {
System.out.println("被限流:" + i);
}
}
// 休眠 4s
Thread.sleep(4000);
// 超过最大执行时间之后,再从发起请求
boolean res = isPeriodLimiting("java", 3, 10);
if (res) {
System.out.println("休眠后,正常执行请求");
} else {
System.out.println("休眠后,被限流");
}
}
/**
* 限流方法(滑动时间算法)
* @param key 限流标识
* @param period 限流时间范围(单位:秒)
* @param maxCount 最大运行访问次数
* @return
*/
private static boolean isPeriodLimiting(String key, int period, int maxCount) {
long nowTs = System.currentTimeMillis(); // 当前时间戳
// 删除非时间段内的请求数据(清除老访问数据,比如 period=60 时,标识清除 60s 以前的请求记录)
jedis.zremrangeByScore(key, 0, nowTs - period * 1000);
long currCount = jedis.zcard(key); // 当前请求次数
if (currCount >= maxCount) {
// 超过最大请求次数,执行限流
return false;
}
// 未达到最大请求数,正常执行业务
jedis.zadd(key, nowTs, "" + nowTs); // 请求记录 +1
return true;
}
}
Результат выполнения вышеуказанной программы:
Нормальный запрос выполнения: 0
Нормальный запрос выполнения: 1
Нормальное выполнение запросов: 2
Нормальное выполнение запросов: 3
Нормальное выполнение запросов: 4
Нормальное выполнение запросов: 5
Нормальный запрос на выполнение: 6
Нормальное выполнение запросов: 7
Нормальное выполнение запросов: 8
Нормальное выполнение запросов: 9
Текущее ограничение: 10
Текущее ограничение: 11
Текущее ограничение: 12
Текущее ограничение: 13
Текущее ограничение: 14
После спящего режима выполните запрос в обычном режиме
У этой реализации есть два недостатка:
- Используйте ZSet для хранения каждой записи доступа, если объем данных относительно велик, он займет много места, например, когда 60s разрешает доступ 100W;
- Выполнение этого кода является неатомарной операцией, которая сначала оценивается, а затем добавляется, а выполнение другой бизнес-логики может быть вкраплено в промежутки посередине, что в конечном итоге приведет к неточным результатам.
2. Алгоритм дырявого ведра
Утечный алгоритм ведра вдохновил воронку, как показано ниже:
Проблема с алгоритмом скользящего времени заключается в том, что в пределах определенного диапазона, например, может быть только 10 запросов в течение 60 с. Когда 10 запросов поступают в первую секунду, то все запросы могут быть отклонены только в течение оставшихся 59 с. алгоритм ведра может решить эту проблему.
Алгоритм воронки аналогичен воронке в жизни, какой бы большой поток воды наверху ни лился в воронку, то есть сколько бы ни было запросов, она будет медленно вытекать с одинаковой скоростью. Когда скорость воды наверху больше, чем скорость оттока внизу, воронка будет медленно наполняться, а когда воронка наполнится, новые запросы будут отброшены; когда скорость воды наверху меньше скорости отток внизу, воронка никогда не будет заполнена полностью и может течь все время.
Шаги реализации алгоритма дырявого ведра заключаются в том, чтобы сначала объявить очередь для сохранения запросов.Эта очередь эквивалентна воронке.Когда емкость очереди заполнена, новые запросы отбрасываются, а затем повторно объявляется поток для периодического получения одного или более запросов из очереди задач.Выполняется задача, таким образом реализуя алгоритм дырявого ведра.
Выше мы продемонстрировали, что скорость управления Nginx на самом деле использует алгоритм дырявого ведра Конечно, мы также можем легко реализовать алгоритм дырявого ведра с помощью Redis.
Мы можем использовать модуль Redis-Cell, представленный в версии Redis 4.0, который использует алгоритм воронки и предоставляет инструкции по атомарному ограничению тока, и, полагаясь на Redis, естественно распределенную программу, может достичь более совершенного ограничения тока.
Метод Redis-Cell для реализации ограничения тока также очень прост, вам нужно использовать только одну инструкцию cl.throttle, Пример использования следующий:
> cl.throttle mylimit 15 30 60
1)(integer)0 # 0 表示获取成功,1 表示拒绝
2)(integer)15 # 漏斗容量
3)(integer)14 # 漏斗剩余容量
4)(integer)-1 # 被拒绝之后,多长时间之后再试(单位:秒)-1 表示无需重试
5)(integer)2 # 多久之后漏斗完全空出来
Где 15 — пропускная способность воронки, а 30/60 — скорость воронки.
3. Алгоритм токена
В алгоритме корзины токенов есть программа, которая генерирует токены с постоянной скоростью и сохраняет их в корзине токенов, и каждый запрос должен получить токен, прежде чем его можно будет выполнить.Если запрос не получает токен, вы можете выбрать ожидание или отказаться от выполнения, как показано на следующем рисунке:
Мы можем использовать пакет guava с открытым исходным кодом Google, чтобы легко реализовать алгоритм ведра маркеров.Во-первых, добавьте ссылку на guava в pom.xml, и конфигурация будет следующей:
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
Конкретный код реализации выглядит следующим образом:
import com.google.common.util.concurrent.RateLimiter;
import java.time.Instant;
/**
* Guava 实现限流
*/
public class RateLimiterExample {
public static void main(String[] args) {
// 每秒产生 10 个令牌(每 100 ms 产生一个)
RateLimiter rt = RateLimiter.create(10);
for (int i = 0; i < 11; i++) {
new Thread(() -> {
// 获取 1 个令牌
rt.acquire();
System.out.println("正常执行方法,ts:" + Instant.now());
}).start();
}
}
}
Результат выполнения вышеуказанной программы:
Обычный метод выполнения, ts:2020-05-15T14:46:37.175Z
Обычный метод выполнения, ts:2020-05-15T14:46:37.237Z
Обычный метод выполнения, ts:2020-05-15T14:46:37.339Z
Обычный метод выполнения, ts:2020-05-15T14:46:37.442Z
Обычный метод выполнения, ts:2020-05-15T14:46:37.542Z
Обычный метод выполнения, ts:2020-05-15T14:46:37.640Z
Обычный метод выполнения, ts:2020-05-15T14:46:37.741Z
Метод нормального исполнения, TS: 2020-05-15T14: 46: 37,840z
Метод нормального исполнения, TS: 2020-05-15T14: 46: 37.942Z
Обычный метод выполнения, ts:2020-05-15T14:46:38.042Z
Обычный метод выполнения, ts:2020-05-15T14:46:38.142Z
Из приведенных выше результатов видно, что токен действительно генерируется каждые 100 мс, а метод Acquire() блокируется и ожидает получения токена. Он может передать параметр типа int, чтобы указать количество токенов, которые необходимо получить. Его альтернативой является tryAcquire(), который возвращает false, если нет доступных токенов, чтобы не блокировать ожидание. Конечно, метод tryAcquire() также может устанавливать тайм-аут, если максимальное время ожидания не превышено, он заблокирует ожидание получения токена, если максимальное время ожидания превышено, он вернет false, если нет доступного токена. .
Примечание. Алгоритм токена, реализованный guava, представляет собой автономную схему ограничения тока на уровне программы, в то время как описанный выше алгоритм с использованием Redis-Cell представляет собой распределенную схему ограничения тока.
Суммировать
В этой статье представлены 6 специальных средств для реализации ограничения тока, а именно: Tomcat используетmaxThreads
Для достижения ограничения тока; Nginx предоставляет два метода ограничения тока, один черезlimit_req_zone
иburst
Чтобы добиться ограничения скорости тока, второй должен пройтиlimit_conn_zone
иlimit_conn
Две директивы контролируют общее количество одновременных подключений. Наконец, мы рассказали об алгоритме временного окна, который можно реализовать с помощью упорядоченных коллекций Redis, об алгоритме дырявого ведра, который можно реализовать с помощью Redis-Cell, и об алгоритме токена, который можно реализовать, решив пакет guava от Google.
Следует отметить, что схема ограничения тока, реализованная с помощью Redis, может использоваться в распределенных системах, в то время как схема ограничения тока, реализованная с помощью guava, может использоваться только в среде с одним компьютером. Если вам не нравятся проблемы с ограничением тока на стороне сервера, вы даже можете использовать ограничение тока контейнера (Nginx или Tomcat) напрямую, не меняя код, но только если это может удовлетворить ваши бизнес-потребности.
Что ж, на этом статья заканчивается, жду вас в следующем выпуске~
последние слова
Оригинальность – это непросто, если вы считаете, что эта статья вам полезна, нажмите на значок "отличный", это самая большая поддержка и поощрение для автора, спасибо!
Ссылки и благодарности
woo woo woo.cn blog on.com/big little press…
Подпишитесь на официальный аккаунт «Java Chinese Community» и ответьте на «Галантные товары», чтобы получить 50 оригинальных галантерейных товаров.Топ-лист.