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

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

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

什么是令牌桶限流?

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

開(kāi)始我們的實(shí)現(xiàn)

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

我們首先需要?jiǎng)?chuàng)建一個(gè)自定義注解,用于標(biāo)識(shí)需要進(jìn)行限流的方法。這個(gè)注解可以命名為@RateLimit,它可以帶有以下幾個(gè)參數(shù)

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

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

接下來(lái),我們需要編寫(xiě)一個(gè)類(lèi)來(lái)處理限流邏輯。這個(gè)類(lèi)可以命名為RateLimitAspect,它將會(huì)掃描所有被@RateLimit注解標(biāo)記的方法,并在必要時(shí)進(jìn)行限流。

/**
 * 令牌桶限流
 *
 * @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();
            // 如果生成的令牌超過(guò)的桶最大容量,那么令牌數(shù)取桶最大容量
            var2.setTokens(Math.min(var2.getTokens() + addTokens, v.capacity()));
            var2.setLastRefillTime(nowTime);
            double remain = var2.getTokens() - 1;
            if (remain < 0) {
                throw new BusinessException("操作太頻繁,請(qǐng)稍后重試", 42901);
            }
            var2.setTokens(remain);
            long timeout = (long) Math.ceil(v.capacity() / v.rate());// redis過(guò)期時(shí)間設(shè)置大于 容量/速率
            redisTemplate.opsForValue().set(k, var2, timeout, TimeUnit.SECONDS);
        });
        return joinPoint.proceed();
    }
}

SpEL解析工具類(lèi)

@Component
public class SpELUtil {
    /**
     * 獲取表達(dá)式中的參數(shù)值
     *
     * @param expr      表達(dá)式
     * @param joinPoint 切點(diǎn)
     * @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ù)名稱(chēng)
     *
     * @param joinPoint 切點(diǎn)
     * @return 參數(shù)名稱(chēng)
     */
    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)在,我們可以在需要進(jìn)行限流的方法上使用@RateLimited注解,指定相應(yīng)的限流速率。

示例1: 限制了令牌桶容量10,每10秒生成一個(gè)令牌,限制對(duì)象為當(dāng)前用戶(hù)。

@Api(tags = "項(xiàng)目服務(wù)")
@Validated
@Slf4j
@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
public class ProjectController {
    @Resource
    private IProjectService projectService;
    @ApiOperation("創(chuàng)建項(xià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秒生成一個(gè)令牌,限制對(duì)象為該項(xiàng)目。

@Slf4j
@RestController
@RequestMapping("/api/v1")
public class ProjectController {
    @Resource
    private IProjectService projectService;
    @ApiOperation("數(shù)據(jù)導(dǎo)出")
    @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();
    }
}

總結(jié)

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

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

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

相關(guān)文章

  • java加載properties文件的六種方法總結(jié)

    java加載properties文件的六種方法總結(jié)

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

    Java Unsafe學(xué)習(xí)筆記分享

    今天小編就為大家分享一篇Java Unsafe學(xué)習(xí)筆記,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    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ū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-04-04
  • 如何使用spring gateway微服務(wù)網(wǎng)關(guān)(基本用法)

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

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

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

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

    Java獲取當(dāng)前時(shí)間戳案例詳解

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

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

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

    java部署項(xiàng)目流程幾種常見(jiàn)方式詳解

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

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

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

    Spring Boot 中使用cache緩存的方法

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

最新評(píng)論

巩义市| 名山县| 广水市| 台江县| 赤水市| 偏关县| 偃师市| 泰顺县| 康马县| 巴彦县| 博爱县| 哈巴河县| 宜兰市| 如东县| 吉水县| 鄂尔多斯市| 台北县| 来凤县| 景东| 岢岚县| 阆中市| 喀喇沁旗| 扬州市| 呼和浩特市| 泊头市| 贵南县| 沅江市| 东宁县| 乌拉特前旗| 措勤县| 南乐县| 昌宁县| 余姚市| 荣成市| 民和| 比如县| 惠安县| 蒲城县| 苗栗市| 奇台县| 天门市|