参考: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自定义限流的更多相关文章

  1. Spring Cloud Gateway 网关限流

    Spring Cloud Gateway 限流 一.背景 二.实现功能 三.网关层限流 1.使用默认的redis来限流 1.引入jar包 2.编写配置文件 3.网关正常响应 4.网关限流响应 2.自定 ...

  2. spring cloud gateway 之限流篇

    转载请标明出处: https://www.fangzhipeng.com 本文出自方志朋的博客 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方 ...

  3. 微服务架构spring cloud - gateway网关限流

    1.算法 在高并发的应用中,限流是一个绕不开的话题.限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击. 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池.线程池). ...

  4. 深入了解springcloud gateway 的限流重试机制

    前言 前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其他功能. Spring Cloud Gateway中的重试 我 ...

  5. Gateway的限流重试机制详解

    前言 想要源码地址的可以加上此微信:Lemon877164954  前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其 ...

  6. spring cloud gateway自定义过滤器

    在API网关spring cloud gateway和负载均衡框架ribbon实战文章中,主要实现网关与负载均衡等基本功能,详见代码.本节内容将继续围绕此代码展开,主要讲解spring cloud g ...

  7. Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置

    用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...

  8. Spring Boot Web 自定义注解篇(注解很简单很好用)

    自从spring 4.0 开放以后,可以添加很多新特性的注解了.使用系统定义好的注解可以大大方便的提高开发的效率. 下面我贴一段代码来讲解注解: 通过小小的注解我们支持了以下功能: 使 spring. ...

  9. Spring cloud gateway自定义filter以及负载均衡

    自定义全局filter package com.example.demo; import java.nio.charset.StandardCharsets; import org.apache.co ...

随机推荐

  1. 在Visual studio 2017中使用EF6连接MySQL

    在Visual studio 2017中使用EF6连接Mysql ADO.NET Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) ...

  2. 在java程序中利用线程

    package 第十一章; import java.awt.Button; import java.awt.Color; import java.awt.Font; import java.awt.F ...

  3. vue项目1-pizza点餐系统11-设计menu页面

    菜单的页面设计是基于bootstrap实现的,主要用到的是table标签,其中获取data数据用到遍历. <template> <div class=""> ...

  4. man - 格式化并显示在线帮助手册页

    总览 man [-acdfFhkKtwW] [-m 系统名] [-p <前处理程序>] [-C <配置文件>] [-M <路径>] [-P <浏览方式> ...

  5. Jmeter--函数助手之随机函数_Random(随机函数)

    各函数调用方法如下:1)__Random( , , ),获取值的方式:${__Random( param1,param2 ,param3 )},param1为随机数的下限,param2为随机数的上限, ...

  6. PAT Advanced 1036 Boys vs Girls (25 分)

    This time you are asked to tell the difference between the lowest grade of all the male students and ...

  7. Cron表达式 详解

    Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: (1) 7个域: Seconds Minutes Hours DayofMon ...

  8. php内置函数分析之array_change_key_case()

    PHP_FUNCTION(array_change_key_case) { zval *array, *entry; zend_string *string_key; zend_string *new ...

  9. solaris硬盘格式化分区

    创建EFI分区及挂载文件系统的过程: # format AVAILABLE DISK SELECTIONS: 0. c1t0d0 <LSI-MR9261-8i-2.12-557.86GB> ...

  10. vue开发使用vue-particles如何兼容IE11?

    最近开发vue项目,项目有一个粒子特效使用vue-particles.项目用vue-cli构建,webpack打包完毕,丢到服务器,chrome访问完美,测试360和Edge也正常.遗憾的是,在IE1 ...