spring-cloud-zuul-ratelimit 用于在基于 Spring Cloud Zuul 构建的微服务网关中实现速率限制(限流)的组件。它能够控制进入微服务系统的流量,防止系统因过多的请求而崩溃或性能下降。在分布式系统中,尤其是在面对高并发场景时,限流是保障系统稳定性和可用性的重要手段。
spring-cloud-zuul-ratelimit 中有五种内置的速率限制方法:
USER:使用经过身份验证的用户名或“anonymous”。
ORIGIN:使用用户源请求。
URL:使用下游服务的请求路径。
URL_PATTERN:使用请求的 Ant 路径模式到下游服务。
ROLE:使用经过身份验证的用户角色。
HTTP_METHOD:使用 HTTP 请求方法。
HTTP_HEADER:使用 HTTP 请求头。
每个服务的全局配置:
此配置不验证请求来源、已认证用户或统一资源标识符(URI)。
要使用这种方法,只需不设置参数“type”。
注意:可以将已认证用户、请求来源、统一资源定位符(URL)、角色(ROLE)和请求方法结合起来,只需向列表中添加多个值即可。
最后版本:2.4.3.RELEASE
如果您使用的是 Spring Boot 1.5.x 版本,则必须使用 Spring Cloud Zuul RateLimit 1.7.x 版本。
在 pom.xml 中添加依赖项:
<dependency> <groupId>com.marcosbarbero.cloud</groupId> <artifactId>spring-cloud-zuul-ratelimit</artifactId> <version>${latest-version}</version> </dependency>
根据所选的数据存储相应地添加以下依赖项:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
此实现还需要一个数据库表,下面你可以找到一个示例脚本:
CREATE TABLE rate ( rate_key VARCHAR(255) NOT NULL, remaining BIGINT, remaining_quota BIGINT, reset BIGINT, expiration TIMESTAMP, PRIMARY KEY(rate_key) );
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> </dependency> <dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-jcache</artifactId> </dependency> <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> </dependency>
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-hazelcast</artifactId> </dependency> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast</artifactId> </dependency>
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-infinispan</artifactId> </dependency> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-core</artifactId> </dependency>
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-ignite</artifactId> </dependency> <dependency> <groupId>org.apache.ignite</groupId> <artifactId>ignite-core</artifactId> </dependency>
示例 YAML 配置:
zuul: ratelimit: key-prefix: your-prefix enabled: true repository: REDIS behind-proxy: true add-response-headers: true deny-request: response-status-code: 404 #default value is 403 (FORBIDDEN) origins: - 200.187.10.25 - somedomain.com default-policy-list: #optional - will apply unless specific policy exists - limit: 10 #optional - request number limit per refresh interval window quota: 1000 #optional - request time limit per refresh interval window (in seconds) refresh-interval: 60 #default value (in seconds) type: #optional - user - origin - url - http_method policy-list: myServiceId: - limit: 10 #optional - request number limit per refresh interval window quota: 1000 #optional - request time limit per refresh interval window (in seconds) refresh-interval: 60 #default value (in seconds) type: #optional - user - origin - url - type: #optional value for each type - user=anonymous - origin=somemachine.com - url=/api #url prefix - role=user - http_method=get #case insensitive - http_header=customHeader - type: - url_pattern=/api/*/payment
简单 Properties 配置:
zuul.ratelimit.enabled=true zuul.ratelimit.key-prefix=your-prefix zuul.ratelimit.repository=REDIS zuul.ratelimit.behind-proxy=true zuul.ratelimit.add-response-headers=true zuul.ratelimit.deny-request.response-status-code=404 zuul.ratelimit.deny-request.origins[0]=200.187.10.25 zuul.ratelimit.deny-request.origins[1]=somedomain.com zuul.ratelimit.default-policy-list[0].limit=10 zuul.ratelimit.default-policy-list[0].quota=1000 zuul.ratelimit.default-policy-list[0].refresh-interval=60 # Adding multiple rate limit type zuul.ratelimit.default-policy-list[0].type[0]=user zuul.ratelimit.default-policy-list[0].type[1]=origin zuul.ratelimit.default-policy-list[0].type[2]=url zuul.ratelimit.default-policy-list[0].type[3]=http_method # Adding the first rate limit policy to "myServiceId" zuul.ratelimit.policy-list.myServiceId[0].limit=10 zuul.ratelimit.policy-list.myServiceId[0].quota=1000 zuul.ratelimit.policy-list.myServiceId[0].refresh-interval=60 zuul.ratelimit.policy-list.myServiceId[0].type[0]=user zuul.ratelimit.policy-list.myServiceId[0].type[1]=origin zuul.ratelimit.policy-list.myServiceId[0].type[2]=url # Adding the second rate limit policy to "myServiceId" zuul.ratelimit.policy-list.myServiceId[1].type[0]=user=anonymous zuul.ratelimit.policy-list.myServiceId[1].type[1]=origin=somemachine.com zuul.ratelimit.policy-list.myServiceId[1].type[2]=url_pattern=/api/*/payment zuul.ratelimit.policy-list.myServiceId[1].type[3]=role=user zuul.ratelimit.policy-list.myServiceId[1].type[4]=http_method=get zuul.ratelimit.policy-list.myServiceId[1].type[5]=http_header=customHeader
“quota”(配额)和“refresh-interval”(刷新间隔)都可以用 Spring Boot 的时长格式表示:
常规的长格式表示(默认以秒为单位)
Java 的 java.time.Duration 所使用的标准 ISO-8601 格式(例如 PT30M 表示 30 分钟)。
一种更易读的格式,其中值和单位结合在一起(例如,10s 表示 10 秒)。
提供了八种实现方式:
实现 | 数据存储 |
ConsulRateLimiter | |
RedisRateLimiter | |
SpringDataRateLimiter | |
Bucket4jJCacheRateLimiter | |
Bucket4jHazelcastRateLimiter | |
Bucket4jIgniteRateLimiter | |
Bucket4jInfinispanRateLimiter |
Bucket4j 实现需要使用 @Qualifier("RateLimit") 的相关 Bean:
JCache - javax.cache.Cache
Hazelcast - com.hazelcast.map.IMap
Ignite - org.apache.ignite.IgniteCache
Infinispan - org.infinispan.functional.ReadWriteMap
属性命名空间:zuul.ratelimit
属性名 | 值 | 默认值 |
enabled | true/false | false |
behind-proxy | true/false | false |
response-headers | NONE, STANDARD, VERBOSE | VERBOSE |
key-prefix | String | ${spring.application.name:rate-limit-application} |
repository | CONSUL, REDIS, JPA, BUCKET4J_JCACHE, BUCKET4J_HAZELCAST, BUCKET4J_INFINISPAN, BUCKET4J_IGNITE | - |
deny-request | - | |
default-policy-list | List of Policy | - |
policy-list | Map of Lists of Policy | - |
postFilterOrder | int | FilterConstants.SEND_RESPONSE_FILTER_ORDER - 10 |
preFilterOrder | int | FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER |
拒绝请求属性:
属性名 | 值 | 默认值 |
origins | 被拒绝访问的来源列表表 | - |
response-status-code | 被拒绝请求时要返回的 HTTP 状态码码 | 403 (FORBIDDEN) |
策略属性:
属性名 | 值 | 默认值 |
limit | 请求数量 | - |
quota | 请求时间 | - |
refresh-interval | 秒数 | 60 |
type | [ORIGIN, USER, URL, URL_PATTERN, ROLE, HTTP_METHOD, HTTP_HEADER] | [] |
breakOnMatch | true/false | false |
本节将详细介绍如何添加自定义实现
如果应用程序需要在 type 属性提供的选项之外控制密钥策略,那么只需创建一个自定义的 RateLimitKeyGenerator bean 实现,添加更多限定符或其他完全不同的东西即可:
@Bean public RateLimitKeyGenerator ratelimitKeyGenerator(RateLimitProperties properties, RateLimitUtils rateLimitUtils) { return new DefaultRateLimitKeyGenerator(properties, rateLimitUtils) { @Override public String key(HttpServletRequest request, Route route, RateLimitProperties.Policy policy) { return super.key(request, route, policy) + ":" + request.getMethod(); } }; }
本框架使用第三方应用程序控制速率限制访问,这些库不受本框架控制。如果其中一个第三方应用程序出现故障,框架将在 DefaultRateLimiterErrorHandler 类中处理该故障,并在故障发生时记录错误日志。
如果需要以不同方式处理错误,可以通过定义自定义的 RateLimiterErrorHandler Bean 来实现,例如:
@Bean public RateLimiterErrorHandler rateLimitErrorHandler() { return new DefaultRateLimiterErrorHandler() { @Override public void handleSaveError(String key, Exception e) { // custom code } @Override public void handleFetchError(String key, Exception e) { // custom code } @Override public void handleError(String msg, Exception e) { // custom code } } }
如果应用程序需要在超过速率限制访问时收到通知,则可以通过监听 RateLimitExceededEvent 事件来实现:
@EventListener public void observe(RateLimitExceededEvent event) { // custom code }