参考: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. angular 示例项目

    cnpm install 环境安装好后,快速创建一个新项目,如在D盘下新建一个angularProject项目,只要在本地目录下 ng new angularProject angularProjec ...

  2. django编辑框实现

    一些常用的: CKEditor UEEditor TinyEditor KindEditor 下载: http://kindeditor.net/down.php 使用方法: <textarea ...

  3. layoutSubviews何时调用的问题(原文:http://www.cnblogs.com/pengyingh/articles/2417211.html)

    今天跟旺才兄学习了一下UIView的setNeedsDisplay和setNeedsLayout方法.首先两个方法都是异步执行的.而setNeedsDisplay会调用自动调用drawRect方法,这 ...

  4. 一、IIS搭建前端静态模板_资源加载问题

    一.模板文件说明和效果展示 二.IIS配置模板 三.解决方案 把资源文件复制到html目录内与index.htm同级,因为我iis指定站点就是该目录.

  5. Python列表(list)的方法调用

    #list# n = [12,34,"yue"]# v = n.append(27) #增加元素,注意是在尾部增加,由于列表是可修改的,所以是在原列表中增加,与字符串存在区别# p ...

  6. PAT Advanced 1031 Hello World for U (20 分)

    Given any string of N (≥) characters, you are asked to form the characters into the shape of U. For ...

  7. 02CSS

    1.简介 从事网页制作或者相关工作,就要学习HTML,CSS.其中HTML是网页制作的主要语言网页的基础,CSS层叠样式表,主要用来修饰页面的元素 CSS 是 Cascading Style Shee ...

  8. ubuntu 系统问题总结

    一.主题问题 高分辨率显示可能会造成虚拟机中的显示很小,需要调整合适的显示比例.但是 ubuntu 18.04 中的 display 的 scale 只能调整 100% 300%,可能使用 gnome ...

  9. 逻辑卷管理器(LVM)

    一.什么是LVM? LVM(Logical Volume Manager)逻辑卷管理是在Linux2.4内核以上实现的磁盘管理技术.它是Linux环境下对磁盘分区进行管理的一种机制.现在不仅仅是Lin ...

  10. bean获取Spring容器

    Person.java public class Person implements ApplicationContextAware{ private String name; private int ...