优化接口设计接口限流策略(转)

发布时间:2024年01月12日

一、前言

 开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是遵循大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,掌握了一些实用的技巧。

二、业务场景

1. API速率限制

对外提供的API接口可能需要限制每个用户或每个IP地址在单位时间内的访问次数,以防止滥用或过载。
2. 网站流量控制

对于高流量的网站,为了防止瞬时访问量过大导致服务器压力过重,可以通过限流保护系统稳定运行。
3. 秒杀活动

电商平台在进行秒杀活动时,可能会遭遇大量用户同时抢购,通过计数器限流算法可以有效地控制访问量,避免系统崩溃。
4. 微服务架构

在微服务架构中,限流可以防止某个服务因为突发的高流量而成为瓶颈,进而影响到整个系统的稳定性。
5. 分布式系统的互斥操作

在进行诸如分布式锁的操作时,限流算法可以避免过多的请求同时竞争资源,保证系统的公平性和效率。
6. 基础设施保护

对于数据库、缓存等基础设施服务,通过计数器限流可以避免过多的并发请求导致服务不可用。
7. 网络带宽控制

对于带宽有限的网络服务,限流算法可以用来确保带宽的合理分配,防止网络拥堵。

三、限流策略

1. 计数器

(1)代码

CounterRateLimit.java
package com.summo.demo.config.limitstrategy.counter;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CounterRateLimit {
    /**
     * 请求的数量
     *
     * @return
     */
    int requests();

    /**
     * 时间窗口,单位为秒
     *
     * @return
     */
    int timeWindow();
}
CounterRateLimitAspect.java
package com.summo.demo.config.limitstrategy.counter;

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

import com.summo.demo.exception.biz.BizException;
import com.summo.demo.model.response.ResponseCodeEnum;
import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
@Aspect
@Component
@Order(5)
public class CounterRateLimitAspect {
    /**
     * 用来存储每个方法请求计数的映射
     */
    private final ConcurrentHashMap<String, AtomicInteger> REQUEST_COUNT = new ConcurrentHashMap<>();
    /**
     * 来存储每个方法的时间戳的映射
     */
    private final ConcurrentHashMap<String, Long> TIMESTAMP = new ConcurrentHashMap<>();

    @Around("@annotation(com.summo.demo.config.limitstrategy.counter.CounterRateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取注解信息
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        CounterRateLimit counterRateLimit = method.getAnnotation(CounterRateLimit.class);

        //获取注解上配置的参数
        int maxRequests = counterRateLimit.requests();
        long windowSizeInMillis = TimeUnit.SECONDS.toMillis(counterRateLimit.timeWindow());

        // 获取方法的字符串表示,用作键值
        String methodName = method.toString();

        // 初始化计数器和时间戳
        AtomicInteger count = REQUEST_COUNT.computeIfAbsent(methodName, k -> new AtomicInteger(0));
        long startTime = TIMESTAMP.computeIfAbsent(methodName, k -> System.currentTimeMillis());

        // 获取当前时间
        long currentTimeMillis = System.currentTimeMillis();
        // 如果当前时间超出时间窗口,则重置计数器和时间戳
        if (currentTimeMillis - startTime > windowSizeInMillis) {
            // 原子地重置时间戳和计数器
            TIMESTAMP.put(methodName, currentTimeMillis);
            count.set(0);
        }

        // 原子地增加计数器并检查其值
        if (count.incr
文章来源:https://blog.csdn.net/xdpcxq1029/article/details/135557893
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。