最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Java自定義注解實現(xiàn)一個簡單的令牌桶限流器

 更新時間:2023年10月08日 08:30:59   作者:酸奶小肥陽  
限流是在分布式系統(tǒng)中常用的一種策略,它可以有效地控制系統(tǒng)的訪問流量,保證系統(tǒng)的穩(wěn)定性和可靠性,在本文中,我將介紹如何使用Java自定義注解來實現(xiàn)一個簡單的令牌桶限流器,需要的朋友可以參考下

什么是令牌桶限流?

令牌桶限流是一種常用的限流算法,它基于令牌桶的概念。在令牌桶中,令牌以固定的速率被生成并放置其中。當一個請求到達時,它必須獲取一個令牌才能繼續(xù)執(zhí)行,否則將被阻塞或丟棄。

開始我們的實現(xiàn)

第一步:創(chuàng)建一個自定義注解

我們首先需要創(chuàng)建一個自定義注解,用于標識需要進行限流的方法。這個注解可以命名為@RateLimit,它可以帶有以下幾個參數(shù)

  • rate: 表示該方法的限流速率,單位可以是每秒請求數(shù)(QPS)。
  • prefixKey: 針對不同方法上對同一個資源做限流的情況。
  • target: 限流的對象,默認使用spEl表達式對入?yún)⑦M行獲取
  • capacity: 令牌桶容量,滿了之后令牌不再增加
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    /**
     * key的前綴,默認取方法全限定名,除非我們在不同方法上對同一個資源做頻控,就自己指定
     */
    String prefixKey() default "";
    /**
     * 選擇目標類型: 1.EL,需要在spEl()中指定限流資源。2.USER,針對用戶進行限流
     */
    Target target() default Target.EL;
    /**
     * springEl表達式 指定頻控對象
     */
    String spEl() default "";
    /**
     * 令牌桶容量
     */
    double capacity() default 10;
    /**
     * 令牌生成速率 n/秒
     */
    double rate() default 1;
    enum Target {
        EL, USER
    }
}

第二步:實現(xiàn)限流邏輯

接下來,我們需要編寫一個類來處理限流邏輯。這個類可以命名為RateLimitAspect,它將會掃描所有被@RateLimit注解標記的方法,并在必要時進行限流。

/**
 * 令牌桶限流
 *
 * @date 2023/07/07
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class RateLimitAspect {
    @Resource
    private RedisTemplate<String, BucketLog> redisTemplate;
    @Resource
    private RbacUserService rbacUserService;
    private final SpELUtil spELUtil;
    @Around("@annotation(com.netease.fuxi.config.annotation.RateLimit)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        RateLimit[] annotations = method.getAnnotationsByType(RateLimit.class);
        var var1 = new HashMap<String, RateLimit>();
        for (int i = 0; i < annotations.length; i++) {
            RateLimit annotation = annotations[i];
            final String prefix = StringUtils.isBlank(annotation.prefixKey()) ?
                    method.getDeclaringClass() + "#" + method.getName() + ":index:" + i : annotation.prefixKey();
            String key = "";
            switch (annotation.target()) {
                case EL:
                    key = spELUtil.getArgValue(annotation.spEl(), joinPoint);
                    break;
                case USER:
                    key = rbacUserService.getCurrentUser();
            }
            var1.put(prefix + ":" + key, annotation);
        }
        var1.forEach((k, v) -> {
            var var2 = Boolean.TRUE.equals(redisTemplate.hasKey(k)) ? redisTemplate.opsForValue().get(k) :
                    new BucketLog(v.capacity(), Instant.now().getEpochSecond());
            long nowTime = Instant.now().getEpochSecond();
            double addTokens = (nowTime - var2.getLastRefillTime()) * v.rate();
            // 如果生成的令牌超過的桶最大容量,那么令牌數(shù)取桶最大容量
            var2.setTokens(Math.min(var2.getTokens() + addTokens, v.capacity()));
            var2.setLastRefillTime(nowTime);
            double remain = var2.getTokens() - 1;
            if (remain < 0) {
                throw new BusinessException("操作太頻繁,請稍后重試", 42901);
            }
            var2.setTokens(remain);
            long timeout = (long) Math.ceil(v.capacity() / v.rate());// redis過期時間設置大于 容量/速率
            redisTemplate.opsForValue().set(k, var2, timeout, TimeUnit.SECONDS);
        });
        return joinPoint.proceed();
    }
}

SpEL解析工具類

@Component
public class SpELUtil {
    /**
     * 獲取表達式中的參數(shù)值
     *
     * @param expr      表達式
     * @param joinPoint 切點
     * @return 參數(shù)值
     */
    public String getArgValue(String expr, JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = getParameterNames(joinPoint);
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        ExpressionParser parser = new SpelExpressionParser();
        return parser.parseExpression(expr).getValue(context, String.class);
    }
    /**
     * 獲取方法的參數(shù)名稱
     *
     * @param joinPoint 切點
     * @return 參數(shù)名稱
     */
    private String[] getParameterNames(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] parameterNames = signature.getParameterNames();
        if (parameterNames == null || parameterNames.length == 0) {
            ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
            parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
        }
        return parameterNames;
    }
}

第三步:在方法上使用@RateLimit注解

現(xiàn)在,我們可以在需要進行限流的方法上使用@RateLimited注解,指定相應的限流速率。

示例1: 限制了令牌桶容量10,每10秒生成一個令牌,限制對象為當前用戶。

@Api(tags = "項目服務")
@Validated
@Slf4j
@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
public class ProjectController {
    @Resource
    private IProjectService projectService;
    @ApiOperation("創(chuàng)建項目")
    @PostMapping("/project")
    @RateLimit(capacity = 10, rate = 0.1, target = RateLimit.Target.USER)
    public Result<ProjectVO> createProject() {
        ProjectVO projectVO = projectService.createProject();
        return Result.ok(projectVO);
    }
}

示例2: 限制了令牌桶容量1,每2秒生成一個令牌,限制對象為該項目。

@Slf4j
@RestController
@RequestMapping("/api/v1")
public class ProjectController {
    @Resource
    private IProjectService projectService;
    @ApiOperation("數(shù)據(jù)導出")
    @PostMapping("/project/{projectId}/export")
    @RateLimit(capacity = 1, rate = 0.5, spEl = "#projectId")
    public Result<Void> export(@PathVariable String projectId,
                               @RequestBody @Valid ExportDTO exportDTO) {
        projectService.export(projectId, exportDTO);
        return Result.ok();
    }
}

總結

通過使用Java自定義注解,我們成功地實現(xiàn)了一個簡單的令牌桶限流器。這個限流器可以方便地應用于需要對訪問速率進行控制的方法中,保證系統(tǒng)的穩(wěn)定性和可靠性。

在實際項目中,我們可以根據(jù)需求對限流器進行進一步地擴展和優(yōu)化,以滿足不同場景下的限流需求。希望本文對你理解和實現(xiàn)限流算法有所幫助!

到此這篇關于基于Java自定義注解實現(xiàn)一個令牌桶限流的文章就介紹到這了,更多相關Java實現(xiàn)令牌桶限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java加載properties文件的六種方法總結

    java加載properties文件的六種方法總結

    這篇文章主要介紹了java加載properties文件的六種方法總結的相關資料,需要的朋友可以參考下
    2017-05-05
  • Java Unsafe學習筆記分享

    Java Unsafe學習筆記分享

    今天小編就為大家分享一篇Java Unsafe學習筆記,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot中application.properties、application.yaml、application.yml區(qū)別

    SpringBoot中application.properties、application.yaml、applicati

    本文主要介紹了SpringBoot中application.properties、application.yaml、application.yml區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-04-04
  • 如何使用spring gateway微服務網(wǎng)關(基本用法)

    如何使用spring gateway微服務網(wǎng)關(基本用法)

    本文介紹spring gateway的使用,包括配置文件的使用和調(diào)試跟蹤,讓大家了解spring gateway的基本用法,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • java基于數(shù)據(jù)庫實現(xiàn)全局唯一ID的示例

    java基于數(shù)據(jù)庫實現(xiàn)全局唯一ID的示例

    本文主要介紹了java基于數(shù)據(jù)庫實現(xiàn)全局唯一ID的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • Java獲取當前時間戳案例詳解

    Java獲取當前時間戳案例詳解

    這篇文章主要介紹了Java獲取當前時間戳案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 基于spring?@Cacheable?注解的spel表達式解析執(zhí)行邏輯

    基于spring?@Cacheable?注解的spel表達式解析執(zhí)行邏輯

    這篇文章主要介紹了spring?@Cacheable?注解的spel表達式解析執(zhí)行邏輯,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • java部署項目流程幾種常見方式詳解

    java部署項目流程幾種常見方式詳解

    部署Java項目通常有幾種常見的方法,具體選擇哪種方法取決于項目需求、資源、團隊習慣等因素,這篇文章主要介紹了java部署項目流程幾種常見方式,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-06-06
  • IDEA配置Tomcat本地路徑實現(xiàn)過程

    IDEA配置Tomcat本地路徑實現(xiàn)過程

    文章介紹了在將項目從Eclipse遷移到IntelliJ IDEA時,配置Tomcat虛擬路徑與本地路徑的過程,通過修改Tomcat的server.xml文件和IDEA的運行配置,實現(xiàn)了成功部署和運行
    2026-02-02
  • Spring Boot 中使用cache緩存的方法

    Spring Boot 中使用cache緩存的方法

    Spring Cache是Spring針對Spring應用,給出的一整套應用緩存解決方案。下面小編給大家?guī)砹薙pring Boot 中使用cache緩存的方法,感興趣的朋友參考下吧
    2018-01-01

最新評論

珲春市| 肇东市| 巩留县| 福海县| 榆中县| 百色市| 筠连县| 长宁区| 丹凤县| 闽侯县| 河曲县| 汕尾市| 泰顺县| 汕尾市| 新和县| 冕宁县| 秦皇岛市| 芒康县| 鞍山市| 措勤县| 甘南县| 大余县| 湘西| 丹巴县| 大石桥市| 遂宁市| 招远市| 普格县| 阿坝县| 遵义市| 荆门市| 含山县| 游戏| 泸定县| 洞口县| 师宗县| 万州区| 枞阳县| 永康市| 旬邑县| 白山市|