From 220ec74d5a2f7dfeadcf36a1bca3f741eaa0cf8b Mon Sep 17 00:00:00 2001 From: ztzh_xieyun Date: Fri, 26 Apr 2024 15:30:30 +0800 Subject: [PATCH] =?UTF-8?q?vertx=E9=99=90=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/pojo/ApiCurrentLimitingConfig.java | 18 +++ .../api/pojo/AppCurrentLimitingConfig.java | 18 +++ .../org.eclipse.core.resources.prefs | 2 + .../config/FastJson2JsonRedisSerializer.java | 48 ++++++ .../java/com/sf/vertx/config/RedisConfig.java | 69 ++++++++ .../{RedisConfig.java => RedisKeyConfig.java} | 16 +- .../com/sf/vertx/handle/RateLimitHandler.java | 27 ++++ .../sf/vertx/handle/RateLimitHandlerImpl.java | 10 ++ .../handle/RateLimitHandlerRedisImpl.java | 51 ++++++ .../com/sf/vertx/handle/RedisRateLimiter.java | 53 ++++++ .../vertx/handle/RestfulFailureHandler.java | 10 ++ .../handle/RestfulFailureHandlerImpl.java | 23 +++ .../com/sf/vertx/init/DynamicBuildServer.java | 26 +-- .../service/impl/AppConfigServiceImpl.java | 95 ++++++----- .../java/com/sf/vertx/utils/SpringUtils.java | 151 ++++++++++++++++++ 15 files changed, 562 insertions(+), 55 deletions(-) create mode 100644 sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiCurrentLimitingConfig.java create mode 100644 sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppCurrentLimitingConfig.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/config/RedisConfig.java rename sf-vertx/src/main/java/com/sf/vertx/constans/{RedisConfig.java => RedisKeyConfig.java} (69%) create mode 100644 sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandler.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerImpl.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerRedisImpl.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/handle/RedisRateLimiter.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java create mode 100644 sf-vertx/src/main/java/com/sf/vertx/utils/SpringUtils.java diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiCurrentLimitingConfig.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiCurrentLimitingConfig.java new file mode 100644 index 0000000..3590f36 --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/ApiCurrentLimitingConfig.java @@ -0,0 +1,18 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class ApiCurrentLimitingConfig implements Serializable { + private static final long serialVersionUID = 8042924090032324660L; + private Integer threshold; // 2, // 限流阈值 + private Integer timeWindow; //1, //时间窗口,单位s + private String defaultResponse; +// ": "{ +// \"msg\": \"接口繁忙请重试\", +// \"code\": 501, +// \"data\": \"到达限流阈值\", +// }" // 默认限流响应,JSON字符串。 +} diff --git a/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppCurrentLimitingConfig.java b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppCurrentLimitingConfig.java new file mode 100644 index 0000000..2efe4a3 --- /dev/null +++ b/sf-vertx-api/src/main/java/com/sf/vertx/api/pojo/AppCurrentLimitingConfig.java @@ -0,0 +1,18 @@ +package com.sf.vertx.api.pojo; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class AppCurrentLimitingConfig implements Serializable { + private static final long serialVersionUID = 5851544674347437522L; + private Integer threshold; // 2, // 限流阈值(APP总和) + private Integer timeWindow; //1, //时间窗口,单位s + private String defaultResponse; +// ": "{ +// \"msg\": \"接口繁忙请重试\", +// \"code\": 501, +// \"data\": \"到达限流阈值\", +// }" // 默认限流响应,JSON字符串。 +} diff --git a/sf-vertx/.settings/org.eclipse.core.resources.prefs b/sf-vertx/.settings/org.eclipse.core.resources.prefs index abdea9a..29abf99 100644 --- a/sf-vertx/.settings/org.eclipse.core.resources.prefs +++ b/sf-vertx/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,6 @@ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 encoding/=UTF-8 diff --git a/sf-vertx/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java b/sf-vertx/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..62e0b04 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,48 @@ +package com.sf.vertx.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; + +/** + * Redis使用FastJson序列化 + * + * @author ztzh + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/config/RedisConfig.java b/sf-vertx/src/main/java/com/sf/vertx/config/RedisConfig.java new file mode 100644 index 0000000..e0b34f3 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/config/RedisConfig.java @@ -0,0 +1,69 @@ +package com.sf.vertx.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author ztzh + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean(name = "redisTemplate") + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java b/sf-vertx/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java similarity index 69% rename from sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java rename to sf-vertx/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java index 0d731c4..1b486a0 100644 --- a/sf-vertx/src/main/java/com/sf/vertx/constans/RedisConfig.java +++ b/sf-vertx/src/main/java/com/sf/vertx/constans/RedisKeyConfig.java @@ -3,22 +3,22 @@ package com.sf.vertx.constans; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; - @Component -public class RedisConfig { - +public class RedisKeyConfig { + @Value("${server.vertx.environment}") - private String vertxEnvironment; - + private String vertxEnvironment; + public static final String BASE_REDIS_KEY = "vertx:config:"; public static String APP_CONFIG_SET_KEY = null; + public static String APP_CURRENT_LIMITING_CONFIG_KEY = null; public static String APP_CONFIG_PREFIX_KEY = null; public static String VERTX_CONFIG_STRING_KEY = null; - + public void init() { APP_CONFIG_PREFIX_KEY = BASE_REDIS_KEY + vertxEnvironment; - APP_CONFIG_SET_KEY = APP_CONFIG_PREFIX_KEY+":set"; + APP_CONFIG_SET_KEY = APP_CONFIG_PREFIX_KEY + ":set"; + APP_CURRENT_LIMITING_CONFIG_KEY = APP_CONFIG_PREFIX_KEY + ":app"; VERTX_CONFIG_STRING_KEY = BASE_REDIS_KEY + vertxEnvironment + ":vertx"; } } - diff --git a/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandler.java b/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandler.java new file mode 100644 index 0000000..141db87 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandler.java @@ -0,0 +1,27 @@ +package com.sf.vertx.handle; + +import io.vertx.codegen.annotations.VertxGen; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; + +/*** + * 限流熔断, redis存储 + * @author xy + * + */ +@VertxGen +public interface RateLimitHandler extends Handler { + + static RateLimitHandler create(String instance) { + switch (instance) { + case "redis": + RedisRateLimiter redisRateLimiter = new RedisRateLimiter(); + return new RateLimitHandlerRedisImpl(redisRateLimiter); + default: + // 本地缓存 + return null; + } + + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerImpl.java b/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerImpl.java new file mode 100644 index 0000000..bc42b07 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerImpl.java @@ -0,0 +1,10 @@ +package com.sf.vertx.handle; + +/*** + * 内存存储 + * @author xy + * + */ +public class RateLimitHandlerImpl { + +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerRedisImpl.java b/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerRedisImpl.java new file mode 100644 index 0000000..b2ccbb0 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/handle/RateLimitHandlerRedisImpl.java @@ -0,0 +1,51 @@ +package com.sf.vertx.handle; + +import com.sf.vertx.api.pojo.AppCurrentLimitingConfig; +import com.sf.vertx.init.DynamicBuildServer; +import com.sf.vertx.service.impl.AppConfigServiceImpl; + +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.HttpException; +import lombok.extern.slf4j.Slf4j; + +/*** + * redis存储 + * + * @author xy + * + */ +@Slf4j +public class RateLimitHandlerRedisImpl implements RateLimitHandler { + private RedisRateLimiter rateLimiter; + + public RateLimitHandlerRedisImpl(RedisRateLimiter rateLimiter) { + this.rateLimiter = rateLimiter; + } + + @Override + public void handle(RoutingContext rc) { + String sacAppHeaderKey = rc.request().headers().get(DynamicBuildServer.SAC_APP_HEADER_KEY); + // TODO 判断是否开启限流配置 + log.info("RateLimitHandlerRedisImpl request:{}", sacAppHeaderKey); +// rc.next(); +// return; + // 获取模式 + int pattern = 1; // 1:app,2:接口默认,3:服务接口配置限流策略 + if (rateLimiter.acquire(rc, pattern)) { + log.info("rateLimiter.acquire true"); + rc.next(); + return; + } else { + switch (pattern) { + case 1: + AppCurrentLimitingConfig appCurrentLimitingConfig = AppConfigServiceImpl + .getAppCurrentLimitingConfig(sacAppHeaderKey); + rc.fail(new HttpException(429, appCurrentLimitingConfig.getDefaultResponse())); + return; +// JsonObject dataJson = new JsonObject(appCurrentLimitingConfig.getDefaultResponse()); +// rc.response().setChunked(true).setStatusCode(429).putHeader("Content-Type", "application/json").end(dataJson.toBuffer()); +// return; + } + } + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/handle/RedisRateLimiter.java b/sf-vertx/src/main/java/com/sf/vertx/handle/RedisRateLimiter.java new file mode 100644 index 0000000..5c2b9d7 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/handle/RedisRateLimiter.java @@ -0,0 +1,53 @@ +package com.sf.vertx.handle; + +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.RedisTemplate; + +import com.sf.vertx.api.pojo.AppCurrentLimitingConfig; +import com.sf.vertx.constans.RedisKeyConfig; +import com.sf.vertx.init.DynamicBuildServer; +import com.sf.vertx.service.impl.AppConfigServiceImpl; +import com.sf.vertx.utils.SpringUtils; + +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RedisRateLimiter { + public Boolean acquire(RoutingContext rc, int pattern) { + String appCode = rc.request().headers().get(DynamicBuildServer.SAC_APP_HEADER_KEY); + //TODO 先测试app模式,后面通过app缓存获取模式 + switch (pattern) { + case 1: + String key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY +":"+ appCode; + return rateLimiter(key, appCode); + default: + break; + } + return false; + + } + + @SuppressWarnings("unchecked") + private Boolean rateLimiter(String key, String appCode) { + RedisTemplate redisTemplate = SpringUtils.getBean("redisTemplate", RedisTemplate.class); + AppCurrentLimitingConfig appCurrentLimitingConfig = AppConfigServiceImpl.getAppCurrentLimitingConfig(appCode); + Object count = redisTemplate.opsForValue().get(key); + //redisTemplate.delete(key); + log.info("count:{}", count); + if(count == null) { + // 初始化,设置过期时间 + redisTemplate.opsForValue().increment(key); + log.info("redis app threshold: {}", redisTemplate.opsForValue().get(key)); + redisTemplate.expire(key, appCurrentLimitingConfig.getTimeWindow(), TimeUnit.SECONDS); + } else if(Integer.valueOf(count.toString()) < appCurrentLimitingConfig.getThreshold()) { + redisTemplate.opsForValue().increment(key); + } else { + return false; + } + return true; + } +} + + diff --git a/sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java b/sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java new file mode 100644 index 0000000..8b739ad --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java @@ -0,0 +1,10 @@ +package com.sf.vertx.handle; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +public interface RestfulFailureHandler extends Handler { + static RestfulFailureHandlerImpl create() { + return new RestfulFailureHandlerImpl(); + } +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java b/sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java new file mode 100644 index 0000000..25f76b5 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java @@ -0,0 +1,23 @@ +package com.sf.vertx.handle; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.HttpException; + +public class RestfulFailureHandlerImpl implements RestfulFailureHandler { + + @Override + public void handle(RoutingContext frc) { + Throwable failure = frc.failure(); + if (failure instanceof HttpException) { + HttpException httpException = (HttpException) failure; + // frc.response().setStatusCode(404).end(); + JsonObject dataJson = new JsonObject(httpException.getPayload()); + frc.response().setChunked(true).setStatusCode(httpException.getStatusCode()) + .putHeader("Content-Type", "application/json").end(dataJson.toBuffer()); + return; + } + frc.response().setStatusCode(500).setStatusMessage("Server internal error:" + failure.getMessage()).end(); + } + +} diff --git a/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java b/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java index 724f622..4fd0da7 100644 --- a/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java +++ b/sf-vertx/src/main/java/com/sf/vertx/init/DynamicBuildServer.java @@ -19,9 +19,11 @@ import com.sf.vertx.api.pojo.SacService; import com.sf.vertx.api.pojo.VertxConfig; import com.sf.vertx.arithmetic.roundRobin.SacLoadBalancing; import com.sf.vertx.arithmetic.roundRobin.WeightedRoundRobin; -import com.sf.vertx.constans.RedisConfig; +import com.sf.vertx.constans.RedisKeyConfig; import com.sf.vertx.handle.BodyHandler; import com.sf.vertx.handle.ProxyHandler; +import com.sf.vertx.handle.RateLimitHandler; +import com.sf.vertx.handle.RestfulFailureHandler; import com.sf.vertx.security.MainSecurity; import com.sf.vertx.service.AppConfigService; import com.sf.vertx.service.impl.AppConfigServiceImpl; @@ -33,9 +35,11 @@ import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.json.JsonObject; import io.vertx.core.net.SocketAddress; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.WebClient; +import io.vertx.ext.web.handler.HttpException; import io.vertx.httpproxy.HttpProxy; import io.vertx.httpproxy.ProxyContext; import io.vertx.httpproxy.ProxyInterceptor; @@ -61,12 +65,12 @@ public class DynamicBuildServer implements ApplicationRunner { private AppConfigService appConfigService; @Autowired - private RedisConfig redisConfig; + private RedisKeyConfig redisKeyConfig; @Override public void run(ApplicationArguments args) throws Exception { // 初始化redis key - redisConfig.init(); + redisKeyConfig.init(); // 加载vertx、应用配置 appStartLoadData(); } @@ -112,17 +116,21 @@ public class DynamicBuildServer implements ApplicationRunner { log.info("addInterceptor uri appCode:{}", sacAppHeaderKey); // 判断是否需要加解析 if (AppConfigServiceImpl.appDataSecurity(sacAppHeaderKey)) { - - //String data = decode(null, sacAppHeaderKey); - + + // String data = decode(null, sacAppHeaderKey); + } return context.sendRequest(); } }); WebClient mainWebClient = WebClient.create(VERTX); - //mainHttpRouter.route().handler(ProxyHandler.create(proxy)); - - mainHttpRouter.route().handler(BodyHandler.create()).handler(ProxyHandler.create(mainWebClient,proxy)); + // mainHttpRouter.route().handler(ProxyHandler.create(proxy)); + + // TODO 实例化方式 从 VertxConfig 读取 + String instance = "redis"; + mainHttpRouter.route().handler(RateLimitHandler.create(instance)).handler(BodyHandler.create()) + .handler(ProxyHandler.create(mainWebClient, proxy)).failureHandler(RestfulFailureHandler.create()); + // mainHttpRouter.route().handler(BodyHandler.create()).handler(ProxyHandler.create(mainWebClient,proxy)); } public SocketAddress resolveOriginAddress(ConcurrentHashMap cacheAppConfig, diff --git a/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java b/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java index 6c79ea3..63a6fd3 100644 --- a/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java +++ b/sf-vertx/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java @@ -13,8 +13,9 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.TypeReference; import com.sf.vertx.api.pojo.AppConfig; +import com.sf.vertx.api.pojo.AppCurrentLimitingConfig; import com.sf.vertx.api.pojo.VertxConfig; -import com.sf.vertx.constans.RedisConfig; +import com.sf.vertx.constans.RedisKeyConfig; import com.sf.vertx.service.AppConfigService; import lombok.extern.slf4j.Slf4j; @@ -23,95 +24,113 @@ import lombok.extern.slf4j.Slf4j; @Service public class AppConfigServiceImpl implements AppConfigService { @Value("${server.vertx.environment}") - private String vertxEnvironment; + private String vertxEnvironment; @Autowired private RedisTemplate redisTemplate; - private static final ConcurrentHashMap cacheAppConfig = new ConcurrentHashMap<>(); - - + private static final ConcurrentHashMap CACHE_APP_CONFIG = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap CACHE_APP_CURRENT_LIMITING_CONFIG = new ConcurrentHashMap<>(); + public static boolean appDataSecurity(String appCode) { - return cacheAppConfig.get(appCode) != null && cacheAppConfig.get(appCode).getDataSecurity() != null ? true : false; + return CACHE_APP_CONFIG.get(appCode) != null && CACHE_APP_CONFIG.get(appCode).getDataSecurity() != null ? true + : false; } - + + public static AppCurrentLimitingConfig getAppCurrentLimitingConfig(String appCode) { + return CACHE_APP_CURRENT_LIMITING_CONFIG.get(appCode); + } + /*** * 从redis加载数据 */ public ConcurrentHashMap loadAllConfig() { - Set set = redisTemplate.opsForZSet().range(RedisConfig.APP_CONFIG_SET_KEY, 0, -1); - for(String appCode : set) { - String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; + Set set = redisTemplate.opsForZSet().range(RedisKeyConfig.APP_CONFIG_SET_KEY, 0, -1); + for (String appCode : set) { + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; String appCodeValue = redisTemplate.opsForValue().get(appCodeKey); - if(StringUtils.isNotBlank(appCodeValue)) { - AppConfig appConfig = JSON.parseObject(appCodeValue, new TypeReference() {}); - cacheAppConfig.put(appCode, appConfig); + if (StringUtils.isNotBlank(appCodeValue)) { + AppConfig appConfig = JSON.parseObject(appCodeValue, new TypeReference() { + }); + CACHE_APP_CONFIG.put(appCode, appConfig); + } } - log.info("cacheAppConfig:{}", JSON.toJSONString(cacheAppConfig)); - return cacheAppConfig; + + // TODO 限流 + AppCurrentLimitingConfig appCurrentLimitingConfig = new AppCurrentLimitingConfig(); + appCurrentLimitingConfig.setThreshold(1); + appCurrentLimitingConfig.setTimeWindow(10); + appCurrentLimitingConfig.setDefaultResponse( + "{\n" + " \"msg\": \"接口繁忙请重试\",\n" + " \"code\": 501,\n" + " \"data\": \"到达限流阈值\"\n" + "}"); + CACHE_APP_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappd", appCurrentLimitingConfig); + CACHE_APP_CURRENT_LIMITING_CONFIG.put("dsafdsfadafhappC", appCurrentLimitingConfig); + log.info("cacheAppConfig:{}", JSON.toJSONString(CACHE_APP_CONFIG)); + + return CACHE_APP_CONFIG; } - + public AppConfig getAppConfig(String appCode) { - String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appCode; String appCodeValue = redisTemplate.opsForValue().get(appCodeKey); - if(StringUtils.isNotBlank(appCodeValue)) { + if (StringUtils.isNotBlank(appCodeValue)) { AppConfig appConfig = JSONObject.parseObject(appCodeValue, AppConfig.class); return appConfig; } return null; } - + /*** * 新增、修改 + * * @param appConfig */ public void addAppConfig(String appConfigStr) { AppConfig appConfig = JSON.parseObject(appConfigStr, AppConfig.class); - redisTemplate.opsForZSet().add(RedisConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode(), 0); - String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); + redisTemplate.opsForZSet().add(RedisKeyConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode(), 0); + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); redisTemplate.opsForValue().set(appCodeKey, appConfigStr); - + // 发送redis队列,vertx处理 - //String queue = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; - + // String queue = RedisKeyConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; + } - + /*** * 删除 + * * @param appConfig */ public void deleteAppConfig(AppConfig appConfig) { - redisTemplate.opsForZSet().remove(RedisConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode()); - String appCodeKey = RedisConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); + redisTemplate.opsForZSet().remove(RedisKeyConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode()); + String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode(); redisTemplate.delete(appCodeKey); - - + // 发送redis队列,vertx处理 - //String queue = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; + // String queue = RedisKeyConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; } - - + /*** * 加载vertx配置 */ public VertxConfig loadVertxConfig() { - String vertxConfigKey = RedisConfig.VERTX_CONFIG_STRING_KEY; + String vertxConfigKey = RedisKeyConfig.VERTX_CONFIG_STRING_KEY; String vertxConfigValue = redisTemplate.opsForValue().get(vertxConfigKey); - if(StringUtils.isNotBlank(vertxConfigValue)) { + if (StringUtils.isNotBlank(vertxConfigValue)) { VertxConfig vertxConfig = JSONObject.parseObject(vertxConfigValue, VertxConfig.class); return vertxConfig; } return null; } - + /*** * 新增、修改 + * * @param appConfig */ public void addVertxConfig(VertxConfig vertxConfig) { - String vertxConfigKey = RedisConfig.VERTX_CONFIG_STRING_KEY; + String vertxConfigKey = RedisKeyConfig.VERTX_CONFIG_STRING_KEY; redisTemplate.opsForValue().set(vertxConfigKey, JSONObject.toJSONString(vertxConfig)); - + // 发送redis队列,vertx处理 - //String queue = RedisConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; + // String queue = RedisKeyConfig.VETX_ENVIRONMENT_KEY+vertxEnvironment+":list"; } } diff --git a/sf-vertx/src/main/java/com/sf/vertx/utils/SpringUtils.java b/sf-vertx/src/main/java/com/sf/vertx/utils/SpringUtils.java new file mode 100644 index 0000000..3b043e2 --- /dev/null +++ b/sf-vertx/src/main/java/com/sf/vertx/utils/SpringUtils.java @@ -0,0 +1,151 @@ +package com.sf.vertx.utils; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ztzh + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + public static T getBean(String name, Class classType) throws BeansException + { + return (T) beanFactory.getBean(name, classType); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +}