限流

每一个对外提供的API接口都是需要做流量控制的,不然会导致系统直接崩溃。很简单的例子,和保险丝的原理一样,如果用电负荷超载就会烧断保险丝断掉电源以达到保护的作用。API限流的意义也是如此,如果API上的流量请求超过核定的数值我们就得对请求进行引流或者直接拒绝等操作。

concrete预定义了两种限流模式,最大并发及令牌桶,默认令牌桶限流。开发者也可以根据需要自己的需要扩展限流策略。

使用

新定义一个服务用来做限流演示。

package org.coodex.concrete.demo.api.excepted;

import org.coodex.concrete.api.ConcreteService;
import org.coodex.concrete.api.limiting.MaximumConcurrency;
import org.coodex.concrete.api.limiting.TokenBucket;

@ConcreteService
public interface LimitingService {

    @MaximumConcurrency(strategy = "demo")
    void demoMaximumConcurrency();

    @TokenBucket(
            bucket = "demo", // 使用哪个桶里的令牌
            tokenUsed = 10 // 本服务一次需要10个令牌
    )
    void demoTokenBucket();
}

实现

package org.coodex.concrete.demo.impl;

import org.coodex.concrete.demo.api.excepted.LimitingService;

import javax.inject.Named;

@Named
public class LimitingServiceImpl implements LimitingService {
    @Override
    public void demoMaximumConcurrency() {
        try {
            // 拖延十秒形成并发量
            Thread.sleep(10000);
        } catch (InterruptedException ignore) {
        }
    }

    @Override
    public void demoTokenBucket() {
        // doNothing
    }
}

concrete.yml 开启拦截器

interceptors:
  limiting: true # 开启限流拦截器

分别设置比较小的参数

limiting.maximum.concurrency.yml

demo:
  max: 3

tokenBucket.yml

demo:
  capacity: 50 # 令牌桶容量
  flow: 20 # 每秒流入几个令牌

客户端模拟演示

package org.coodex.concrete.demo.client;

import org.coodex.concrete.Client;
import org.coodex.concrete.ClientException;
import org.coodex.concrete.demo.api.excepted.LimitingService;
import org.coodex.concurrent.ExecutorsHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutorService;

public class LimitingServiceDemo {
    private final static Logger log = LoggerFactory.getLogger(LimitingServiceDemo.class);


    private static final ExecutorService EXECUTOR_SERVICE =
            ExecutorsHelper.newFixedThreadPool(30, "demo");

    public static void main(String[] args) {
        LimitingService limitingService = Client.getInstance(LimitingService.class, "jaxrs");
        // 连着请求 demoMaximumConcurrency
        for (int i = 1; i <= 20; i++) {
            int finalI = i;
            EXECUTOR_SERVICE.execute(() -> {

                try {
                    limitingService.demoMaximumConcurrency();
                } catch (ClientException e) {
                    log.info("{}, demoMaximumConcurrency {}", finalI, e.getLocalizedMessage());
                }
            });
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignore) {
            }
        }


        EXECUTOR_SERVICE.execute(() -> {
            for (int i = 1; i < 30; i++) {
                try {
                    limitingService.demoTokenBucket();
                } catch (ClientException e) {
                    log.info("{}, demoTokenBucket {}", i, e.getLocalizedMessage());
                }
                if (i % 5 == 0) { // 每5个任务一组,每组之间休息800毫秒
                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException ignore) {
                    }
                }

            }
        });
    }

}
15:15:31.610 [demo-4] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 4, demoMaximumConcurrency 过载.
15:15:32.483 [demo-5] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 5, demoMaximumConcurrency 过载.
15:15:33.479 [demo-6] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 6, demoMaximumConcurrency 过载.
15:15:34.481 [demo-7] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 7, demoMaximumConcurrency 过载.
15:15:35.483 [demo-8] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 8, demoMaximumConcurrency 过载.
15:15:36.489 [demo-9] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 9, demoMaximumConcurrency 过载.
15:15:37.488 [demo-10] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 10, demoMaximumConcurrency 过载.
15:15:38.485 [demo-11] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 11, demoMaximumConcurrency 过载.
15:15:39.495 [demo-12] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 12, demoMaximumConcurrency 过载.
15:15:43.502 [demo-16] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 16, demoMaximumConcurrency 过载.
15:15:44.487 [demo-17] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 17, demoMaximumConcurrency 过载.
15:15:45.486 [demo-18] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 18, demoMaximumConcurrency 过载.
15:15:46.497 [demo-19] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 19, demoMaximumConcurrency 过载.
15:15:47.496 [demo-20] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 20, demoMaximumConcurrency 过载.
15:15:49.430 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 7, demoTokenBucket 过载.
15:15:49.440 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 8, demoTokenBucket 过载.
15:15:49.450 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 9, demoTokenBucket 过载.
15:15:49.458 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 10, demoTokenBucket 过载.
15:15:50.297 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 13, demoTokenBucket 过载.
15:15:50.306 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 14, demoTokenBucket 过载.
15:15:50.313 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 15, demoTokenBucket 过载.
15:15:51.146 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 18, demoTokenBucket 过载.
15:15:51.159 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 19, demoTokenBucket 过载.
15:15:51.166 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 20, demoTokenBucket 过载.
15:15:51.989 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 22, demoTokenBucket 过载.
15:15:51.999 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 23, demoTokenBucket 过载.
15:15:52.008 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 24, demoTokenBucket 过载.
15:15:52.016 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 25, demoTokenBucket 过载.
15:15:52.842 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 28, demoTokenBucket 过载.
15:15:52.852 [demo-21] INFO org.coodex.concrete.demo.client.LimitingServiceDemo - 29, demoTokenBucket 过载.
2019-08-09 15:15:48.561  INFO 17788 --- [ice.executor-21] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 40
2019-08-09 15:15:48.572  INFO 17788 --- [ice.executor-22] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 30
2019-08-09 15:15:48.582  INFO 17788 --- [ice.executor-23] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 20
2019-08-09 15:15:48.593  INFO 17788 --- [ice.executor-24] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 10
2019-08-09 15:15:48.602  INFO 17788 --- [ice.executor-25] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 0
2019-08-09 15:15:49.417  INFO 17788 --- [ice.executor-26] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens flow in: 17, current: 17, from 1565334948561 to 1565334949416
2019-08-09 15:15:49.417  INFO 17788 --- [ice.executor-26] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 7
2019-08-09 15:15:50.274  INFO 17788 --- [ice.executor-31] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens flow in: 17, current: 24, from 1565334949416 to 1565334950274
2019-08-09 15:15:50.274  INFO 17788 --- [ice.executor-31] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 14
2019-08-09 15:15:50.286  INFO 17788 --- [ice.executor-32] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 4
2019-08-09 15:15:51.123  INFO 17788 --- [ice.executor-36] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens flow in: 16, current: 20, from 1565334950274 to 1565334951123
2019-08-09 15:15:51.124  INFO 17788 --- [ice.executor-36] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 10
2019-08-09 15:15:51.135  INFO 17788 --- [ice.executor-37] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 0
2019-08-09 15:15:51.976  INFO 17788 --- [ice.executor-41] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens flow in: 17, current: 17, from 1565334951123 to 1565334951976
2019-08-09 15:15:51.977  INFO 17788 --- [ice.executor-41] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 7
2019-08-09 15:15:52.825  INFO 17788 --- [ice.executor-46] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens flow in: 16, current: 23, from 1565334951976 to 1565334952825
2019-08-09 15:15:52.825  INFO 17788 --- [ice.executor-46] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 13
2019-08-09 15:15:52.832  INFO 17788 --- [ice.executor-47] o.c.c.c.intercept.TokenBucketLimiting    : [demo]tokens picked: 10, remain: 3

结合这两段分析,我们看一下限流拦截器怎么做的。

最大并发策略:1、2、3任务把3个并发占满了,4-12被拒绝,13执行的时候,1任务释放了一个并发,14、15同,16-20又被拒绝了

令牌桶策略:一开始桶里是满的,1-5号任务都可以从桶里拿到10个令牌,6号任务执行时,回流了17个(800ms左右),它也拿到了,7-10号任务执行时,只剩7个令牌,所以被拒绝,11号任务执行时,回流了17个,桶里变成了24个,所以11/12正常,13开始,只剩下4个,只到16号开始执行时,回流16个。。。。不再一一解读。

扩展

开发者需要自己的限流算法时,实现org.coodex.concrete.api.LimitingStrategy放到SPI里即可。

(ɔ) coodex.org 2016 - 2020            修订时间: 2020-03-09 02:56:36

results matching ""

    No results matching ""