{"id":1947,"date":"2023-04-01T10:11:13","date_gmt":"2023-04-01T02:11:13","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1947"},"modified":"2023-04-22T08:41:02","modified_gmt":"2023-04-22T00:41:02","slug":"spring-cloud-gateway-custom-filter-for-flow-limiting","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/04\/01\/spring-cloud-gateway-custom-filter-for-flow-limiting\/","title":{"rendered":"Spring Cloud Gateway\u81ea\u5b9a\u4e49\u8fc7\u6ee4\u5668\u8fdb\u884c\u9650\u6d41"},"content":{"rendered":"<pre><code class=\"language-xml\">&lt;dependencies&gt;\n    &lt;!-- Spring Cloud Gateway\u7684\u4f9d\u8d56--&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-cloud-starter-gateway&lt;\/artifactId&gt;\n    &lt;\/dependency&gt;\n    &lt;!-- Bucket4j\u9650\u6d41\u4f9d\u8d56--&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;com.github.vladimir-bukhtoyarov&lt;\/groupId&gt;\n        &lt;artifactId&gt;bucket4j-core&lt;\/artifactId&gt;\n        &lt;version&gt;4.0.0&lt;\/version&gt;\n    &lt;\/dependency&gt;\n&lt;\/dependencies&gt;<\/code><\/pre>\n<p><!-- more --><\/p>\n<pre><code class=\"language-java\">@SpringBootApplication\npublic class GatewayApplication {\n\n    @Bean\n    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {\n        return builder.routes()\n                .route(r -&gt; r.path(&quot;\/test\/rateLimit&quot;)\n                        .filters(f -&gt; f.rewritePath(&quot;test&quot;, &quot;hello&quot;)\n                                .filter(new GatewayRateLimitByIpFilter(10, 1, Duration.ofSeconds(1))))\n                        .uri(&quot;http:\/\/127.0.0.1:9001&quot;)\n                )\n                .build();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-java\">import io.github.bucket4j.Bandwidth;\nimport io.github.bucket4j.Bucket;\nimport io.github.bucket4j.Bucket4j;\nimport io.github.bucket4j.Refill;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.cloud.gateway.filter.GatewayFilter;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.core.Ordered;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport java.time.Duration;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n\/**\n * @author yezhou\n *\/\n@Slf4j\npublic class GatewayRateLimitByIpFilter implements GatewayFilter, Ordered {\n\n    \/**\n     * \u5355\u673a\u7f51\u5173\u9650\u6d41\u7528\u4e00\u4e2aConcurrentHashMap\u6765\u5b58\u50a8 bucket\n     * \u5982\u679c\u662f\u5206\u5e03\u5f0f\u96c6\u7fa4\u9650\u6d41\u7684\u8bdd\uff0c\u53ef\u4ee5\u91c7\u7528 Redis\u7b49\u5206\u5e03\u5f0f\u89e3\u51b3\u65b9\u6848\n     *\/\n    private static final Map&lt;String, Bucket&gt; LOCAL_CACHE = new ConcurrentHashMap&lt;&gt;();\n\n    \/**\n     * \u6876\u7684\u6700\u5927\u5bb9\u91cf\uff0c\u5373\u80fd\u88c5\u8f7d Token \u7684\u6700\u5927\u6570\u91cf\n     *\/\n    @Getter\n    @Setter\n    int capacity;\n    \/**\n     * \u6bcf\u6b21 Token \u8865\u5145\u91cf\n     *\/\n    @Getter\n    @Setter\n    int refillTokens;\n    \/**\n     *\u8865\u5145 Token \u7684\u65f6\u95f4\u95f4\u9694\n     *\/\n    @Getter\n    @Setter\n    Duration refillDuration;\n\n    public GatewayRateLimitByIpFilter() {\n    }\n\n    public GatewayRateLimitByIpFilter(int capacity, int refillTokens, Duration refillDuration) {\n        this.capacity = capacity;\n        this.refillTokens = refillTokens;\n        this.refillDuration = refillDuration;\n    }\n\n    private Bucket createNewBucket() {\n        Refill refill = Refill.of(refillTokens, refillDuration);\n        Bandwidth limit = Bandwidth.classic(capacity, refill);\n        return Bucket4j.builder().addLimit(limit).build();\n    }\n\n    @Override\n    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();\n        Bucket bucket = LOCAL_CACHE.computeIfAbsent(ip, k -&gt; createNewBucket());\n        log.info(&quot;IP:{}, \u4ee4\u724c\u901a\u53ef\u7528\u7684Token\u6570\u91cf:{}&quot;, ip, bucket.getAvailableTokens());\n        if (bucket.tryConsume(1)) {\n            return chain.filter(exchange);\n        } else {\n            \/\/\u5f53\u53ef\u7528\u7684\u4ee4\u724c\u4e66\u4e3a0\u662f\uff0c\u8fdb\u884c\u9650\u6d41\u8fd4\u56de429\u72b6\u6001\u7801\n            exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);\n            return exchange.getResponse().setComplete();\n        }\n    }\n\n    @Override\n    public int getOrder() {\n        return -100;\n    }\n\n    public static Map&lt;String, Bucket&gt; getLocalCache() {\n        return LOCAL_CACHE;\n    }\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>&lt;dependencies&gt; &lt;!&#8211; Spring Cloud Gateway\u7684\u4f9d\u8d56&#8211;&#038; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[439],"class_list":["post-1947","post","type-post","status-publish","format-standard","hentry","category-spring-cloud","tag-spring-cloud-gateway"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1947","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1947"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1947\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1947"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}