spring boot gateway自定义限流
参考:https://blog.csdn.net/ErickPang/article/details/84680132
采用自带默认网关请参照微服务架构spring cloud - gateway网关限流,参数与其唯一的区别是header中多了参数userLevel,值为A或者B
此处实现按传入参数取到不同配置
userLvl.A.replenishRate: 10
userLvl.A.burstCapacity: 100
userLvl.B.replenishRate: 20
userLvl.B.burstCapacity: 1000 自定义限流器
package com.gatewayaop.filter; import com.iot.crm.gatewayaop.common.config.UserLevelRateLimiterConf;
import org.springframework.beans.BeansException;
import org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import javax.validation.constraints.Min;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean; public class UserLevelRedisRateLimiter extends AbstractRateLimiter<UserLevelRedisRateLimiter.Config> implements ApplicationContextAware {
//这些变量全部从RedisRateLimiter复制的,都会用到。
public static final String REPLENISH_RATE_KEY = "replenishRate"; public static final String BURST_CAPACITY_KEY = "burstCapacity"; public static final String CONFIGURATION_PROPERTY_NAME = "sys-redis-rate-limiter";
public static final String REDIS_SCRIPT_NAME = "redisRequestRateLimiterScript";
public static final String REMAINING_HEADER = "X-RateLimit-Remaining";
public static final String REPLENISH_RATE_HEADER = "X-RateLimit-Replenish-Rate";
public static final String BURST_CAPACITY_HEADER = "X-RateLimit-Burst-Capacity"; //处理速度
private static final String DEFAULT_REPLENISHRATE="default.replenishRate";
//容量
private static final String DEFAULT_BURSTCAPACITY="default.burstCapacity"; private ReactiveRedisTemplate<String, String> redisTemplate;
private RedisScript<List<Long>> script;
private AtomicBoolean initialized = new AtomicBoolean(false); private String remainingHeader = REMAINING_HEADER; /** The name of the header that returns the replenish rate configuration. */
private String replenishRateHeader = REPLENISH_RATE_HEADER; /** The name of the header that returns the burst capacity configuration. */
private String burstCapacityHeader = BURST_CAPACITY_HEADER; private Config defaultConfig; public UserLevelRedisRateLimiter(ReactiveRedisTemplate<String, String> redisTemplate,
RedisScript<List<Long>> script, Validator validator) {
super(Config.class , CONFIGURATION_PROPERTY_NAME , validator);
this.redisTemplate = redisTemplate;
this.script = script;
initialized.compareAndSet(false,true);
} public UserLevelRedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity){
super(Config.class , CONFIGURATION_PROPERTY_NAME , null);
defaultConfig = new Config()
.setReplenishRate(defaultReplenishRate)
.setBurstCapacity(defaultBurstCapacity); }
//具体限流实现,此处调用的是lua脚本
@Override
public Mono<Response> isAllowed(String routeId, String id) {
if (!this.initialized.get()) {
throw new IllegalStateException("RedisRateLimiter is not initialized");
}
if (ObjectUtils.isEmpty(rateLimiterConf) ){
throw new IllegalArgumentException("No Configuration found for route " + routeId);
}
//获取的是自定义的map
Map<String , Integer> rateLimitMap = rateLimiterConf.getRateLimitMap();
//缓存的key,此处routeId为userSev,Id为header参数userLevel的值(A或者B)
String replenishRateKey = routeId + "." + id + "." + REPLENISH_RATE_KEY;
//若map中不存在则采用默认值,存在则取值。
int replenishRate = ObjectUtils.isEmpty(rateLimitMap.get(replenishRateKey)) ? rateLimitMap.get(DEFAULT_REPLENISHRATE) : rateLimitMap.get(replenishRateKey);
//容量key
String burstCapacityKey = routeId + "." + id + "." + BURST_CAPACITY_KEY;
//若map中不存在则采用默认值,存在则取值。
int burstCapacity = ObjectUtils.isEmpty(rateLimitMap.get(burstCapacityKey)) ? rateLimitMap.get(DEFAULT_BURSTCAPACITY) : rateLimitMap.get(burstCapacityKey); try {
List<String> keys = getKeys(id); List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "",
Instant.now().getEpochSecond() + "", "1");
Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys, scriptArgs); return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L)))
.reduce(new ArrayList<Long>(), (longs, l) -> {
longs.addAll(l);
return longs;
}) .map(results -> {
boolean allowed = results.get(0) == 1L;
Long tokensLeft = results.get(1); RateLimiter.Response response = new RateLimiter.Response(allowed, getHeaders(replenishRate , burstCapacity , tokensLeft)); return response;
});
} catch (Exception e) {
e.printStackTrace();
} return Mono.just(new RateLimiter.Response(true, getHeaders(replenishRate , burstCapacity , -1L)));
} private UserLevelRateLimiterConf rateLimiterConf; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.rateLimiterConf = applicationContext.getBean(UserLevelRateLimiterConf.class);
} public HashMap<String, String> getHeaders(Integer replenishRate, Integer burstCapacity , Long tokensLeft) {
HashMap<String, String> headers = new HashMap<>();
headers.put(this.remainingHeader, tokensLeft.toString());
headers.put(this.replenishRateHeader, String.valueOf(replenishRate));
headers.put(this.burstCapacityHeader, String.valueOf(burstCapacity));
return headers;
} static List<String> getKeys(String id) {
// use `{}` around keys to use Redis Key hash tags
// this allows for using redis cluster // Make a unique key per user.
//此处可以自定义redis前缀信息
String prefix = "request_sys_rate_limiter.{" + id; // You need two Redis keys for Token Bucket.
String tokenKey = prefix + "}.tokens";
String timestampKey = prefix + "}.timestamp";
return Arrays.asList(tokenKey, timestampKey);
} @Validated
public static class Config{
@Min(1)
private int replenishRate;
@Min(1)
private int burstCapacity = 1; public int getReplenishRate() {
return replenishRate;
} public Config setReplenishRate(int replenishRate) {
this.replenishRate = replenishRate;
return this;
} public int getBurstCapacity() {
return burstCapacity;
} public Config setBurstCapacity(int burstCapacity) {
this.burstCapacity = burstCapacity;
return this;
} @Override
public String toString() {
return "Config{" +
"replenishRate=" + replenishRate +
", burstCapacity=" + burstCapacity +
'}';
}
}
}
读取自定义配置类
package com.gatewayaop.common.config; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; //使用配置文件的方式进行初始化 @Component
@ConfigurationProperties(prefix = "comsumer.ratelimiter-conf")
//@EnableConfigurationProperties(UserLevelRateLimiterConf.class)
public class UserLevelRateLimiterConf {
//处理速度
private static final String DEFAULT_REPLENISHRATE="default.replenishRate";
//容量
private static final String DEFAULT_BURSTCAPACITY="default.burstCapacity"; //默认配置
private Map<String , Integer> rateLimitMap = new ConcurrentHashMap<String , Integer>(){
{
put(DEFAULT_REPLENISHRATE , 10);
put(DEFAULT_BURSTCAPACITY , 100);
}
}; public Map<String, Integer> getRateLimitMap() {
return rateLimitMap;
} public void setRateLimitMap(Map<String, Integer> rateLimitMap) {
this.rateLimitMap = rateLimitMap;
}
}
定义限流器种类
package com.gatewayaop.common.config; import com.iot.crm.gatewayaop.filter.UserLevelRedisRateLimiter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.validation.Validator;
import reactor.core.publisher.Mono; import java.util.List; @Configuration
public class RequestRateLimiterConfig {
@Bean
@Primary
KeyResolver apiKeyResolver() {
//按URL限流
return exchange -> Mono.just(exchange.getRequest().getPath().toString());
} @Bean
KeyResolver userKeyResolver() {
//按用户限流
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
} @Bean
KeyResolver ipKeyResolver() {
//按IP来限流
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
} @Bean
KeyResolver userLevelKeyResolver() {
//按IP来限流
return exchange -> Mono.just(exchange.getRequest().getHeaders().getFirst("userLevel"));
} @Bean
@Primary
//使用自己定义的限流类
UserLevelRedisRateLimiter userLevelRedisRateLimiter(
ReactiveRedisTemplate<String, String> redisTemplate,
@Qualifier(UserLevelRedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript<List<Long>> script,
@Qualifier("defaultValidator") Validator validator){
return new UserLevelRedisRateLimiter(redisTemplate , script , validator);
} }
yml配置
server:
port: 9701 spring:
application:
name: gateway-aop-dev
profiles:
active: dev
index: 62
cloud:
gateway:
discovery:
locator:
enabled: true
# 服务名小写
lower-case-service-id: true
routes:
#与customer.中key相同即是java代码中的routeID
- id: userSev
# lb代表从注册中心获取服务,且已负载均衡方式转发
uri: lb://hello-dev
predicates:
- Path=/hello-dev/**
# 加上StripPrefix=1,否则转发到后端服务时会带上consumer前缀
filters:
- StripPrefix=1
# 限流过滤器,使用gateway内置令牌算法
- name: RequestRateLimiter
args:
# # 令牌桶每秒填充平均速率,即行等价于允许用户每秒处理多少个请求平均数
# redis-rate-limiter.replenishRate: 10
# # 令牌桶的容量,允许在一秒钟内完成的最大请求数
# redis-rate-limiter.burstCapacity: 20
# 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
key-resolver: "#{@userLevelKeyResolver}"
rate-limiter: "#{@userLevelRedisRateLimiter}"
comsumer:
ratelimiter-conf:
#配置限流参数与RateLimiterConf类映射
rateLimitMap:
#格式为:routeid(gateway配置routes时指定的).系统名称.replenishRate(流速)/burstCapacity令牌桶大小
userSev.A.replenishRate: 10
userSev.A.burstCapacity: 100
userSev.B.replenishRate: 20
userSev.B.burstCapacity: 1000
spring boot gateway自定义限流的更多相关文章
- Spring Cloud Gateway 网关限流
Spring Cloud Gateway 限流 一.背景 二.实现功能 三.网关层限流 1.使用默认的redis来限流 1.引入jar包 2.编写配置文件 3.网关正常响应 4.网关限流响应 2.自定 ...
- spring cloud gateway 之限流篇
转载请标明出处: https://www.fangzhipeng.com 本文出自方志朋的博客 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方 ...
- 微服务架构spring cloud - gateway网关限流
1.算法 在高并发的应用中,限流是一个绕不开的话题.限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击. 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池.线程池). ...
- 深入了解springcloud gateway 的限流重试机制
前言 前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其他功能. Spring Cloud Gateway中的重试 我 ...
- Gateway的限流重试机制详解
前言 想要源码地址的可以加上此微信:Lemon877164954 前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其 ...
- spring cloud gateway自定义过滤器
在API网关spring cloud gateway和负载均衡框架ribbon实战文章中,主要实现网关与负载均衡等基本功能,详见代码.本节内容将继续围绕此代码展开,主要讲解spring cloud g ...
- Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...
- Spring Boot Web 自定义注解篇(注解很简单很好用)
自从spring 4.0 开放以后,可以添加很多新特性的注解了.使用系统定义好的注解可以大大方便的提高开发的效率. 下面我贴一段代码来讲解注解: 通过小小的注解我们支持了以下功能: 使 spring. ...
- Spring cloud gateway自定义filter以及负载均衡
自定义全局filter package com.example.demo; import java.nio.charset.StandardCharsets; import org.apache.co ...
随机推荐
- activemq热备与消息丢失
1. 解压 tar -zxvf apache-activemq-5.12.0-bin.tar.gz2. 改名 mv apache-activemq-5.12.0 activemq3. cd activ ...
- sublime3故障收集emmet无法安装pyv8
本文主要介绍Sublime Text如何开启debug模式,分析使用过程中一些常见错误情形的解决方法.情形一:Package Control:There are no packages availab ...
- JS事件流、事件监听、事件对象、事件委托
JS事件流: 01.DOM级别和DOM事件 02.JS事件流:页面中接收事件的顺序 事件冒泡阶段-->处于目标阶段-->事件捕获阶段 (事件捕获总发生在事件冒泡前面) 03.捕获:从外向里 ...
- 自动布局(storyboard,code)
xcode 6使用storyboard 进行自动布局,迷惑的问题主要由: 1,classsize 到底是一个什么东东? 2,classSize 和 layout 有什么区别? 3, 如何使用stor ...
- VB中IIF函数
IIf 函数语法:IIf(表达式, 真值部分, 假值部分)根据表达式的值,表达式为真时,返回真值部分,表达式为假时,返回假部分.如:iif(a>0, "对","错& ...
- evpp心跳机制
client server xin good
- [效率神技]Intellij 的快捷键和效率技巧|系列一|常用快捷键
Intellij 是个功能强大的IDE,这里只讲window下社区版的Intellij. 1. 常用快捷: Alt+回车 导入包,自动修正Ctrl+N 查找类Ctrl+Shift+N 查找文件Ct ...
- 算法竞赛进阶指南 0x00 基本算法
放在原来这个地方不太方便,影响阅读体验.为了读者能更好的刷题,另起一篇随笔. 0x00 基本算法 0x01 位运算 [题目][64位整数乘法] 知识点:快速幂思想的灵活运用 [题目][最短Hamilt ...
- RestTemplate 超级严重BUG之 restTemplate.getForEntity对于下载文件的地址请求 header不起作用
错误下载:RestTemplate restTemplate=new RestTemplate();HttpHeaders httpHeaders=new HttpHeaders();httpHead ...
- bzoj4819 [Sdoi2017]新生舞会 分数规划+最大费用最大流
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4819 题解 首先上面说, \[ C = \frac{\sum\limits_{i=1}^n a ...