今天使用下 rate_limiter 插件。

开启 redis,启动 soul-admin,soul-bootstrap,soul-examples-http,开启 divide 插件和 rate_limiter 插件。

先看下插件链的执行顺序。skip和未开启的插件就不讲了。

GlobalPlugin 构建 SoulContext ,并把 SoulContext 放到 ServerWebExchange

先走的是 rate_limiter 插件,RateLimiterPlugin 使用 RedisRateLimiter 类进行处理。RedisRateLimiter 如果 isAllowed 是false,就不允许访问,返回 Too Many Requests。

接着走 dividePlugin,拿到具体的 url,走完后,执行的是 WebClientPlugin,其他插件会先在 AbstractSoulPlugin 执行 execute 方法,再去具体插件类执行 doExecute 方法 ,但是 WebClientPlugin 插件是直接执行它的 execute 方法,这里就是封装了 http 请求,并执行了这个请求,把结果放到 exchange 里。

    private Mono<Void> handleRequestBody(final WebClient.RequestBodySpec requestBodySpec,
                                         final ServerWebExchange exchange,
                                         final long timeout,
                                         final int retryTimes,
                                         final SoulPluginChain chain) {
        return requestBodySpec.headers(httpHeaders -> {
            httpHeaders.addAll(exchange.getRequest().getHeaders());
            httpHeaders.remove(HttpHeaders.HOST);
        })
                .contentType(buildMediaType(exchange))
                .body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody()))
                .exchange()
                .doOnError(e -> log.error(e.getMessage()))
                .timeout(Duration.ofMillis(timeout))
                .retryWhen(Retry.onlyIf(x -> x.exception() instanceof ConnectTimeoutException)
                    .retryMax(retryTimes)
                    .backoff(Backoff.exponential(Duration.ofMillis(200), Duration.ofSeconds(20), 2, true)))
                .flatMap(e -> doNext(e, exchange, chain));

    }

接下来会走几个 BodyParamPlugin 方法,这里主要是针对 sofa,dubbo,tars 这几个插件进行处理的。

最后执行的是 WebClientResponsePlugin,从 exchange 拿结果对返回结果进行封装。

我在 rate_limiter 插件配置的规则如下。

capacity 是允许用户在一秒钟内执行的最大请求数,这是令牌桶可以保存的令牌数。

rate 是你允许用户每秒执行多少请求,这是令牌桶的填充速率。

我配置了每秒最多执行12个请求后,执行 sb -u http://localhost:9195/http/order/findById?id=2 -c 20 -N 6 压测时发现,tokensRemaining 的数量在减少。

当 tokensRemaining 值为0的时候,就不允许访问了,这里的 allowed=false 就是false,这里也表明我们设置的限流机制已经生效了。

还不清楚令牌桶的原理,目前猜测是把这个最大请求数放在 redis 里,执行一个,redis 数量就减一,通过这个来实现限流的。