不依赖SpringBoot一个注解实现接口限流、防抖、防重

29 阅读3分钟

概述

限流

限制在一定时间内允许访问接口的次数,防止过度请求对系统造成压力。

防抖

控制接口调用的频率,确保短时间内的多次调用合并为一次实际处理。

防重

防止重复请求对系统的影响,确保同一个请求只被处理一次。

实现注解和功能

创建限流注解

我们首先定义一个注解 @RateLimit,用于标识需要限流的方法。

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    // 限流的时间窗口,单位是秒
    int windowSeconds() default 60;
    // 时间窗口内允许的最大请求次数
    int maxRequests() default 100;
}

创建限流处理器

我们实现一个限流处理器,通过在方法调用时检查是否超出限制。

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

public class RateLimitProcessor {
    private static final ConcurrentHashMap<String, RequestLimit> requestLimits = new ConcurrentHashMap<>();

    public static boolean isAllowed(Method method) {
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        // 如果没有注解,则不进行限流
        if (rateLimit == null) {
            return true; 
        }

        String key = method.getDeclaringClass().getName() + "." + method.getName();
        RequestLimit limit = requestLimits.computeIfAbsent(key, k -> new RequestLimit(rateLimit.windowSeconds(), rateLimit.maxRequests()));
        return limit.isAllowed();
    }

    private static class RequestLimit {
        private final int windowSeconds;
        private final int maxRequests;
        private final AtomicInteger requestCount;
        private long windowStart;

        RequestLimit(int windowSeconds, int maxRequests) {
            this.windowSeconds = windowSeconds;
            this.maxRequests = maxRequests;
            this.requestCount = new AtomicInteger();
            this.windowStart = System.currentTimeMillis();
        }

        synchronized boolean isAllowed() {
            long currentTime = System.currentTimeMillis();
            if (currentTime - windowStart > windowSeconds * 1000) {
                windowStart = currentTime;
                requestCount.set(0);
            }
            return requestCount.incrementAndGet() <= maxRequests;
        }
    }
}

创建防抖注解

定义一个注解 @Debounce 用于标识需要防抖处理的方法。

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Debounce {
    // 防抖的时间窗口,单位是毫秒
    long windowMillis() default 300;
}

创建防抖处理器

实现一个防抖处理器,确保在短时间内的多次调用被合并。

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DebounceProcessor {
    private static final ConcurrentHashMap<String, DebounceTask> debounceTasks = new ConcurrentHashMap<>();
    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public static void schedule(Method method, Runnable task, long debounceMillis) {
        String key = method.getDeclaringClass().getName() + "." + method.getName();
        DebounceTask debounceTask = debounceTasks.computeIfAbsent(key, k -> new DebounceTask(task, debounceMillis));
        debounceTask.schedule();
    }

    private static class DebounceTask {
        private final Runnable task;
        private final long debounceMillis;
        private volatile long lastExecutionTime;

        DebounceTask(Runnable task, long debounceMillis) {
            this.task = task;
            this.debounceMillis = debounceMillis;
            this.lastExecutionTime = 0;
        }

        synchronized void schedule() {
            long currentTime = System.currentTimeMillis();
            long delay = debounceMillis - (currentTime - lastExecutionTime);
            if (delay <= 0) {
                lastExecutionTime = currentTime;
                scheduler.submit(task);
            } else {
                lastExecutionTime = currentTime + delay;
                scheduler.schedule(() -> {
                    if (currentTime >= lastExecutionTime) {
                        task.run();
                    }
                }, delay, TimeUnit.MILLISECONDS);
            }
        }
    }
}

创建防重注解

定义一个注解 @Deduplicate 用于标识需要防重处理的方法。

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Deduplicate {
}

创建防重处理器

实现一个防重处理器,确保同一请求不会被重复处理。

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

public class DeduplicationProcessor {
    private static final ConcurrentHashMap<String, Object> requestCache = new ConcurrentHashMap<>();

    public static boolean isUnique(Method method, Object requestKey) {
        Deduplicate deduplicate = method.getAnnotation(Deduplicate.class);
        if (deduplicate == null) {
            return true; // 如果没有注解,则不进行防重处理
        }

        return requestCache.putIfAbsent(requestKey.toString(), new Object()) == null;
    }
}

集成处理器

我们需要在实际调用接口时,集成以上的限流、防抖和防重处理器。可以在方法调用前进行相应的检查或处理。

import java.lang.reflect.Method;

public class ApiService {

    public void someMethod() {
        Method method = getMethod("someMethod");

        // 限流检查
        if (!RateLimitProcessor.isAllowed(method)) {
            throw new RuntimeException("Rate limit exceeded");
        }

        // 防抖处理
        DebounceProcessor.schedule(method, this::performAction, 300);

        // 防重处理
        Object requestKey = generateRequestKey();
        if (!DeduplicationProcessor.isUnique(method, requestKey)) {
            return; // 如果请求不是唯一的,则不处理
        }

        performAction();
    }

    private void performAction() {
        // 实际业务逻辑
    }

    private Method getMethod(String methodName) {
        try {
            return this.getClass().getMethod(methodName);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private Object generateRequestKey() {
        // 生成请求唯一标识的逻辑
        return new Object();
    }
}

总结

文章描述了如何通过自定义注解和基础 Java 代码实现接口限流、防抖和防重机制通过这种方式,我们可以在不依赖 SpringBoot 或其他框架的情况下,灵活地对接口进行控制,提高系统的稳定性和用户体验。