一個優秀的分散式springboot/SpringCloudAPI限流框架,特別適合微服務架構

寧城雲哥發表於2018-12-08

啥也不說了,上程式碼先:

redislimiter-spring-boot

一個優秀的分散式spring boot/Spring Cloud API限流框架,特別適合微服務架構.

快速開始

1. git clone https://github.com/tangaiyun/redislimiter-spring-boot.git

2. cd redislimiter-spring-boot-starter

3. mvn clean install

4. 新建一個Spring boot API 專案,具體參考demo1專案,要在專案依賴中加入

        <dependency>
            <groupId>com.tay</groupId>
            <artifactId>redislimiter-spring-boot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

5. 修改專案resources/application.yml檔案

server:
    port: 8888                                #埠
spring:
    application:
        name: demo1                           #應用名稱必須要配置,不然無法啟動
    redis-limiter:                            #限流器配置
        redis-host: 127.0.0.1                 #redis server ip  
        check-action-timeout: 100             #訪問檢查動作最大執行時間(單位毫秒)
        enable-dynamical-conf: true           #開啟動態限流配置 

spring.application.name必須配置

6. 新建一個RestController類

package com.tay.demo1;

import com.tay.redislimiter.RateLimiter;
import com.tay.redislimiter.dynamic.DynamicRateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;


@RestController
@RequestMapping("/demo")
public class DemoController {

    @GetMapping("/test")
    //基於使用者限流,獨立使用者每分鐘最多2次訪問,使用者id在header中,key為userid
    //RateLimiter標籤為靜態配置,此類配置不可動態修改
    @RateLimiter(base = "#Headers[`userid`]", permits = 2, timeUnit = TimeUnit.MINUTES) 
    public String test() {
        return "test!";
    }

    @GetMapping("/dynamictest")
    //基於來源ip限流,獨立ip每分鐘最多訪問5次訪問,來源ip位於header中,key為X-Real-IP
    //DynamicRateLimiter標籤代表動態配置,此類配置可在執行時動態修改
    @DynamicRateLimiter(base = "#Headers[`X-Real-IP`]", permits = 5, timeUnit = TimeUnit.MINUTES)
    public String dynamicTest() {
        return "dynamictest!";
    }

}

7. 在本機安裝redis並啟動,強烈建議在本機安裝docker環境,然後執行

sudo docker run -d -p 6379:6379 redis

就是這麼爽氣!

8. 執行Demo1Application.java

9. 測試

通過postman或者restd訪問url http://localhost:8888/demo/test 在header中指定userid=tom, 可以發現tom一分鐘最多隻能訪問2次

通過postman或者restd訪問url http://localhost:8888/demo/dynamictest 在header中指定X-Real-IP=127.0.0.1, 可以發現127.0.0.1一分鐘最多隻能訪問5次

高階教程

1. 配置項大全

spring:
    redis-limiter: 
        redis-host: 127.0.0.1           # redis server IP                  預設值:127.0.0.1
        redis-port: 6379                # redis service 埠               預設值:6379  
        redis-password: test            # redis 訪問密碼                   預設值:null 
        redis-connection-timeout: 2000  # redis 連線超時時間               預設值:2000
        redis-pool-max-idle: 50         # redis 連線池最大空閒連線數        預設值:50
        redis-pool-min-idle: 10         # redis 連線池最小空閒連線數        預設值: 10 
        redis-pool-max-wait-millis: -1 # 從連線池中獲取連線最大等待時間     預設值: -1 
        redis-pool-max-total: 200       # 連線池中最大連線數                預設值: 200
        redis-key-prefix: #RL           # 訪問痕跡key值字首                 預設值: #RL
        check-action-timeout: 100       # 訪問檢查動作最大執行時間(單位毫秒) 預設值: 100
        enable-dynamical-conf: true     # 是否開啟動態配置                  預設值: false 
        channel: #RLConfigChannel      # 配置變更事件傳送channel名稱        預設值: #RLConfigChannel   

2 標籤

@RateLimiter, @DynamicRateLimiter 是使用者最經常使用到的。

2.1 標籤說明 –整體說明

@RateLimiter @DynamicRateLimiter 這兩個標籤用法完全一致,他們都有4個屬性base、path、timeUnit、permits.

@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface RateLimiter {

    String base() default "";

    String path() default "";

    TimeUnit timeUnit() default TimeUnit.SECONDS;

    int permits() default 10000;
}

@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface DynamicRateLimiter {
    String base() default "";

    String path() default "";

    TimeUnit timeUnit() default TimeUnit.SECONDS;

    int permits() default 10000;
}

2.2 標籤說明 — base引數(Spel表示式)說明

標籤都有一個屬性base,含義就是限流是”基於what”來進行的,如果你不指定base,那麼所有的請求都會聚合在一起統計,base為一個Spel表示式。

@RateLimiter(base = "#Headers[`userid`]", permits = 2, timeUnit = TimeUnit.MINUTES) 
@DynamicRateLimiter(base = "#Headers[`X-Real-IP`]", permits = 5, timeUnit = TimeUnit.MINUTES)

目前base表示式僅支援從header和cookie中取值,Headers和Cookies就是兩個Map, 下面兩種配置都是合法的。

"#Headers[`X-Real-IP`]"
"#Cookies[`userid`]"

2.3 標籤使用 — path 引數說明

path 如果不設定預設值是””, 當path為””, 框架內部會把它改寫為request.getRequestURI(),一般情況下框架預設行為就OK了。但在一種情況下你可能需要設定path引數,就是RequestMapping的path裡面包含Path Parameters的情況,例如:

    @GetMapping("/user/{userid}")
    @DynamicRateLimiter(base = "#Headers[`X-Real-IP`]", path = "/user", permits = 5, timeUnit = TimeUnit.MINUTES)
    public User get(@PathVariable String userid) {
        User user ...
       
        return user;
    }

在這種情況下,我們一般不會基於”/user/001″這樣統計,所有訪問”/user/001″, “/user/002″的請求都會聚合到path “/user`上統計。

2.4 標籤使用 — timeUnit 引數說明

訪問統計時間單位,以下4種都是有效的:

TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS

2.5 標籤使用 — permits 引數說明

單位時間內允許訪問的次數

3. 動態配置

動態配置使用@DynamicRateLimiter標籤,動態配置含義就是在執行時可以動態修改限流配置,這個是通過提供內建配置訪問Rest API來實現的。

RestController
@RequestMapping("/limiterconfig")
@RequiredArgsConstructor
public final class LimiterConfigResource implements InitializingBean, ApplicationContextAware {
    ...
    
    @PutMapping
    public void update(@RequestBody LimiterConfig limiterConfig, HttpServletResponse response) throws IOException {
        if(applicationName.equals(limiterConfig.getApplicationName())) {
            publish(limiterConfig);
        }
        else {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            response.getWriter().print("Bad request for updating limiter configuration!");
        }
    }
    @GetMapping
    public LimiterConfig get(@RequestParam("controller") String controller, @RequestParam("method")String method) {
        String limiterConfigKey = controller + ":" + method;
        return redisLimiterConfigProcessor.get(limiterConfigKey);
    }

    @DeleteMapping
    public void delete(@RequestParam("controller") String controller, @RequestParam("method")String method) {
        LimiterConfig limiterConfig = new LimiterConfig();
        limiterConfig.setApplicationName(applicationName);
        limiterConfig.setControllerName(controller);
        limiterConfig.setMethodName(method);
        limiterConfig.setDeleted(true);
        publish(limiterConfig);
    }

目前提供了修改(PUT), 查詢 (GET), 刪除(DELETE)三種操作。

對於demo1專案

我們可以通過 GET http://localhost:8888/limiterconfig?controller=DemoController&method=dynamicTest 來獲取限流配置,返回值為

{
  "applicationName": "demo1",
  "controllerName": "DemoController",
  "methodName": "dynamicTest",
  "baseExp": "#Headers[`userid`]",
  "path": "",
  "timeUnit": "MINUTES",
  "permits": 5,
  "deleted": false
}

通過指定Content-Type為application/json PUT http://localhost:8888/limiterconfig 來改動限流配置, 傳送內容如

{
  "applicationName": "demo1",
  "controllerName": "DemoController",
  "methodName": "dynamicTest",
  "baseExp": "#Headers[`userid`]",
  "path": "",
  "timeUnit": "MINUTES",
  "permits": 10,
  "deleted": false
}

通過 DELETE http://localhost:8888/limiterconfig?controller=DemoController&method=dynamicTest 可刪除限流配置


相關文章