springboot 整合 google.guava 限流 / Redisson


 

一. 整合 google.guava

 

Controller:

package com..web;

import com..anno.RateLimitAnno;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;

@RestController
public class LimiterController {

    @RateLimitAnno
    @RequestMapping("/limiter")
    public String limiter(HttpServletRequest request){
        StringBuilder sb = new StringBuilder("{");
        //正常返回
        sb.append("'result':'0000','msg':'成功'");
        return  sb.append("}").toString();
    }

}

 

自定義注解:

package com..anno;

import java.lang.annotation.*;

@Inherited   // 允許子類繼承  元注解
@Documented  // 被 javadoc工具記錄
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE}) //注解可能出現在Java程序中的語法位置
@Retention(RetentionPolicy.RUNTIME)  //注解保留時間,保留至運行時
public @interface RateLimitAnno {


}

 

具體限流實現:

package com..util;

import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
@Scope
@Aspect
public class RateLimitAspect {

    final double permitsPerSecond = 5.0;        //每秒生成5個令牌
    final long warmupPeriod = 1;                //在warmupPeriod時間內RateLimiter會增加它的速率,在抵達它的穩定速率或者最大速率之前
    final TimeUnit timeUnit = TimeUnit.SECONDS; //參數warmupPeriod 的時間單位
    /*
     * 創建一個穩定輸出令牌的RateLimiter,保證了平均每秒不超過qps個請求
     * 當請求到來的速度超過了qps,保證每秒只處理qps個請求
     * 當這個RateLimiter使用不足(即請求到來速度小於qps),會囤積最多qps個請求
     *
     * 創建的是SmoothBursty 實例 平滑穩定
     */
    RateLimiter rateLimiter = RateLimiter.create(permitsPerSecond);

    /**
     *
     * 根據指定的穩定吞吐率和預熱期來創建RateLimiter,
     * 這里的吞吐率是指每秒多少許可數(通常是指QPS,每秒多少查詢),
     * 在這段預熱時間內,RateLimiter每秒分配的許可數會平穩地增長直到預熱期結束時達到其最大速率(只要存在足夠請求數來使其飽和)。
     * 同樣地,如果RateLimiter 在warmupPeriod時間內閑置不用,它將會逐步地返回冷卻狀態。
     * 也就是說,它會像它第一次被創建般經歷同樣的預熱期。
     * 返回的RateLimiter 主要用於那些需要預熱期的資源,這些資源實際上滿足了請求(比如一個遠程服務),
     * 而不是在穩定(最大)的速率下可以立即被訪問的資源。
     * 返回的RateLimiter 在冷卻狀態下啟動(即預熱期將會緊跟着發生),並且如果被長期閑置不用,它將回到冷卻狀態
     *
     * 創建的是SmoothWarmingUp實例    平滑預熱
     */
    RateLimiter rl = RateLimiter.create(permitsPerSecond,warmupPeriod,timeUnit);


    //設置業務切入點為標注了自定義注解的位置
    @Pointcut("@annotation(com.wondersgroup.anno.RateLimitAnno)")
    public void aspectService(){ }

    //統計
    int countSuccess,countFail = 0;

    //環繞通知
    @Around("aspectService()")
    public Object aroundMsg(ProceedingJoinPoint joinPoint){

        Object obj = null;

        boolean flag = rateLimiter.tryAcquire(); // 在無延遲下的情況下獲得

        // 從RateLimiter獲取一個許可,該方法會被阻塞直到獲取到請求。
        // 如果存在等待的情況的話,告訴調用者獲取到該請求所需要的睡眠時間。該方法等同於acquire(1)。
        //double waitTime = rabuyGoodsteLimiter.acquire(); 我非要得到令牌才返回

        try{

            if(flag){ //如果獲取了令牌,則可以繼續執行業務層面的邏輯
                obj = joinPoint.proceed();

                countSuccess++;//並發時,統計不准確!!!

            }else{
                obj = "{'result':'0001','msg':'當前系統繁忙,請重試...'}"; //未獲取到令牌,直接返回

                countFail++;
            }

        }catch(Throwable ex){
            ex.printStackTrace();
        }

        System.out.println(flag +"   :    "+ obj + " success:" + countSuccess + " , fail:" + countFail);

        return obj;
    }

}

 

運行項目,訪問  http://localhost:8080/limiter  

結果:

 

 

 

二. 整合 Redisson

 

package com..distributed;

import org.redisson.Redisson;
import org.redisson.api.*;
import org.redisson.config.Config;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class RedisRateLimiter {

    static RedissonClient redisson = null;
    static RRateLimiter myLimiter;

    static {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setConnectionMinimumIdleSize(10);
        //創建Redisson客戶端
        redisson = Redisson.create(config);

        /**
         * 基於Redis的分布式限流器可以用來在分布式環境下現在請求方的調用頻率。
         * 既適用於不同Redisson實例下的多線程限流,也適用於相同Redisson實例下的多線程限流。
         * 該算法不保證公平性。
         */
        myLimiter = redisson.getRateLimiter("my");

        /**
         * Total rate for all RateLimiter instances
         * 作用在所有的RRateLimiter實例
         * OVERALL
         *
         * Total rate for all RateLimiter instances working with the same Redisson instance
         * 作用在同一個Redisson實例創建的 RRateLimiter上面。
         * PER_CLIENT
         *
         * return : 設置是否成功。 對同一個redis服務端,只需要設置一次。如果redis重啟需要重新設置
         */
        boolean bl = myLimiter.trySetRate(RateType.PER_CLIENT, 5, 1, RateIntervalUnit.SECONDS);
    }


    //結合redis,實現分布式的qpi接口限流
    public static void test() {

        CountDownLatch cd = new CountDownLatch(1);
        Random rd = new Random();

        for (int i = 0; i < 20; i++) {
            new Thread(() -> {

                try {
                    cd.await(); //使得當前線程阻塞
                    Thread.sleep(rd.nextInt(1000)); //模擬20個請求的並發,有一點點先后順序的差異
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {

                    //獲取令牌
                    System.out.println(Thread.currentThread().getName() + " : " + myLimiter.tryAcquire());
                }

            }).start();
        }

        cd.countDown();
    }

    public static void main(String[] args) {

        test();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //正常退出 status為0時為正常退出程序,也就是結束當前正在運行中的java虛擬機
        System.exit(0);
    }

}

 

運行 main 函數

結果:

 

 

pom 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gupao.ls</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

        <!--Redisson插件-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.10.2</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>


        <!--guava JAR-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM