Чем больше вы знаете, тем больше вы не знаете
Вопрос часто приводит к череде вопросов, и слепое пятно знаний незаметно для себя открываю🤣. рут в сделай сам限流注解
При столкновении с проблемами верно, что один больше, чем один:
- Какой алгоритм ограничения тока является более подходящим?
- Как реализовать ограничение тока с помощью аннотаций
- Как регулировать каждый метод по отдельности
- Как преобразовать длинные строки в короткие строки
- шестигранник
or
шестнадцатеричный - Что такое LRU и как его реализовать с помощью простых структур данных
упражняться
Что такое ограничение тока
накладывает ограничения на запросы, принимаемые сервером, только часть запросов может реально дойти до сервера, а остальные запросы могут задерживаться или отклоняться. Тем самым избегая всех запросов к БД и ломая БД.
Возьмите сценарий, с которым вы можете столкнуться в своей жизни, особенно в Пекине, Шанхае, Гуанчжоу и Шэньчжэне или в новых городах первого уровня, метро Ханчжоу, линия 1, станция Fengqi Road, когда пассажиропоток достигает определенного пика, дядя-полицейский 👮 ♀ Вам могут запретить садиться в метро и пользоваться другими видами транспорта.️. . . Это все слезы
Какой алгоритм ограничения тока является более подходящим?
По поводу текущего алгоритма ограничения, в интернете много объяснений, алгоритм дырявого ведра, алгоритм ведра с токеном и т.д.,百度一下,你就知道
, где рут реализован с помощью простейшего алгоритма счетчика.
Алгоритм счетчика
- Разделите одну секунду на 10 этапов по 100 мс каждый.
- Количество вызовов интерфейса записывается каждые 100 мс.
- Конечно, со временем этапов будет все больше и больше. В это время можно удалить первые n стадий, и останется только 10 стадий, то есть останется только 1с.
- Количество вызовов последнего минус первого — это количество вызовов интерфейса за 1 с.
Как реализовать ограничение тока с помощью аннотаций
С использованиемnginx
Когда ток ограничен, он будетnginx
Поскольку прокси-уровень перехватывает запрос, обрабатывает его, то вSpring
Средний уровень агентаAOP
Ла
AOP
На веб-сервере существует множество сценариев, которые могут быть реализованы с помощью АОП, например:
- Распечатать журнал, записать класс времени, метод, параметр
- Используйте отражение, чтобы установить значение по умолчанию для подкачки PageRow, PageNum
- Игровая сцена, чтобы судить, закончилась ли игра, не нужно судить по всем методам
- Расшифровка, проверка подписи и т.д.
задача на время
В алгоритме счетчика мы упомянули, что количество обращений к интерфейсу нужно записывать каждые 100 мс и сохранять. Здесь пригодятся временные задачи.
Существует множество реализаций задач с синхронизацией, таких как использование пулов потоков.ScheduledExecutorService
,КонечноSpring
изScheduled
Без проблем.
Во-вторых, какая структура данных используется для сохранения количества звонков -->LinkedList.
Кроме того, нам нужно ограничить поток несколькими методами, как это решить? -->Каждый метод имеет уникальное соответствующее значение:package + class + methodName
, поэтому мы используем это уникальное значение в качестве ключа, linkedList в качестве карты, следующий код
/** 每个key 对应的调用次数**/
private Map<String, Long> countMap = new ConcurrentHashMap<>();
/** 每个key 对应的linkedlist**/
private static Map<String, LinkedList<Long>> calListMap = new ConcurrentHashMap<>();
## 每s一次查询
@Scheduled(cron = "*/1 * * * * ?")
private void timeGet(){
countMap.forEach((k,v)->{
LinkedList<Long> calList = calListMap.get(k);
if(calList == null){
calList = new LinkedList<>();
}
# 每个方法的调用次数放入linkedList中
calList.addLast(v);
calListMap.put(k, calList);
if (calList.size() > 10) {
calList.removeFirst();
}
});
}
инспекция АОП
определить аннотации
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CalLimitAnno {
String value() default "" ;
String methodName() default "" ;
long count() default 100;
}
Проверка перед вызовом интерфейса
@Around(value = "@annotation(around)")
public Object initBean(ProceedingJoinPoint point, CalLimitAnno around) throws Throwable {
/** 获取类名和方法名 **/
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
String[] classNameArray = method.getDeclaringClass().getName().split("\\.");
String methodName = classNameArray[classNameArray.length - 1] + "." + method.getName();
String classZ = signature.getDeclaringTypeName();
String countMapKey = classZ + "|" + methodName;
LinkedList<Long> calList = calListMap.get(countMapKey);
if(calList != null){
/** 调用次数判断是否已经超过注解设置的值 **/
if ((calList.peekLast() - calList.peekFirst()) > Long.valueOf(around.count())) {
throw new RuntimeException("被限流了");
}
/** 存放**/
countMap.putIfAbsent(countMapKey,0L);
countMap.put(countMapKey,countMap.get(countMapKey) + 1);
}
Object object = point.proceed();
return object;
}
метод
Учитывая, что частота задач, выполняемых по времени, не может быть слишком маленькой, наши задачи, выполняемые по времени, выполняются один раз в секунду, и здесь нам нужно установить текущее предельное значение в 10 секунд, что приведет к большей детализации.
@CalLimitAnno(count = 1000)
public void testPageAnno(){
System.out.println("成功执行");
}
Оптимизация карты
Выше мыpackage + className + methodName
В качестве единственного ключа длина ключа становится особенно большой. Должны ли мы найти способ уменьшить длину ключа?
Некоторые учащиеся думают о сжатии, но это совершенно нереально. По конкретным причинам см.Ссылка на сайт.
Это нельзя использовать, и это нельзя использовать, и это будет мешать людям жить🥺. Вы когда-нибудь думали, что текстовые сообщения, которые вы обычно получаете, иногда содержат короткую ссылку, и эти короткие ссылки на самом деле являются используемым передатчиком чисел --> Получите уникальный идентификатор с автоинкрементом из службы, а затем преобразуйте этот идентификатор. Например, в это время оно увеличивается до 100000, а затем преобразуется из десятичного числа 100000 в 62.q0U
. Это похоже на ссылку в текстовом сообщении, не так ли?
Сохранение карты
Поскольку самоувеличивается, одни и те же длинные символы, преобразованные в короткие строки при вызове службы, различаются. В некоторых бизнес-сценариях он может вызываться часто, что требует хранилища kv. В противном случае нет необходимости делать хранилище, делать все больше и больше ошибок~
kv оптимизация хранения
Предполагая, что нам нужно сделать хранилище кв, то, что может придумать детская обувь, вероятно,jvm
память илиredis
. Поскольку эта корреспонденция обычно долго не хранится, ее обычно используют как запрос в горячем событии. еслиredis
, вы можете установить время истечения срока действия как выселение. затем вjvm
В памяти надо учитывать, чтоLRU
. последний использованный
- Используемый ключ необходимо поместить в начало очереди
- Наименее часто используемые должны быть удалены после превышения лимита длины очереди.
Итак, какая структура данных нам нужна для реализации этой условной очереди?
GET
- Предполагая, что ключ не существует, вернуть null
- Предполагая, что ключ существует, когда вам нужно вернуть значение, вам нужно удалить соответствующий ключ и поместить ключ в начало очереди.
В приведенном выше сценарии это неприменимо к коллекции, базовым слоем которой является массив, такой как ArrayList. Не говорите, что вы не можете понять это. .
Тогда есть только связанный список, такой как LinkedList, но LinkedList необходимо пройти при запросе. Если мы сохраним на карте одновременно с LinkedList, этого достаточно? Конечно. . . . не этоmap
У меня есть запрос,node
Предыдущий узел необходимо сохранить. Таким образом, когда значение найдено, можно получить предыдущий узел, а соответствующий узел удалить из связанного списка.
PUT
- Предполагая, что ключ не существует, поместите его в начало очереди.
- Предполагая, что ключ существует, удалите ключ и поместите его в начало очереди.
проходить черезGet
Предвосхищение, само собой разумеется
Окончательный результат: LinedHashMap
LinkedHashMap
Конкретная колея здесь не навязана, или百度一下,你就知道
конец
Небезопасность потока, вызванная параллелизмом, здесь не рассматривается, просто ссылка~~~
После долгого разговора у вас все еще должно быть некоторое замешательство. Пожалуйста, оставьте сообщение ниже. Ни в коем случае, бедный язык.