限流框架 - RateLimit(接入篇)

目錄

問題:

快捷接入

依賴

配置

代碼:

踩坑篇

zuul.ratelimit.enabled配置

Swagger-ui 攔截問題

Failed to convert property value of type 'java.lang.String' to required type 'java.time.Duration'

原因

解決方案


問題:

前端頁面存在大量重複點擊行爲,對系統服務造成無用請求開支.
同時也可以防止DOS,故此可以通過限流方式進行控制

有時必須控制網絡接收到的流量的速率,以防止DoS之類的攻擊或將單個用戶/源可以向某個終結點發出的請求數量限制到一定範圍。
在微服務架構中,API網關是整個應用程序的入口點,並且在這一層具有速率限制非常適合。

快捷接入

依賴

  1. 引入ratelimit jar包
<dependency>
            <groupId>com.marcosbarbero.cloud</groupId>
            <artifactId>spring-cloud-zuul-ratelimit</artifactId>
            <version>2.2.3.RELEASE</version>
</dependency>

     寫下該博客時,zuul-rateLimit已更新到 版本.

     但該版本對SpringBoot低版本不太友好,必須更新SpringBoot v2.0.1.RELEASE版本後方才能正常運行,詳看【踩坑篇 3】

     本次項目中使用Redis進行計數器存儲,更多數據存儲【

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

 

配置

# ReteLimit 開關,如果已引入對應jar包,該配置必須爲true.詳看【坑1】
zuul.ratelimit.enabled = true
# 自定義關鍵字前綴
zuul.ratelimit.key-prefix = GTW-MS
# 數據存儲方式
zuul.ratelimit.repository = REDIS
# response-heads中是否追加RateLimit信息
zuul.ratelimit.add-response-headers = true
# RateLimitPreFilter order 優先級,0-最高 
#zuul.ratelimit.post-filter-order = 10
# RateLimitPostFilter order 優先級,0-最高
#zuul.ratelimit.pre-filter-order = 0
# 默認限流策略(全局),可解讀爲:
# 全局策略1,根據url進行限流,1秒允許調用10次,且這10次調用總耗時需小於20s
# 如果想設置更多的限流策略,新增default-policy-list數組元素即可.
zuul.ratelimit.default-policy-list[0].limit = 10
zuul.ratelimit.default-policy-list[0].quota = 20
zuul.ratelimit.default-policy-list[0].refresh-interval = 1
zuul.ratelimit.default-policy-list[0].type[0] = url
# 微服務MSG-MS網關登記配置,msg爲路徑自定義命名.
# 指定網關對外映射路徑
zuul.routes.msg.path=/msg/**
# 前綴
zuul.routes.msg.stripPrefix=true
# 指向微服務
zuul.routes.msg.serviceId=MSG-MS

# 爲指定msg定製策略,可解讀:
# /msg/* 路徑請求, 3秒內允許訪問2次,且2次調用耗時需小於4s
# 或 30秒內允許訪問10次,且10次調用耗時小於30s
zuul.ratelimit.policy-list.msg[0].limit = 2
zuul.ratelimit.policy-list.msg[0].quota = 4
zuul.ratelimit.policy-list.msg[0].refresh-interval = 3
zuul.ratelimit.policy-list.msg[0].type[0] = url

zuul.ratelimit.policy-list.msg[1].limit = 10
zuul.ratelimit.policy-list.msg[1].quota = 30
zuul.ratelimit.policy-list.msg[1].refresh-interval = 30
zuul.ratelimit.policy-list.msg[1].type[0] = url

代碼:

/**
 * 重寫Key定義
 *
 * @Date 2020/5/19
 * @Time 18:16
 */
@Configuration
@Slf4j
public class RateLimitConfig {
    /**
     * 爲了針對地址+用戶進行限流,重新定義RateLimitKey
     * 配置中已定義 ratelimit.xxx.type=url,並且在原先key中追加消息頭中的token字段加以實現 
     * @param properties
     * @param rateLimitUtils
     * @return
     */ 
    @Bean
    public RateLimitKeyGenerator rateLimitKeyGenerator(final RateLimitProperties properties, final RateLimitUtils rateLimitUtils) {

        return new DefaultRateLimitKeyGenerator(properties, rateLimitUtils) {
            @Override
            public String key(final HttpServletRequest request, final Route route, final RateLimitProperties.Policy policy) {
                String key = super.key(request, route, policy) + ":" + request.getHeader("Auth");
                log.info("===> current limit key :{}", key);
                return key;
            }
        };
    }
}
package com.enmonster.platformbasic.gtw.config;

import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.DefaultRateLimiterErrorHandler;
import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.RateLimiterErrorHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @Date 2020/5/20
 * @Time 18:01
 */
@Component
@Slf4j
public class RateLimiterErrorConfig {

    /**
     * 重新定義RateLimiterErrorHandler 
     * 可以進行日誌捕捉或者其他操作
     * @return
     */
    @Bean
    public RateLimiterErrorHandler rateLimitErrorHandler() {
        return new DefaultRateLimiterErrorHandler() {
            /**
             * 往redis存儲限流請求信息的時候報錯的處理,此方法一般不用重寫
             *
             * @param key
             * @param e
             */
            @Override
            public void handleSaveError(String key, Exception e) {
                log.info("===>rateLimiter redisKey 存儲出錯:{}!! ", e);
                super.handleSaveError(key, e);
            }

            /**
             * 從redis取出限流請求信息的時候報錯的處理,此方法一般不用重寫
             *
             * @param key
             * @param e
             */
            @Override
            public void handleFetchError(String key, Exception e) {
                super.handleFetchError(key, e);
            }

            /**
             * 請求發生限流了,或者限流過程中發生錯誤了的處理
             * 對限流進行日誌記錄,返回限流的信息等,方便後期分析日誌排查問題,這裏就簡單處理打印日誌
             *
             * @param msg
             * @param e
             */
            @Override
            public void handleError(String msg, Exception e) {
                log.error("限流信息msg={},錯誤信息e={}", e);
                super.handleError(msg, e);
            }
        };
    }


}

 

踩坑篇

zuul.ratelimit.enabled配置

  一旦引入RateLimit的jar包後,邊必須設置zuul.ratelimit.enabled=true,否則項目啓動時報錯

RateLimit enable爲false.pngRateLimit缺少配置.jpg

Swagger-ui 攔截問題

經測試 2.2.3.RELEASE版本會對Swagger-ui頁面造成攔截,將版本升級到2.2.7.RELEASE版本後可解決該問題

2.2.3版本,swagger無法正常使用.png

SWAGGER-UI.jpg

Failed to convert property value of type 'java.lang.String' to required type 'java.time.Duration'

服務啓動失敗報錯

***************************

APPLICATION FAILED TO START

***************************

Description:

Binding to target com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.properties.RateLimitProperties@2178d296 failed:

  Property: zuul.ratelimit.defaultPolicyList[0].quota

  Value: 1000

  Reason: Failed to convert property value of type 'java.lang.String' to required type 'java.time.Duration' for property 'defaultPolicyList[0].quota'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.time.Duration]

  Property: zuul.ratelimit.defaultPolicyList[0].refreshInterval

  Value: 60

  Reason: Failed to convert property value of type 'java.lang.String' to required type 'java.time.Duration' for property 'defaultPolicyList[0].refreshInterval'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [@javax.validation.constraints.NotNull java.time.Duration]

Action:

Update your application's configuration

原因

在Zuul RateLimit版本介紹中,v2.3.0.RELEASE 支持 java.time.Duratio配置 refresh-internalquote

作者對配置解析進行了優化,這是好事.
但是翻看v2.3.0版本後發現,作者使用org.springframework.boot.convert.DurationUnit 
String轉換爲Duration,.且該方法SpringBoot v2.0.1.RELEASE版本纔開始存在.
故此低SpringBoot版本引入2.3.0.RELEASE或更高的Zuul-RateLimit包後,啓動會報上述錯誤

image.png

image.png

解決方案

SpringBoot版本在 v2.0.1.RELEASE以下的,建議使用Zuul-RateLimit版本不得超過 v2.2.7.RELEASE

SpringBoot版本在v2.0.1.RELEAS或者更高版本的,推薦使用Zuul-RateLimit的最新版本.
最新接入可參考GitHub官方文檔:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章