共计 2512 个字符,预计需要花费 7 分钟才能阅读完成。
1、为什么要限流
一般而言,正常的流量越多越好,比如用户快速增长、热点事件带来的蜂拥的人流。但在实际的网络流量中,除正常的流量外,还有很多非正常的流量,比如网络攻击、恶意爬虫。所以在高并发的应用中,需要通过限流来保障服务对所有用户的可用性。限流和缓存、降级一样,也是保护高并发系统的利器。
2、常见的限流措施
高并发系统常采用以下限流措施:
-
限制总并发数。如,数据库连接池,线程池
-
限制瞬时并发数。如,Nginx 的 limit_conn 模块可以限制瞬时并发连接数
-
限制时间窗口内的平均速率。如,Nginx 的 limit_req 模块
-
限制消息中间件的消费速率
-
限制远程接口的调用速率
-
限制每秒的平均速率
-
对线程池进行隔离。如果超过线程池的负载,则进行熔断
-
通过 Tomcat 容器限制线程数来控制并发
一般限流都在网关层实现,比如使用 Nginx、Zuul、Spring Cloud Gateway、Openresty、Kong 等。
3、限流算法
3.1、计算器算法
算法原理:从第一个请求进来开始计时,在接下来时间内 (如 1s),每来一个请求就把计数加 1;如果累加的数字达到了设定的值,则后续的请求就会被全部拒绝;等单位时间结束后把计数恢复为 0,重新开始计数。
3.2、漏桶算法
算法原理: 把请求先放入漏桶里等待,然后漏桶以一定的速度处理进入漏桶中的请求;如果请求的进入速度过大,则导致漏桶装不下请求而拒绝后续的请求。
3.3、令牌桶算法
令牌桶算法和现在各大机构使用的叫号机很类似:当请求到达时,先去令牌桶中取一个令牌,然后等响应。
4、用 Spring Cloud Gateway 内置的限流工厂实现限流
4.1、添加依赖
Spring Cloud Gateway 内置了限流工厂 ”RequestRateLimiterGatewayFilterFactory”,它底层是使用 Redis 的 Lua 脚本实现的,所以在添加好 Spring Cloud Gateway 依赖后还需要添加 Redis 依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4.2、修改启动项,添加 IP 地址限流的 Bean
@SpringBootApplication
public class GatewayEurekaApplication {public static void main(String[] args) {SpringApplication.run(GatewayEurekaApplication.class, args);
}
@Bean
public KeyResolver ipKeyResolver(){// 根据 ip 地址限流
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
4.3、编写配置文件
spring.application.name=gateway-eureka
server.port=50024
#注册中心地址
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://eureka01:50025/eureka/,http://eureka02:50026//eureka/
#Redis 数据库索引 (默认为 0)
spring.redis.database=0
#redis 服务器 ip 地址
spring.redis.host=192.168.0.201
spring.redis.port=6379
#Redis 服务器的连接密码 (默认为空)
#id: 自定义路由 ID
spring.cloud.gateway.routes[0].id=ip_route1
#uri: 目标服务地址
spring.cloud.gateway.routes[0].uri=lb://OPEN-FEIGN
#predicates: 路由条件。Predicate 根据输入参数返回一个布尔值。其包含多种默认方法来将 Predicate 组合成复杂的路由逻辑
spring.cloud.gateway.routes[0].predicates[0]=Path=/hello
# 限流过滤器使用 gateway 内置令牌算法
spring.cloud.gateway.routes[0].filters[0].name=RequestRateLimiter
#令牌补充的频率,每次就一个
spring.cloud.gateway.routes[0].filters[0].args.redis-rate-limiter.replenishRate=1
#令牌桶的最大容量, 允许在一秒钟内完成的最大请求数
spring.cloud.gateway.routes[0].filters[0].args.redis-rate-limiter.burstCapacity=2
#用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据 #{@beanName} 从 Spring 容器中获取 Bean 对象。在 yml 配置文件中需要用双引号包裹。spring.cloud.gateway.routes[0].filters[0].args.key-resolver=#{@ipKeyResolver}
4.4、测试
步骤:
1、启动服务中心
2、启动服务提供者
3、启动服务消费者
4、启动网关工程
5、访问:http://localhost:50024/hello
当快速发送请求时,会进行限流服务不可用