Gateway
在微服务架构系统中,为系统内部服务提供一道安全屏障,提高系统可用性、安全性等问题。使用Gateway
可帮助开发人员快速开发应用,而不需要关心安全控制、流量控制、审计日志、版本控制等问题。
为什么需要限流
简单的说限流就是让控制流量让它们合理、平滑、有效的传递到内部服务,而不是随意想进就进,影响系统运行。比如以下一些情况就会使系统流量在短时间内增加从而导致一些意想不到的问题:
- 用户增长速度快
- 业务高峰时间段保证业务可用性
- 应对网络爬虫
- 防止恶意刷单、非法访问系统
- ...
上面列出的这些情况是很难预知的,如果某个事件导致服务的流量在很短时间内突然增长太快,在没有限流的情况下会导致服务不可用、系统压力大最终可能会使服务器自动重启(古时候是要杀头的~~)。相反如果有了限流就会好很多流量会得到有效控制,超过阈值的访问直接在网关层就会被拒绝,这样就可以减少服务压力,达到保护内部服务的目的。
限流策略
限制流量也不是随意限制的,限制的不好会适得其反,下面列举几种常见的限流策略:
- 按remote地址限流
- 按user限流
- 按Api限流
- 按
remote地址 + api
- 按
remote地址 + user
- ...
策略有很多,选择最合适的才是最好的,就拿remote ip
来限制来说,比如有几百个人在局域网内共享一个公网IP,系统每秒限制10个请求那这几百人在使用你们应用的时候经常会被拒绝,那体验也太差了,这种情况如果按user
来限制每个用户都能每秒发10个请求,再给api
加个总的限制也许就会比remote
策略好很多。所以在限制流量的时候也要考虑策略方面的因素,选择最适合业务场景的策略。
Spring Cloud Gateway限流原理
使用Spring Cloud Gateway
后,如果进行流量管理呢?下面我们就来探讨下如何使用Spring Cloud Gateway
进行流量管理,先简单了解下Spring Cloud Gateway
的工作原理:
经过上面图不难发现,流量会依次进入:
Gateway Handler Mapping
Gateway Web Handler
Filter
- 最后达到内部服务(Proxied Service)
到达Proxied Service
后的请求如何处理Gateway
就不管了,处理完成之后请求又回到Gateway
做后处理。搞清楚请求的走向,我们就可以在请求处理之前和之后对请求做一些不可描述的事了!!。
RequestRateLimiterGatewayFilterFactory 过滤器
Spring Cloud Gateway
很多内置的Filter
,如:AddRequestHeader
、AddRequestParameter
等等,这些都可以直接拿来使用,只需要简单配置参数即可。本文只关注RequestRateLimiter
内置的过滤器来完成限流。首先写一个测试接口:
package com.example.gateway;
@RestController
@RequestMapping("/traffic")
public class TrafficController {
@GetMapping
public ResponseEntity<String> testTrafficLimit(){
return ResponseEntity.ok("Success");
}
}
复制代码
然后再配置下路由:
spring:
cloud:
gateway:
routes:
- id: traffic_litmit
uri: http://localhost:8080
predicates:
- Path=/traffic_route
filters:
- RewritePath=/traffic_route, /traffic
- name: RequestRateLimiter
args:
rate-limiter: "#{@defaultRateLimiter}"
key-resolver: "#{@hostAddKeyResolver}"
复制代码
上面的配置添加一个名称为RequestRateLimiter
的滤器的,RequestRateLimiter
可以理解为一个过滤器工厂的名称,Spring Cloud Gateway
是通过GatewayFilter Factories
去实现过滤器的,RequestRateLimiter
与之对应的工厂类为RequestRateLimiterGatewayFilterFactory
,那RequestRateLimiterGatewayFilterFactory
是如何工作呢?
结合上面的图,简单的说差不多就二步:
- 获取key
- 判断是否允许访问
key是通过调用KeyResolver
实例来获取的,判断是否允许访问是通过调用RateLimiter
来决定的。分工还是很明确的,所以要使用Spring Cloud Gateway
限流只需要实现这两个接口再加上过滤器配置就可以啦。
使用Spring Cloud Gateway限流
上面已经大致介绍过Spring Cloud Gateway
限流的一些知识,再结合Guava
来做个简单的RateLimiter
来演示下限流的使用,话不多说上代码:
public class DefaultRateLimiter extends AbstractRateLimiter<DefaultRateLimiter.Config> {
Logger logger = LoggerFactory.getLogger(DefaultRateLimiter.class.getName());
/**
* 默认一秒一个请求,多了不收了~~
*/
private final RateLimiter limiter = RateLimiter.create(1);
public DefaultRateLimiter() {
super(Config.class, "default-rate-limit", null);
}
@Override
public Mono<Response> isAllowed(String routeId, String id) {
Config config = getConfig().get(routeId);
limiter.setRate(Objects.isNull(config.getPermitsPerSecond()) ? 1 : config.getPermitsPerSecond());
logger.info("Rate: {}", limiter.getRate());
boolean isAllow = limiter.tryAcquire();
logger.info("isAllow: {} , time: {}",isAllow, System.currentTimeMillis());
return Mono.just(new Response(isAllow, new HashMap<>()));
}
@Validated
public static class Config {
@DecimalMin("0.1")
private Double permitsPerSecond;
public Double getPermitsPerSecond() {
return permitsPerSecond;
}
public Config setPermitsPerSecond(Double permitsPerSecond) {
this.permitsPerSecond = permitsPerSecond;
return this;
}
}
}
复制代码
再修改一下过滤器的配置即可完成限流功能:
filters:
- RewritePath=/traffic_route, /traffic
- name: RequestRateLimiter
args:
rate-limiter: "#{@defaultRateLimiter}"
key-resolver: "#{@hostAddKeyResolver}"
default-rate-limit.permitsPerSecond: 0.5
复制代码
这样就完成一个自定义的RateLimiter
了, 如果没有什么特殊要求的话可以直接使用RedisRateLimiter
来实现限流,这个是内置限流器只需要简单配置两个参数就能使用:
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 2
复制代码
参数说明:
- redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求
- redis-rate-limiter.burstCapacity:令牌桶的容量,允许在一秒钟内完成的最大请求数
总结
本文简单的介绍了网关的一些知识和Spring Cloud Gateway
限流的实现,希望对大家有所帮助,最后欢迎大家关注公众号:Java派
一起交流学习Java、Android知识。