任何系统都有流量的限制, 为了使我们的服务保持高可用性, 我们必须对系统进行限流。说到流量,可能来自于微服务集群外部,也可能来自微服务集群内部。如图一所示,服务A自身必须具备限流能力,以确保来自微服务网关的流量和内部应用的流量在自身可控范围内,保证服务高可用。除此之外微服务网关也应该具备限流能力,将外部流量先行进行拦截。
说到限流,算法才是灵魂。下面来说一下限流比较主流的四种算法:计数器算法,漏桶,令牌桶,滑动窗口。
计数器算法:比如一秒内只允许50个请求通过。算法实现思路是第一个请求进来计数为1,后面每通过一个请求计数加1,当计数器满50,后面请求全部拒绝。优点实现简单,利用计数器实现。缺点:当流量突然爆发,一旦计数器满,拒绝了所有后续请求。
漏桶算法:海量请求扑面而来,
可能瞬时就会把服务压垮, 而漏桶就可以用来限流削峰.漏桶是总容量是不变的, 水滴(请求) 以任意速率流入, 但总是以恒定速率流出,
如果请求来得太多太快, 桶的容量就会撑满, 后续的请求就会被拒绝, 也就是说当一个请求到来, 就流一滴水进桶里,如果可以放入, 则处理此请求,
否则漏桶已满, 则拒绝此请求, 直到桶中水滴不再满时。
令牌桶算法:令牌桶算法是对漏桶算法的改进,漏桶算法只能均匀的处理请求。令牌桶算法能够在均匀处理请求的情况下,处理一定程度的突发流量。令牌桶需要一个容器来存储令牌,令牌以一定速率均匀地向桶中存放,当超过桶的容量,桶会丢弃多余令牌。请求到来时从令牌桶中领取一个令牌才可继续处理服务, 如果取不到令牌, 则拒绝此请求。
滑动窗口:一种改进算法综合了固定窗口和滑动日志。有兴趣的小伙伴可以单独去了解这两种算法。 它结合了固定窗口算法的低处理成本和滑动日志的改进边界值, 将当前时间窗口与过去时间窗口综合考虑。与固定窗口算法一样,根据请求更改每个固定窗口的计数器。接下来,再根据当前时间戳计算出当前窗口的加权值, 以及上一个窗口的请求率的加权值,以限制流量突发。例如,如果当前窗口加权是20%,那么我们将前一个窗口的计数加权80%。
Spring cloud gateway限流实现,在Spring Cloud Gateway中,有Filter过滤器,因此可以在“pre”类型的Filter中自行实现上述计数器算法,漏桶,令牌桶限流算法。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,使用Redis和lua脚本实现令牌桶算法进行限流。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类种,lua脚本如所示。
首先在工程的pom文件中引入Gateway的起步依赖和Redis的reactive依赖,代码如下:
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.boot
spring-boot-starter-data-redis-reactive
在工程配置文件application.yml中添加一下配置:
server:
port: 8081
spring:
application:
name: gateway-limiter
redis:
host: localhost
port: 6379
database: 0
cloud:
gateway:
routes:
- id: limit_route
uri: http://httpbin.org:80/get
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
filters:
- name: RequestRateLimiter
args:
key-resolver: '#{@hostAddrKeyResolver}'
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3
上述配置文件,指定应用端口为8081,配置了redis的连接信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置3个参数。
· burstCapacity:令牌桶总容量。
· replenishRate:令牌桶每秒填充平均速率。
· key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式,根据#{@beanName}从 Spring 容器中获取 Bean 对象。
KeyResolver需要实现resolve方法,比如根据Hostname进行限流时,需要用hostAddress去判断。实现了KeyResolver之后,需要将这个类的Bean注册到Ioc容器中,具体代码如下:
public class HostAddrKeyResolver implements KeyResolver {
@Override
public Mono resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
@Bean
public HostAddrKeyResolver hostAddrKeyResolver() {
return new HostAddrKeyResolver();
}
接口限流
public class ApiKeyResolver implements KeyResolver {
@Override
public Mono resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getPath().value());
}
}
用户限流
public class UserResolver implements KeyResolver {
@Override
public Mono resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
}
关于 sentinel与nacos集成实现限流,下一篇文章中重点介绍。本篇不再赘述。
赵亮,2014年毕业于天津工业大学,现任职于北银金科银行转型业务开发部。主要擅长微服务架构,高并发和大数据处理,目前主要研究方向为微服务。
北银金融科技有限责任公司根植于北京银行,是一家致力于大数据、人工智能、云计算、区块链、物联网等新技术创新与金融科技应用的科技企业,公司充分发挥北京银行企业文化和技术积淀先天优势,通过对技术、场景、生态的完美融合,输出科技创新产品和技术服务。
现诚邀优秀人才加盟
共享金融科技时代硕果