25、zuul之多维度限流

  • 对请求的目标URL进行限流(例如:某个URL每分钟只允许调用多少次)

  • 对客户端的访问IP进行限流(例如:某个IP每分钟只允许请求多少次)

  • 对某些特定用户或者用户组进行限流(例如:非VIP用户限制每分钟只允许调用100次某个API等)

  • 多维度混合的限流。此时,就需要实现一些限流规则的编排机制。与、或、非等关系。

介绍

spring-cloud-zuul-ratelimit是和zuul整合提供分布式限流策略的扩展,只需在yaml中配置几行配置,就可使应用支持限流

<dependency>

    <groupId>com.marcosbarbero.cloud</groupId>

    <artifactId>spring-cloud-zuul-ratelimit</artifactId>

<version>1.3.4.RELEASE</version></dependency>

 

支持的限流粒度

  • 服务粒度 (默认配置,当前服务模块的限流控制)

  • 用户粒度 (详细说明,见文末总结)

  • ORIGIN粒度 (用户请求的origin作为粒度控制)

  • 接口粒度 (请求接口的地址作为粒度控制)

  • 以上粒度自由组合,又可以支持多种情况。

  • 如果还不够,自定义RateLimitKeyGenerator实现。

// 默认实现

public String key(final HttpServletRequest request, final Route route, final RateLimitProperties.Policy policy) {

    final List<Type> types = policy.getType();

    final StringJoiner joiner = new StringJoiner(":");

    joiner.add(properties.getKeyPrefix());

    if (route != null) {

        joiner.add(route.getId());

    }

    if (!types.isEmpty()) {

        if (types.contains(Type.URL) && route != null) {

            joiner.add(route.getPath());

        }

        if (types.contains(Type.ORIGIN)) {

            joiner.add(getRemoteAddr(request));

        }

        // 这个结合文末总结。

        if (types.contains(Type.USER)) {

            joiner.add(request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : ANONYMOUS_USER);

        }

    }

    return joiner.toString();

}

支持的存储方式

  • InMemoryRateLimiter - 使用 ConcurrentHashMap作为数据存储

  • ConsulRateLimiter - 使用 Consul 作为数据存储

  • RedisRateLimiter - 使用 Redis 作为数据存储

  • SpringDataRateLimiter - 使用 数据库 作为数据存储

    对API限流是基于Zuul过滤器完成的,默认情况下限流数据是记录在内存中的,实际上是用ConcurrentHashMap保存,当然也提供了多种存储方式,包括Redis、Consul、Spring Data JPA,使用这三种存储方式要添加相关依赖。

限流配置

  • limit 单位时间内允许访问的个数

  • quota 单位时间内允许访问的总时间(统计每次请求的时间综合)

  • refresh-interval 单位时间设置

zuul: 

    ratelimit: 

        key-prefix: your-prefix

        enabled: true

        repository: REDIS

        behind-proxy: true 

        policies: 

            myServiceId: 

                limit: 10 

                quota: 20 

                refresh-interval: 30 

                type: 

                - user

                - origin 

              - url

以上配置意思是:30秒内允许10个访问,并且要求总请求时间小于20秒,某个IP的客户端被限流并不影响其他客户端,即API网关对每个客户端限流是相互独立的

 

效果展示

yaml配置:

zuul: 

    ratelimit: 

        key-prefix: pig-ratelimite

        enabled: true

        repository: REDIS

        behind-proxy: true 

            policies: 

                pig-admin-service: 

                    limit: 2 

                    quota: 1 

                    refresh-interval: 3

动态图 ↓↓↓↓↓ 

Redis 中数据结构 注意红色字体

原理分析

限流拦截时机

限流过滤器是在请求被转发之前调用的

@Override

public String filterType() {

return "pre";

}

限流类型

限流类型主要包括url、origin、user三种

if (types.contains(URL)) {

joiner.add(route.getPath());

}

if (types.contains(ORIGIN)) {

joiner.add(getRemoteAddr(request));

}

if (types.contains(USER)) {

joiner.add(request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : ANONYMOUS);

}

 

url类型的限流就是通过请求路径区分

origin是通过客户端IP地址区分

user是通过授权用户进行区分,也包括匿名用户

可以多个限流类型结合使用

如果不配置限流类型,就不做以上区分

拦截限流请求

在过滤器的run方法中判断请求剩余次数,小于0就拦截请求:

if (rate.getRemaining() < 0) {

ctx.setResponseStatusCode(TOO_MANY_REQUESTS.value());

ctx.put("rateLimitExceeded", "true");

throw new ZuulRuntimeException(new ZuulException(TOO_MANY_REQUESTS.toString(),

TOO_MANY_REQUESTS.value(), null));

}

可以看到,单位时间内剩余请求次数小于0时抛出ZuulRuntimeException,直接返回客户端TOO_MANY_REQUESTS异常消息,达到拦截请求的效果。

总结

  • 可以使用Spring Boot Actuator 提供的服务状态,动态设置限流开关

  • 源码可以参考:https://gitee.com/log4j/pig

  • 用户限流的实现:如果你的项目整合 Shiro 或者 Spring Security 安全框架,那么会自动维护request域UserPrincipal,如果是自己的框架,请登录成功后维护request域UserPrincipal,才能使用用户粒度的限流。未登录默认是:anonymous

快来成为我的朋友或合作伙伴,一起交流,一起进步!
QQ群:961179337
微信:lixiang6153
邮箱:lixx2048@163.com
公众号:IT技术快餐
更多资料等你来拿!

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝壳里的沙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值