Java - Углубленный анализ четырех основных алгоритмов ограничения тока: принципы, реализация и приложения.
Java - Углубленный анализ четырех основных алгоритмов ограничения тока: принципы, реализация и приложения.

Pre

Глубокое понимание распределенных технологий – ограничение тока

Параллельное программирование-25. Метод высокопараллельной обработки: идея очереди сообщений + идея разделения приложения + идея ограничения тока приложения

SpringBoot — Элегантная настройка【Flow Control】

Spring Boot — использование Resilience4j-RateLimiter для управления потоком и ухудшения качества обслуживания

MQ-19 Security_Design схемы ограничения тока

Ежедневный блог — беседа о том, как бороться с «всплеском трафика»

Обзор

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

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

  1. Ограничение тока шлюза API:на микро Служитьв архитектуре,API-шлюз обычно является единственным внешним входом в систему.,Необходимо ограничить количество запросов для одного пользователя или IP в течение определенного периода времени.,Чтобы защитить серверную часть Служить от вредоносных запросов или всплесков потока.
  2. Ограничение сервисного тока в распределенных системах:в распределенных системах,Между различными Служить могут быть вызывающие отношения.,Частоту звонков между Служить можно контролировать через Ограничение тока.,Избегайте перегрузки Служить, вызванной слишком частыми вызовами во время Служить.
  3. Ограничение тока интерфейса между микросервисами:микро Служитьв архитектуре,Связь между Служить через интерфейс,Выполните на интерфейсе Ограничение тока может гарантировать, что связь между Служить не станет недоступной из-за чрезмерных запросов.
  4. Лимит маркетинговой активности:При проведении маркетинговой деятельности,Чтобы предотвратить злонамеренное пролистывание страницы мероприятия, слишком большое количество посещений и сбой.,Интерфейс необходимо доработать.,Убедитесь, что действия могут выполняться в пределах контролируемого диапазона.
  5. Ограничение тока высокочастотной работы пользователя:Для интерфейсов, часто используемых пользователями,Например, вход в систему, публикации, комментирование и т. д.,Необходимость ограничить количество операций, которые пользователь может выполнить за короткий период времени.,Не позволяйте пользователям исчерпать системные ресурсы из-за вредоносных операций или частых операций.
  6. Ограничение активности флэш-продаж:На флэш-распродаже,Чтобы пользователи не отправляли слишком много запросов на флэш-продажу за короткий период времени.,из-за чего система не может обрабатывать,Необходимо провести Ограничение тока на интерфейсах, участвующих в распродаже.
  7. Ограничение тока защиты серверной службы:Для некоторых чувствительных операций или вычислительно интенсивных серверных частей.Служить,проходить Ограничение тока может избежать медленного ответа или сбоя Служить из-за слишком большого количества запросов.
  8. Предотвращение распределенных атак типа «отказ в обслуживании»:Страдание распределенного неприятия Служить(DDoS)При атаке,Стратегия ограничения тока может эффективно снизить давление, вызванное атаками.,Обеспечить стабильность ключевых операций.
  9. Оптимизация пользовательского опыта:Для тех, кому требуется высокая скорость отклика Служить,Чрезмерного количества невыполненных запросов можно избежать с помощью Ограничения тока.,Убедитесь, что пользователи могут получить хороший опыт Служить.

простой счетчик

принцип

простой счетчик является самым основным ограничением В токеалгоритме его принцип калибровки относительно интуитивен.

простой счетчикработапринципследующее:

  1. Настройка временного окна:Сначала установите фиксированноевременное окно,Например, 1 минута.
  2. Инициализация счетчика:В начале каждого временного окна,Сбросьте счетчик на 0.
  3. Запрос поступает:Всякий раз, когда Запрос При выполнении счетчик увеличивается на 1.
  4. Суждение и отказ:если в Значение счетчика в пределах временного окна достигает установленного порога.,Например 1000,Последующие запросы будут отклонены.,дотекущее времяоконные концы,Счетчик обнуляется.

выполнить

Язык кода:javascript
копировать
package com.artisan.counter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CounterRateLimit {


    /**
     * Количество запросов
     *
     * @return
     */
    int maxRequest();


    /**
     * временное окно, Единица секунды
     *
     * @return
     */
    int timeWindow();


}
Язык кода:javascript
копировать
package com.artisan.counter;

import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */

@Aspect
@Component
public class CounterRateLimitAspect {

    // Храните количество запросов, соответствующих каждому методу.
    private Map<String, AtomicInteger> REQUEST_COUNT = new ConcurrentHashMap<>();

    // Сохраните временную метку каждого метода
    private Map<String, Long> REQUEST_TIMESTAMP = new ConcurrentHashMap<>();

    /**
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.artisan.counter.CounterRateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {

        // Получить информацию об аннотации
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        CounterRateLimit annotation = method.getAnnotation(CounterRateLimit.class);

        // Получить аннотированные параметры
        int maxRequest = annotation.maxRequest();
        long timeWindowInMillis = TimeUnit.SECONDS.toMillis(annotation.timeWindow());

        // Получить имя метода
        String methodName = method.toString();

        // Инициализируйте счетчики и временные метки
        AtomicInteger count = REQUEST_COUNT.computeIfAbsent(methodName, x -> new AtomicInteger(0));
        long startTime = REQUEST_TIMESTAMP.computeIfAbsent(methodName, x -> System.currentTimeMillis());

        // Получить текущее время
        long currentTimeMillis = System.currentTimeMillis();

        // Решение: еслитекущее времявневременное окно, затем сброс
        if (currentTimeMillis - startTime > timeWindowInMillis) {
            count.set(0);
            REQUEST_TIMESTAMP.put(methodName, currentTimeMillis);
        }

        // Атомарно увеличить счетчик и проверить его значение
        if (count.incrementAndGet() > maxRequest) {
            // Если максимальное количество запросов превышено, уменьшите счетчик и сообщите об ошибке.
            count.decrementAndGet();
            throw new RuntimeException("Too many requests, please try again later.");
        }

        // оригинальное выполнение метода
        return joinPoint.proceed();
    }
}
Язык кода:javascript
копировать
package com.artisan.counter;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@RestController
public class CounterRateLimitController {

    /**
     * раз в секунду
     *
     * @return
     */
    @GetMapping("/counter")
    @CounterRateLimit(maxRequest = 2, timeWindow = 2)
    public ResponseEntity counter() {
        System.out.println("Request Coming Coming....");
        return ResponseEntity.ok("Artisan OK");
    }
}

тест

Запустите проект, Интерфейс доступа http://localhost:8080/counte

Преимущества и недостатки

Преимущество простого счетчикалгоритма в том, что он прост, но и недостатки очевидны:

  • пограничные проблемы:потому что计数устройствода在时间оконные концы时重置的,Если в системе очень большой объем запросов,Могут быть проблемы с критической точкой временного окна.,т.е. наблюдается всплеск запросов ближе к концу окна,И запросов в начале окна меньше,В результате системные ресурсы не могут быть эффективно использованы.
  • Недостаточная пропускная способность для обработки внезапного увеличения трафика:Невозможно эффективно справляться с внезапными скачками напряженияпоток,потому что его пределы фиксированы,Его нельзя настроить в зависимости от загрузки системы в реальном времени. Чтобы решить простой Для решения этих задач счетчика можно использовать более сложное Ограничение. алгоритм тока, такой как счетчик скользящего окна, алгоритм дырявого ведра、Алгоритм сегмента токенов и др. Эти алгоритмы могут более плавно и эффективно контролировать частоту запросов, повышая стабильность и надежность системы.

В этом примере есть метод счетчика, помеченный @CounterRateLimit. Согласно параметрам аннотации, этот метод можно вызвать только дважды в течение 2-секундного временного окна. Если в течение 2 секунд поступят дополнительные вызовы, эти дополнительные вызовы будут ограничены и будет возвращено сообщение об ошибке.

Предположим, что период времени составляет 1 минуту, при этом за каждый период времени может быть отправлено не более 100 запросов. Существует крайняя ситуация: когда 100 запросов собираются в 10:00:58, порог достигается, когда 100 запросов собираются в 10:01:02, порог достигается. В такой ситуации 200 запросов будут обработаны всего за 4 секунды, тогда как в остальное время ток будет ограничен.


алгоритм скользящего окна

принцип

алгоритм скользящего окнадавыполнить Ограничение тока Распространенный метод,Он контролирует количество запросов в единицу времени, поддерживая временное окно.,Это защищает систему от неожиданных или злонамеренных атак. Его основной принцип — количество запросов в пределах статистического временного окна.,И решите, разрешать ли прохождение новых запросов на основе заданного порога.

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

Например: предположим, что временное окно составляет 10 с, лимит запросов равен 3, первый запрос инициируется в 10:00:00, второй инициируется в 10:00:05, а третий инициируется в 10:00: 11, затем счетчик. Время начала следующего окна стратегии — 10:00:11, а скользящего окна — 10:00:05. Вот почему скользящее окно может уменьшить влияние проблем с критичностью, но не может полностью устранить их.

выполнить

Язык кода:javascript
копировать
package com.artisan.slidingwindow;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SlidingWindowRateLimit {


    /**
     * Количество запросов
     *
     * @return
     */
    int maxRequest();


    /**
     * временное окно, Единица секунды
     *
     * @return
     */
    int timeWindow();

}
Язык кода:javascript
копировать
package com.artisan.slidingwindow;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */

@Aspect
@Component
public class SlidingWindowRateLimitAspect {

    /**
     * использовать ConcurrentHashMap Очередь, содержащая временные метки запроса для каждого метода.
     */
    private final ConcurrentHashMap<String, ConcurrentLinkedQueue<Long>> REQUEST_TIMES_MAP = new ConcurrentHashMap<>();


    @Around("@annotation(com.artisan.slidingwindow.SlidingWindowRateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SlidingWindowRateLimit rateLimit = method.getAnnotation(SlidingWindowRateLimit.class);
        
        // Максимально разрешенное количество запросов
        int requests = rateLimit.maxRequest();
        // Размер скользящего окна (секунды)
        int timeWindow = rateLimit.timeWindow();

        // Получить имя метод указанная строка
        String methodName = method.toString();
        // Если очередь временных меток запроса для текущего метода не существует, инициализируйте новую очередь.
        ConcurrentLinkedQueue<Long> requestTimes = REQUEST_TIMES_MAP.computeIfAbsent(methodName,
                k -> new ConcurrentLinkedQueue<>());

        // текущее время
        long currentTime = System.currentTimeMillis();
        // Вычислить начальную временную метку временного окна
        long thresholdTime = currentTime - TimeUnit.SECONDS.toMillis(timeWindow);

        // Этот фрагмент кода представляет собой скользящее окно. Ключевая часть алгоритма тока, его функция — удалить временную метку запроса перед текущим скользящим окном. Это сделано для того, чтобы в окне сохранялись только записи запросов за самый последний период времени.
        // requestTimes.isEmpty() Условие проверки того, пуста ли очередь. Если очередь пуста, это означает, что записей запросов нет и операция удаления не требуется.
        // requestTimes.peek() < thresholdTime заключается в проверке того, находится ли временная метка в начале очереди раньше, чем время начала скользящего окна. Если это так, это означает, что эта временная метка больше не находится в текущем временном окне и ее следует удалить.
        while (!requestTimes.isEmpty() && requestTimes.peek() < thresholdTime) {
            // Удалить метку времени истечения срока действия из головы очереди.
            requestTimes.poll();
        }

        // 检查текущее время窗口内的请求次数да否超过限制
        if (requestTimes.size() < requests) {
            // Лимит не превышен, и текущее время запроса фиксируется.
            requestTimes.add(currentTime);
            return joinPoint.proceed();
        } else {
            // Лимит превышен, бросьте Ограничение ток ненормальный
            throw new RuntimeException("Too many requests, please try again later.");
        }
    }


}
Язык кода:javascript
копировать
package com.artisan.slidingwindow;

import com.artisan.leakybucket.LeakyBucketRateLimit;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@RestController
public class SlidingWindowController {

    @GetMapping("/slidingWindow")
    @SlidingWindowRateLimit(maxRequest = 2, timeWindow = 2)
    public ResponseEntity slidingWindow() {
        return ResponseEntity.ok("artisan  slidingWindow ");
    }
}

тест

Преимущества и недостатки

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

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

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

принцип

В Дырявом Bucketалгоритмсередина,Контейнеры имеют фиксированную вместимость.,Похоже на утечкуемкость ковша。Данные поступают в контейнер с фиксированной скоростью,если контейнер полон,Лишние данные переполнятся. Данные в контейнере вытекают снизу с постоянной скоростью.,Подобно каплям воды в дырявом ведре. Если в контейнере недостаточно данных для удовлетворения скорости оттока,Он будет ждать, пока не накопится достаточно данных для вывода. Это обеспечивает плавное управление потоком данных.

выполнить

Язык кода:javascript
копировать
package com.artisan.leakybucket;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LeakyBucketRateLimit {

    /**
     * емкость ковша
     *
     * @return
     */
    int capacity();

    /**
     * Скорость воронки, обычно в секундах
     *
     * @return
     */
    int leakRate();
}
Язык кода:javascript
копировать
package com.artisan.leakybucket;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Aspect
@Component
public class LeakyBucketRateLimitAspect {


    @Around("@annotation(com.artisan.leakybucket.LeakyBucketRateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LeakyBucketRateLimit leakyBucketRateLimit = method.getAnnotation(LeakyBucketRateLimit.class);

        int capacity = leakyBucketRateLimit.capacity();
        int leakRate = leakyBucketRateLimit.leakRate();

        // Подпись метода как уникальный идентификатор
        String methodKey = method.toString();

        LeakyBucketLimiter limiter = LeakyBucketLimiter.createLimiter(methodKey, capacity, leakRate);
        if (!limiter.tryAcquire()) {
            // Лимит превышен, бросьте Ограничение ток ненормальный
            throw new RuntimeException("Too many requests, please try again later.");
        }

        return joinPoint.proceed();
    }
}
Язык кода:javascript
копировать
package com.artisan.leakybucket;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class LeakyBucketLimiter {
    /**
     * емкость ковша
     */
    private final int capacity;
    /**
     * Скорость утечки дырявого ведра, количество воды, вытекающей за единицу времени.
     */
    private final int leakRate;
    /**
     * Текущее количество воды в ведре
     */
    private volatile int water = 0;
    /**
     * Время последней утечки
     */
    private volatile long lastLeakTime = System.currentTimeMillis();

    /**
     * дырявый контейнер-ведро
     */
    private static final ConcurrentHashMap<String, LeakyBucketLimiter> LIMITER_MAP = new ConcurrentHashMap<>();

    /**
     * статический фабричный метод,Убедитесь таким же образомиспользовать Тот же экземпляр дырявого ведра
     *
     * @param methodKey имя метода
     * @param capacity
     * @param leakRate
     * @return
     */
    public static LeakyBucketLimiter createLimiter(String methodKey, int capacity, int leakRate) {
        return LIMITER_MAP.computeIfAbsent(methodKey, k -> new LeakyBucketLimiter(capacity, leakRate));
    }

    private LeakyBucketLimiter(int capacity, int leakRate) {
        this.capacity = capacity;
        this.leakRate = leakRate;
    }

    /**
     * Попробуйте получить разрешение (попробуйте to acquire a разрешено), если приобретение прошло успешно, оно возвращает true, в противном случае — false.
     *
     * @return
     */
    public boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();
        synchronized (this) {
            // Рассчитайте последнюю утечку воды по течению время интервал времени
            long leakDuration = currentTime - lastLeakTime;
            // Если временной интервал больше или равен 1 секунде, это означает, что из дырявого ведра вытекло определенное количество воды.
            if (leakDuration >= TimeUnit.SECONDS.toMillis(1)) {
                // Рассчитайте количество вытекшей воды.
                long leakQuantity = leakDuration / TimeUnit.SECONDS.toMillis(1) * leakRate;
                // После того, как из дырявого ведра протекает вода, количество воды в ведре обновляется, но оно не может быть меньше 0.
                water = (int) Math.max(0, water - leakQuantity);
                lastLeakTime = currentTime;
            }
            // Определите, меньше ли количество воды в ведре, чем емкость. Если да, то можно продолжать доливать воду (эквивалентно получению жетона).
            if (water < capacity) {
                water++;
                return true;
            }
        }
        // Если ведро заполнено, получить токен не удастся.
        return false;
    }
}
Язык кода:javascript
копировать
package com.artisan.leakybucket;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@RestController
public class LeakBucketController {

    /**
     * 
     *
     * @return
     */
    @GetMapping("/leakyBucket")
    @LeakyBucketRateLimit(capacity = 10, leakRate = 2)
    public ResponseEntity leakyBucket() {
        return ResponseEntity.ok("leakyBucket test ok!");
    }
}

тест

Преимущества и недостатки

алгоритм дырявого ведраи Алгоритм сегмента Самая очевидная разница между токенами – это Алгоритм. сегмента токенов допускает поток определенной степени выброса. Поскольку алгоритм по умолчанию сегмента токенов,Чтобы забрать жетон, не потребуется время.,То есть,Предположим, что в ведре 100 токенов.,Тогда 100 запросов можно будет разрешить мгновенно.

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

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

использоватьGuavaВходит в комплектRateLimiterвыполнить

Язык кода:javascript
копировать
<!-- guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.1-jre</version>
</dependency>

принцип

Алгоритм сегмента токенов описывается через образную метафору: представьте, что есть ведро.,Ведро содержит определенное количество жетонов. Система добавляет токены в корзину по фиксированной ставке.,Перед отправкой каждый пакет должен получить токен из корзины. Если в ведре достаточно жетонов,Пакет можно отправить немедленно, если в корзине нет токена;,Затем пакет данных должен подождать,Пока в ведре не будет достаточно жетонов.

Ключевые параметры:

  • Скорость генерации токенов:令牌被添加到桶середина的速率,Обычно выражается в токенах в секунду.
  • Емкость ковша:桶середина可以存放的最大令牌数,если ведро полное,Вновь добавленные токены отбрасываются или игнорируются.
  • Начальное количество токенов:Количество токенов в ведре в начале。

Алгоритм процесса:

  1. Токен добавлен:以固定的速率向桶середина添加令牌,Обычно эта скорость соответствует пределу пропускной способности сетевого интерфейса.
  2. Обработка запроса
    • Когда приходит пакет, система проверяет, достаточно ли токенов в корзине.
    • Если токенов достаточно, соответствующее количество токенов удаляется из корзины и пакету разрешается пройти.
    • Если в корзине недостаточно токенов, пакет будет помечен как ожидающий или отброшен.
  3. Полная обработка ковша:если ведро полное,Вновь добавленные жетоны могут быть сброшены или засчитаны сверх количества жетонов.,Чтобы иметь возможность справиться с временными чрезвычайными ситуациями.

выполнить

Язык кода:javascript
копировать
package com.artisan.tokenbucket;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TokenBucketRateLimit {
    /**
     * Скорость генерации токенов (xx штук в секунду)
     *
     * @return
     */
    double permitsPerSecond();
}
Язык кода:javascript
копировать
package com.artisan.tokenbucket;

import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */

@Aspect
@Component
public class TokenBucketRateLimitAspect {

    // ИспользуйтеConcurrentHashMap для хранения Ограничения для каждого метода. токаустройство    private final ConcurrentHashMap<String, RateLimiter> limiters = new ConcurrentHashMap<>();

    // Совет по объему, используемый для добавления Ограничения до и после выполнения метода. токологика
    @Around("@annotation(com.artisan.tokenbucket.TokenBucketRateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        // Получить подпись метода, используемую для получения информации о методе.
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // Получить объект метода на основе сигнатуры метода
        Method method = signature.getMethod();
        // Получить ограничение из объекта метода токааннотация        TokenBucketRateLimit rateLimit = method.getAnnotation(TokenBucketRateLimit.class);

        // Получите количество токенов в секунду, определенное в аннотации.
        double permitsPerSecond = rateLimit.permitsPerSecond();

        // Получить имя метода,как Ограничение Уникальный идентификатор устройства тока
        String methodName = method.toString();
        // если Ограничение Ограничения для этого метода в кеше сервера нет. токар, создай новый
        RateLimiter rateLimiter = limiters.computeIfAbsent(methodName, k -> RateLimiter.create(permitsPerSecond));

        // Попытайтесь получить токен, если его можно получить, продолжите выполнение метода.
        if (rateLimiter.tryAcquire()) {
            return joinPoint.proceed();
        } else {
            // Если токен не может быть получен, создается исключение, информирующее пользователя о том, что запрос выполняется слишком часто.
            throw new RuntimeException("Too many requests, please try again later.");
        }
    }
}
Язык кода:javascript
копировать
package com.artisan.tokenbucket;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
@RestController
public class TokenBucketController {

    @GetMapping("/tokenBucket")
    @TokenBucketRateLimit(permitsPerSecond = 0.5)
    public ResponseEntity tokenBucket() {
        return ResponseEntity.ok("artisan token bucket");
    }
}

тест

Преимущества и недостатки

  • Плавное движение:проходить控制令牌生成的速率,Может сглаживать сетевой поток,Избегайте перегрузки сети, вызванной внезапными атаками.
  • гибкость:Может справиться с некоторыми чрезвычайными ситуациямипоток,Потому что ведро может временно хранить больше токенов, чем среднее количество.
  • легкийвыполнить:алгоритм相对Простой,Легко устанавливается на аппаратное или программное обеспечение.

краткое содержание

В реализации интерфейса Ограничение В текущей стратегии вам следует выбрать подходящее Ограничение, исходя из конкретных бизнес-сценариев и системных требований. токаалгоритмивыполнить Способ,Также обратите внимание на влияние стратегии «Ограничение тока» на пользовательский опыт.,Для защиты стабильной работы системы,Это не доставит особых хлопот законным пользователям.

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода