支持接口限流,优化代码,引入异步, 提高性能
This commit is contained in:
parent
220ec74d5a
commit
7fbb842597
@ -64,6 +64,10 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-pool2</artifactId>
|
<artifactId>commons-pool2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--
|
<!--
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -13,11 +13,11 @@ import io.vertx.ext.web.RoutingContext;
|
|||||||
@VertxGen
|
@VertxGen
|
||||||
public interface RateLimitHandler extends Handler<RoutingContext> {
|
public interface RateLimitHandler extends Handler<RoutingContext> {
|
||||||
|
|
||||||
static RateLimitHandler create(String instance) {
|
static RateLimitHandler create(String instance, int pattern) {
|
||||||
switch (instance) {
|
switch (instance) {
|
||||||
case "redis":
|
case "redis":
|
||||||
RedisRateLimiter redisRateLimiter = new RedisRateLimiter();
|
RedisRateLimiter redisRateLimiter = new RedisRateLimiter();
|
||||||
return new RateLimitHandlerRedisImpl(redisRateLimiter);
|
return new RateLimitHandlerRedisImpl(redisRateLimiter, pattern);
|
||||||
default:
|
default:
|
||||||
// 本地缓存
|
// 本地缓存
|
||||||
return null;
|
return null;
|
||||||
|
@ -17,9 +17,11 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class RateLimitHandlerRedisImpl implements RateLimitHandler {
|
public class RateLimitHandlerRedisImpl implements RateLimitHandler {
|
||||||
private RedisRateLimiter rateLimiter;
|
private RedisRateLimiter rateLimiter;
|
||||||
|
private int pattern;// 1:app,2:接口默认,3:服务接口配置限流策略
|
||||||
|
|
||||||
public RateLimitHandlerRedisImpl(RedisRateLimiter rateLimiter) {
|
public RateLimitHandlerRedisImpl(RedisRateLimiter rateLimiter, int pattern) {
|
||||||
this.rateLimiter = rateLimiter;
|
this.rateLimiter = rateLimiter;
|
||||||
|
this.pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -30,7 +32,7 @@ public class RateLimitHandlerRedisImpl implements RateLimitHandler {
|
|||||||
// rc.next();
|
// rc.next();
|
||||||
// return;
|
// return;
|
||||||
// 获取模式
|
// 获取模式
|
||||||
int pattern = 1; // 1:app,2:接口默认,3:服务接口配置限流策略
|
// TODO redis连不上, 切换到内存模式
|
||||||
if (rateLimiter.acquire(rc, pattern)) {
|
if (rateLimiter.acquire(rc, pattern)) {
|
||||||
log.info("rateLimiter.acquire true");
|
log.info("rateLimiter.acquire true");
|
||||||
rc.next();
|
rc.next();
|
||||||
@ -38,14 +40,15 @@ public class RateLimitHandlerRedisImpl implements RateLimitHandler {
|
|||||||
} else {
|
} else {
|
||||||
switch (pattern) {
|
switch (pattern) {
|
||||||
case 1:
|
case 1:
|
||||||
AppCurrentLimitingConfig appCurrentLimitingConfig = AppConfigServiceImpl
|
AppCurrentLimitingConfig appCurrentLimitingConfig = AppConfigServiceImpl.getAppCurrentLimitingConfig(sacAppHeaderKey);
|
||||||
.getAppCurrentLimitingConfig(sacAppHeaderKey);
|
|
||||||
rc.fail(new HttpException(429, appCurrentLimitingConfig.getDefaultResponse()));
|
rc.fail(new HttpException(429, appCurrentLimitingConfig.getDefaultResponse()));
|
||||||
return;
|
return;
|
||||||
// JsonObject dataJson = new JsonObject(appCurrentLimitingConfig.getDefaultResponse());
|
default:
|
||||||
// rc.response().setChunked(true).setStatusCode(429).putHeader("Content-Type", "application/json").end(dataJson.toBuffer());
|
AppCurrentLimitingConfig appCurrentLimitingConfig1 = AppConfigServiceImpl.getAppCurrentLimitingConfig(sacAppHeaderKey);
|
||||||
// return;
|
rc.fail(new HttpException(430, appCurrentLimitingConfig1.getDefaultResponse()));
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,14 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
|
||||||
|
import com.sf.vertx.api.pojo.ApiCurrentLimitingConfig;
|
||||||
import com.sf.vertx.api.pojo.AppCurrentLimitingConfig;
|
import com.sf.vertx.api.pojo.AppCurrentLimitingConfig;
|
||||||
import com.sf.vertx.constans.RedisKeyConfig;
|
import com.sf.vertx.constans.RedisKeyConfig;
|
||||||
import com.sf.vertx.init.DynamicBuildServer;
|
import com.sf.vertx.init.DynamicBuildServer;
|
||||||
import com.sf.vertx.service.impl.AppConfigServiceImpl;
|
import com.sf.vertx.service.impl.AppConfigServiceImpl;
|
||||||
import com.sf.vertx.utils.SpringUtils;
|
import com.sf.vertx.utils.SpringUtils;
|
||||||
|
|
||||||
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import io.vertx.ext.web.RoutingContext;
|
import io.vertx.ext.web.RoutingContext;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@ -18,10 +20,17 @@ public class RedisRateLimiter {
|
|||||||
public Boolean acquire(RoutingContext rc, int pattern) {
|
public Boolean acquire(RoutingContext rc, int pattern) {
|
||||||
String appCode = rc.request().headers().get(DynamicBuildServer.SAC_APP_HEADER_KEY);
|
String appCode = rc.request().headers().get(DynamicBuildServer.SAC_APP_HEADER_KEY);
|
||||||
// TODO 先测试app模式,后面通过app缓存获取模式
|
// TODO 先测试app模式,后面通过app缓存获取模式
|
||||||
|
String key = null;
|
||||||
switch (pattern) {
|
switch (pattern) {
|
||||||
case 1:
|
case 1:
|
||||||
String key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY +":"+ appCode;
|
AppCurrentLimitingConfig appCurrentLimitingConfig = AppConfigServiceImpl.getAppCurrentLimitingConfig(appCode);
|
||||||
return rateLimiter(key, appCode);
|
key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY + ":" + appCode;
|
||||||
|
return rateLimiter(key, appCode, appCurrentLimitingConfig.getThreshold(), appCurrentLimitingConfig.getTimeWindow());
|
||||||
|
case 2:
|
||||||
|
ApiCurrentLimitingConfig apiCurrentLimitingConfig = AppConfigServiceImpl.getApiCurrentLimitingConfig(appCode);
|
||||||
|
key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY + ":" + appCode + ":" + rc.request().uri() + ":"
|
||||||
|
+ rc.request().method();
|
||||||
|
return rateLimiter(key, appCode, apiCurrentLimitingConfig.getThreshold(), apiCurrentLimitingConfig.getTimeWindow());
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -30,24 +39,34 @@ public class RedisRateLimiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Boolean rateLimiter(String key, String appCode) {
|
private Boolean rateLimiter(String key, String appCode, Integer threshold, Integer timeWindow) {
|
||||||
RedisTemplate<String, Integer> redisTemplate = SpringUtils.getBean("redisTemplate", RedisTemplate.class);
|
RedisTemplate<String, Integer> redisTemplate = SpringUtils.getBean("redisTemplate", RedisTemplate.class);
|
||||||
AppCurrentLimitingConfig appCurrentLimitingConfig = AppConfigServiceImpl.getAppCurrentLimitingConfig(appCode);
|
|
||||||
Object count = redisTemplate.opsForValue().get(key);
|
Object count = redisTemplate.opsForValue().get(key);
|
||||||
// redisTemplate.delete(key);
|
// redisTemplate.delete(key);
|
||||||
log.info("count:{}", count);
|
log.info("count:{}", count);
|
||||||
if (count == null) {
|
if (count == null) {
|
||||||
// 初始化,设置过期时间
|
// 初始化,设置过期时间
|
||||||
redisTemplate.opsForValue().increment(key);
|
ThreadUtil.execAsync(() -> {
|
||||||
log.info("redis app threshold: {}", redisTemplate.opsForValue().get(key));
|
add(timeWindow, redisTemplate, key);
|
||||||
redisTemplate.expire(key, appCurrentLimitingConfig.getTimeWindow(), TimeUnit.SECONDS);
|
});
|
||||||
} else if(Integer.valueOf(count.toString()) < appCurrentLimitingConfig.getThreshold()) {
|
} else if (Integer.valueOf(count.toString()) < threshold) {
|
||||||
redisTemplate.opsForValue().increment(key);
|
ThreadUtil.execAsync(() -> {
|
||||||
|
increment(redisTemplate, key);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void add(Integer timeWindow, RedisTemplate<String, Integer> redisTemplate,
|
||||||
|
String key) {
|
||||||
|
redisTemplate.opsForValue().increment(key);
|
||||||
|
log.info("redis app threshold: {}", redisTemplate.opsForValue().get(key));
|
||||||
|
redisTemplate.expire(key, timeWindow, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void increment(RedisTemplate<String, Integer> redisTemplate, String key) {
|
||||||
|
redisTemplate.opsForValue().increment(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -128,7 +128,9 @@ public class DynamicBuildServer implements ApplicationRunner {
|
|||||||
|
|
||||||
// TODO 实例化方式 从 VertxConfig 读取
|
// TODO 实例化方式 从 VertxConfig 读取
|
||||||
String instance = "redis";
|
String instance = "redis";
|
||||||
mainHttpRouter.route().handler(RateLimitHandler.create(instance)).handler(BodyHandler.create())
|
// 需要从配置拿,先测功能
|
||||||
|
int pattern = 2;// 1:app,2:接口默认,3:服务接口配置限流策略
|
||||||
|
mainHttpRouter.route().handler(RateLimitHandler.create(instance, pattern)).handler(BodyHandler.create())
|
||||||
.handler(ProxyHandler.create(mainWebClient, proxy)).failureHandler(RestfulFailureHandler.create());
|
.handler(ProxyHandler.create(mainWebClient, proxy)).failureHandler(RestfulFailureHandler.create());
|
||||||
//mainHttpRouter.route().handler(BodyHandler.create()).handler(ProxyHandler.create(mainWebClient,proxy));
|
//mainHttpRouter.route().handler(BodyHandler.create()).handler(ProxyHandler.create(mainWebClient,proxy));
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.alibaba.fastjson2.TypeReference;
|
import com.alibaba.fastjson2.TypeReference;
|
||||||
|
import com.sf.vertx.api.pojo.ApiCurrentLimitingConfig;
|
||||||
import com.sf.vertx.api.pojo.AppConfig;
|
import com.sf.vertx.api.pojo.AppConfig;
|
||||||
import com.sf.vertx.api.pojo.AppCurrentLimitingConfig;
|
import com.sf.vertx.api.pojo.AppCurrentLimitingConfig;
|
||||||
import com.sf.vertx.api.pojo.VertxConfig;
|
import com.sf.vertx.api.pojo.VertxConfig;
|
||||||
@ -29,6 +30,7 @@ public class AppConfigServiceImpl implements AppConfigService {
|
|||||||
private RedisTemplate<String, String> redisTemplate;
|
private RedisTemplate<String, String> redisTemplate;
|
||||||
private static final ConcurrentHashMap<String, AppConfig> CACHE_APP_CONFIG = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<String, AppConfig> CACHE_APP_CONFIG = new ConcurrentHashMap<>();
|
||||||
private static final ConcurrentHashMap<String, AppCurrentLimitingConfig> CACHE_APP_CURRENT_LIMITING_CONFIG = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<String, AppCurrentLimitingConfig> CACHE_APP_CURRENT_LIMITING_CONFIG = new ConcurrentHashMap<>();
|
||||||
|
private static final ConcurrentHashMap<String, ApiCurrentLimitingConfig> CACHE_API_CURRENT_LIMITING_CONFIG = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static boolean appDataSecurity(String appCode) {
|
public static boolean appDataSecurity(String appCode) {
|
||||||
return CACHE_APP_CONFIG.get(appCode) != null && CACHE_APP_CONFIG.get(appCode).getDataSecurity() != null ? true
|
return CACHE_APP_CONFIG.get(appCode) != null && CACHE_APP_CONFIG.get(appCode).getDataSecurity() != null ? true
|
||||||
@ -39,6 +41,10 @@ public class AppConfigServiceImpl implements AppConfigService {
|
|||||||
return CACHE_APP_CURRENT_LIMITING_CONFIG.get(appCode);
|
return CACHE_APP_CURRENT_LIMITING_CONFIG.get(appCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ApiCurrentLimitingConfig getApiCurrentLimitingConfig(String appCode) {
|
||||||
|
return CACHE_API_CURRENT_LIMITING_CONFIG.get(appCode);
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 从redis加载数据
|
* 从redis加载数据
|
||||||
*/
|
*/
|
||||||
@ -56,6 +62,14 @@ public class AppConfigServiceImpl implements AppConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO 限流
|
// TODO 限流
|
||||||
|
ApiCurrentLimitingConfig apiCurrentLimitingConfig = new ApiCurrentLimitingConfig();
|
||||||
|
apiCurrentLimitingConfig.setThreshold(1);
|
||||||
|
apiCurrentLimitingConfig.setTimeWindow(10);
|
||||||
|
apiCurrentLimitingConfig.setDefaultResponse(
|
||||||
|
"{\n" + " \"msg\": \"接口繁忙请重试\",\n" + " \"code\": 501,\n" + " \"data\": \"到达限流阈值\"\n" + "}");
|
||||||
|
CACHE_API_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappd", apiCurrentLimitingConfig);
|
||||||
|
CACHE_API_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappC", apiCurrentLimitingConfig);
|
||||||
|
|
||||||
AppCurrentLimitingConfig appCurrentLimitingConfig = new AppCurrentLimitingConfig();
|
AppCurrentLimitingConfig appCurrentLimitingConfig = new AppCurrentLimitingConfig();
|
||||||
appCurrentLimitingConfig.setThreshold(1);
|
appCurrentLimitingConfig.setThreshold(1);
|
||||||
appCurrentLimitingConfig.setTimeWindow(10);
|
appCurrentLimitingConfig.setTimeWindow(10);
|
||||||
@ -63,6 +77,7 @@ public class AppConfigServiceImpl implements AppConfigService {
|
|||||||
"{\n" + " \"msg\": \"接口繁忙请重试\",\n" + " \"code\": 501,\n" + " \"data\": \"到达限流阈值\"\n" + "}");
|
"{\n" + " \"msg\": \"接口繁忙请重试\",\n" + " \"code\": 501,\n" + " \"data\": \"到达限流阈值\"\n" + "}");
|
||||||
CACHE_APP_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappd", appCurrentLimitingConfig);
|
CACHE_APP_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappd", appCurrentLimitingConfig);
|
||||||
CACHE_APP_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappC", appCurrentLimitingConfig);
|
CACHE_APP_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappC", appCurrentLimitingConfig);
|
||||||
|
|
||||||
log.info("cacheAppConfig:{}", JSON.toJSONString(CACHE_APP_CONFIG));
|
log.info("cacheAppConfig:{}", JSON.toJSONString(CACHE_APP_CONFIG));
|
||||||
|
|
||||||
return CACHE_APP_CONFIG;
|
return CACHE_APP_CONFIG;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user