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

SpringBoot?實現(xiàn)流控的操作方法

 更新時間:2024年12月02日 11:13:33   作者:小小工匠  
本文介紹了限流算法的基本概念和常見的限流算法,包括計數(shù)器算法、漏桶算法和令牌桶算法,還介紹了如何在Spring?Boot中使用Guava庫和自定義注解以及AOP實現(xiàn)接口限流功能,感興趣的朋友一起看看吧

概述

限流 簡言之就是當請求達到一定的并發(fā)數(shù)或速率,就對服務(wù)進行等待、排隊、降級、拒絕服務(wù)等操作。

限流算法

我們先簡單捋一捋限流算法

Spring Boot接口限流的常用算法及特點

SpringBoot接口限流的實現(xiàn)方法小結(jié)

計數(shù)器限流

漏桶算法

把水比作是請求,漏桶比作是系統(tǒng)處理能力極限,水先進入到漏桶里,漏桶里的水按一定速率流出,當流出的速率小于流入的速率時,由于漏桶容量有限,后續(xù)進入的水直接溢出(拒絕請求),以此實現(xiàn)限流

令牌桶算法

可以簡單地理解為醫(yī)去銀行辦理業(yè)務(wù),只有拿到號以后才可以進行業(yè)務(wù)辦理。

系統(tǒng)會維護一個令牌(token)桶,以一個恒定的速度往桶里放入令牌(token),這時如果有請求進來想要被處理,則需要先從桶里獲取一個令牌(token),當桶里沒有令牌(token)可取時,則該請求將被拒絕服務(wù)。令牌桶算法通過控制桶的容量、發(fā)放令牌的速率,來達到對請求的限制。

V1.0

上 guava

  <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1-jre</version>
        </dependency>
package com.artisan.controller;
import com.artisan.annos.ArtisanLimit;
import com.google.common.util.concurrent.RateLimiter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Slf4j
@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {
    /**
     * 限流策略 : 1秒鐘1個請求
     */
    private final RateLimiter limiter = RateLimiter.create(1);
    private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @SneakyThrows
    @GetMapping("/test")
    public String testLimiter() {
        //500毫秒內(nèi),沒拿到令牌,就直接進入服務(wù)降級
        boolean tryAcquire = limiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        if (!tryAcquire) {
            log.warn("BOOM 服務(wù)降級,時間{}", LocalDateTime.now().format(dtf));
            return "系統(tǒng)繁忙,請稍后再試!";
        }
        log.info("獲取令牌成功,時間{}", LocalDateTime.now().format(dtf));
        return "業(yè)務(wù)處理成功";
    }

我們可以看到RateLimiter的2個核心方法:create()、tryAcquire()

  • acquire() 獲取一個令牌, 改方法會阻塞直到獲取到這一個令牌, 返回值為獲取到這個令牌花費的時間
  • acquire(int permits) 獲取指定數(shù)量的令牌, 該方法也會阻塞, 返回值為獲取到這 N 個令牌花費的時間
  • tryAcquire() 判斷時候能獲取到令牌, 如果不能獲取立即返回 false
  • tryAcquire(int permits) 獲取指定數(shù)量的令牌, 如果不能獲取立即返回 false
  • tryAcquire(long timeout, TimeUnit unit) 判斷能否在指定時間內(nèi)獲取到令牌, 如果不能獲取立即返回 false
  • tryAcquire(int permits, long timeout, TimeUnit unit) 同上

測試一下

V2.0 自定義注解+AOP實現(xiàn)接口限流

1.0的功能實現(xiàn)了,但是業(yè)務(wù)代碼和限流代碼混在一起,非常的不美觀。

搞依賴

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

搞自定義限流注解

package com.artisan.annos;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ArtisanLimit {
    /**
     * 資源的key,唯一
     * 作用:不同的接口,不同的流量控制
     */
    String key() default "";
    /**
     * 最多的訪問限制次數(shù)
     */
    double permitsPerSecond();
    /**
     * 獲取令牌最大等待時間
     */
    long timeout();
    /**
     * 獲取令牌最大等待時間,單位(例:分鐘/秒/毫秒) 默認:毫秒
     */
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;
    /**
     * 得不到令牌的提示語
     */
    String message() default "系統(tǒng)繁忙,請稍后再試.";
}

搞AOP

使用AOP切面攔截限流注解

package com.artisan.aop;
import com.artisan.annos.ArtisanLimit;
import com.artisan.resp.ResponseCode;
import com.artisan.resp.ResponseData;
import com.artisan.utils.WebUtils;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Slf4j
@Aspect
@Component
public class ArtisanLimitAop {
    /**
     * 不同的接口,不同的流量控制
     * map的key為 ArtisanLimit.key
     */
    private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();
    @Around("@annotation(com.artisan.annos.ArtisanLimit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //拿ArtisanLimit的注解
        ArtisanLimit limit = method.getAnnotation(ArtisanLimit.class);
        if (limit != null) {
            //key作用:不同的接口,不同的流量控制
            String key = limit.key();
            RateLimiter rateLimiter = null;
            //驗證緩存是否有命中key
            if (!limitMap.containsKey(key)) {
                // 創(chuàng)建令牌桶
                rateLimiter = RateLimiter.create(limit.permitsPerSecond());
                limitMap.put(key, rateLimiter);
                log.info("新建了令牌桶={},容量={}", key, limit.permitsPerSecond());
            }
            rateLimiter = limitMap.get(key);
            // 拿令牌
            boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit());
            // 拿不到命令,直接返回異常提示
            if (!acquire) {
                log.warn("令牌桶={},獲取令牌失敗", key);
                this.responseFail(limit.message());
                return null;
            }
        }
        return joinPoint.proceed();
    }
    /**
     * 直接向前端拋出異常
     *
     * @param msg 提示信息
     */
    private void responseFail(String msg) {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ResponseData<Object> resultData = ResponseData.fail(ResponseCode.LIMIT_ERROR.getCode(), msg);
        WebUtils.writeJson(response, resultData);
    }
}

用上驗證

   @GetMapping("/test2")
    @ArtisanLimit(key = "testLimit2", permitsPerSecond = 1, timeout = 500, timeunit = TimeUnit.MILLISECONDS, message = "test2 當前排隊人數(shù)較多,請稍后再試!")
    public String test2() {
        log.info("令牌桶test2獲取令牌成功");
        return "test2 ok";
    }

源碼

https://github.com/yangshangwei/boot2

到此這篇關(guān)于SpringBoot 實現(xiàn)流控的操作方法的文章就介紹到這了,更多相關(guān)SpringBoot流控內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Spire.XLS快速創(chuàng)建與讀取Excel的實戰(zhàn)技巧

    Java Spire.XLS快速創(chuàng)建與讀取Excel的實戰(zhàn)技巧

    在現(xiàn)代軟件開發(fā)中,Excel 文檔的管理和操作是一個常見的需求,,本文將詳細介紹如何使用 Spire.XLS for Java 庫,以便輕松地讀寫 Excel 文檔,感興趣的小伙伴可以了解下
    2025-12-12
  • Springboot如何實現(xiàn)對配置文件中的明文密碼加密

    Springboot如何實現(xiàn)對配置文件中的明文密碼加密

    這篇文章主要介紹了Springboot如何實現(xiàn)對配置文件中的明文密碼加密問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 使用SpringMVC的@Validated注解驗證的實現(xiàn)

    使用SpringMVC的@Validated注解驗證的實現(xiàn)

    這篇文章主要介紹了使用SpringMVC的@Validated注解驗證的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • IDEA集成MyBatis Generator插件的使用

    IDEA集成MyBatis Generator插件的使用

    這篇文章主要介紹了IDEA集成MyBatis Generator插件的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • Java Scanner類的使用示例

    Java Scanner類的使用示例

    這篇文章主要介紹了Java Scanner類的使用示例,幫助大家更好的理解和學習java,感興趣的朋友可以了解下
    2020-08-08
  • 詳解Java數(shù)組的定義和聲明方法

    詳解Java數(shù)組的定義和聲明方法

    在Java開發(fā)中,數(shù)組是最常用的數(shù)據(jù)結(jié)構(gòu)之一,因此,深入了解Java數(shù)組的定義和聲明是非常必要的,本文將詳細介紹Java數(shù)組的定義和聲明方法,以及其在實際開發(fā)中的應(yīng)用場景、優(yōu)缺點等方面,需要的朋友可以參考下
    2023-11-11
  • Java 中 getClass() 方法的使用與原理深入分析(對象類型信息)

    Java 中 getClass() 方法的使用與原理深入分析(對象類型信息)

    在 Java 編程中,getClass() 是一個非常重要的方法,它用于獲取對象的運行時類信息,無論是調(diào)試代碼、反射操作,還是類型檢查,getClass() 都扮演著關(guān)鍵角色,本文將深入探討 getClass() 的使用方法、底層原理以及實際應(yīng)用場景,感興趣的朋友一起看看吧
    2024-12-12
  • SpringBoot操作spark處理hdfs文件的操作方法

    SpringBoot操作spark處理hdfs文件的操作方法

    本文介紹了如何使用Spring Boot操作Spark處理HDFS文件,包括導入依賴、配置Spark信息、編寫Controller和Service處理地鐵數(shù)據(jù)、運行項目以及觀察Spark和HDFS的狀態(tài),感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • Java中的Semaphore信號量詳解

    Java中的Semaphore信號量詳解

    這篇文章主要介紹了Java中的Semaphore信號量詳解,Semaphore(信號量)是用來控制同時訪問特定資源的線程數(shù)量,通過協(xié)調(diào)各個線程以保證合理地使用公共資源,需要的朋友可以參考下
    2023-12-12
  • Java批量向PDF文件中添加圖像水印實現(xiàn)細節(jié)

    Java批量向PDF文件中添加圖像水印實現(xiàn)細節(jié)

    這篇文章主要為大家介紹了Java批量向PDF文件中添加圖像水印實現(xiàn)細節(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05

最新評論

温宿县| 灵山县| 富锦市| 文安县| 吉木乃县| 桃园市| 桐梓县| 胶州市| 峨眉山市| 永安市| 建昌县| 牙克石市| 凤凰县| 长葛市| 扬州市| 建阳市| 烟台市| 鄢陵县| 闽侯县| 阿拉善右旗| 长宁县| 佛教| 太白县| 从化市| 定南县| 玉田县| 兴业县| 九龙县| 田林县| 东至县| 云南省| 青海省| 东乡| 开化县| 汨罗市| 平凉市| 大余县| 南宫市| 韶山市| 潼南县| 广汉市|