SpringCloud微服务网关做边缘服务限流方案

在高并发的系统中 , 往往需要在系统中做限流 , 一方面是为了防止大量的请求使服务器过载 , 导致服务不可用 , 另一方面是为了防止网络攻击 。
常见的限流方式 , 比如Hystrix适用线程池隔离 , 超过线程池的负载 , 走熔断的逻辑 。 在一般应用服务器中 , 比如tomcat容器也是通过限制它的线程数来控制并发的;也有通过时间窗口的平均速度来控制流量 。 常见的限流纬度有比如通过Ip来限流、通过uri来限流、通过用户访问频次来限流 。
一般限流都是在网关这一层做 , 比如Nginx、Openresty、kong、zuul、Spring Cloud Gateway等;也可以在应用层通过Aop这种方式去做限流 。 这里主要讲讲常用的限流方式 。 一、常见的限流算法目前常用的限流算法有两个:漏桶算法和令牌桶算法 。 1·计数器算法计数器算法采用计数器实现限流有点简单粗暴 , 一般我们会限制一秒钟的能够通过的请求数 , 比如限流qps为100 , 算法的实现思路就是从第一个请求进来开始计时 , 在接下去的1s内 , 每来一个请求 , 就把计数加1 , 如果累加的数字达到了100 , 那么后续的请求就会被全部拒绝 。 等到1s结束后 , 把计数恢复成0 , 重新开始计数 。 具体的实现可以是这样的:对于每次服务调用 , 可以通过AtomicLong#incrementAndGet()方法来给计数器加1并返回最新值 , 通过这个最新值和阈值进行比较 。 这种实现方式 , 相信大家都知道有一个弊端:如果我在单位时间1s内的前10ms , 已经通过了100个请求 , 那后面的990ms , 只能眼巴巴的把请求拒绝 , 我们把这种现象称为“突刺现象”2.漏桶算法漏桶算法的原理比较简单 , 请求进入到漏桶中 , 漏桶以一定的速率漏水 。 当请求过多时 , 水直接溢出 。 可以看出 , 漏桶算法可以强制限制数据的传输速度 。 但高并发情况下最容易出现瓶颈 。
SpringCloud微服务网关做边缘服务限流方案文章插图
【SpringCloud微服务网关做边缘服务限流方案】3.令牌桶算法令牌桶算法的原理是系统以一定速率向桶中放入令牌 , 如果有请求时 , 请求会从桶中取出令牌 , 如果能取到令牌 , 则可以继续完成请求 , 否则等待或者拒绝服务 。 这种算法可以应对突发程序的请求 , 因此比漏桶算法好 。
SpringCloud微服务网关做边缘服务限流方案文章插图
在Wikipedia上 , 令牌桶算法是这么描述的:
·每秒会有r个令牌放入桶中 , 或者说 , 每过1/r 秒桶中增加一个令牌·桶中最多存放b个令牌 , 如果桶满了 , 新放入的令牌会被丢弃·当一个n字节的数据包到达时 , 消耗n个令牌 , 然后发送该数据包·如果桶中可用令牌小于n , 则该数据包将被缓存或丢弃Spring Cloud Gateway限流在Spring Cloud Gateway中 , 有Filter过滤器 , 因此可以在“pre”类型的Filter中自行实现上述三种过滤器 。 但是限流作为网关最基本的功能 , Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类 , 适用Redis和lua脚本实现了令牌桶的方式 。 具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中 , lua脚本在如下图所示的文件夹中:
SpringCloud微服务网关做边缘服务限流方案文章插图
读者可以自行查看 , 先以案例的形式来讲解如何在Spring Cloud Gateway中使用内置的限流过滤器工厂来实现限流 。
首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖 , 代码如下:
org.springframework.cloudspring-cloud-starter-gatewayorg.springframework.bootspring-boot-starter-data-redis-reactive在配置文件中做以下的配置:
server:port: 8088spring:cloud:gateway:routes:- id: limit_routeuri:predicates:- After=2017-01-20T17:42:47.789-07:00[America/Denver]filters:- name: RequestRateLimiterargs:key-resolver: '#{@hostAddrKeyResolver}'redis-rate-limiter.replenishRate: 1redis-rate-limiter.burstCapacity: 3application:name: gateway-limiterredis:host: localhostport: 6379database: 0在上面的配置文件 , 指定程序的端口为8088 , 配置了 redis的信息 , 并配置了RequestRateLimiter的限流过滤器 , 该过滤器需要配置三个参数:
burstCapacity , 令牌桶总容量 。 replenishRate , 令牌桶每秒填充平均速率 。 key-resolver , 用于限流的键的解析器的 Bean 对象的名字 。 它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象 。 KeyResolver需要实现resolve方法 , 比如根据Hostname进行限流 , 则需要用hostAddress去判断 。 实现完KeyResolver之后 , 需要将这个类的Bean注册到Ioc容器中 。