diff --git a/fizz-common/src/main/java/we/config/RedisReactiveConfig.java b/fizz-common/src/main/java/we/config/RedisReactiveConfig.java index 63e31d6..9c9f3b7 100644 --- a/fizz-common/src/main/java/we/config/RedisReactiveConfig.java +++ b/fizz-common/src/main/java/we/config/RedisReactiveConfig.java @@ -65,10 +65,12 @@ public abstract class RedisReactiveConfig { } rcs.setDatabase(redisReactiveProperties.getDatabase()); + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxTotal(16); LettucePoolingClientConfiguration ccs = LettucePoolingClientConfiguration.builder() .clientResources(clientResources) .clientOptions(ClientOptions.builder().publishOnScheduler(true).build()) - .poolConfig(new GenericObjectPoolConfig()) + .poolConfig(poolConfig) .build(); return new LettuceConnectionFactory(rcs, ccs); diff --git a/fizz-core/src/main/java/we/controller/CacheCheckController.java b/fizz-core/src/main/java/we/controller/CacheCheckController.java index 233a795..07997b1 100644 --- a/fizz-core/src/main/java/we/controller/CacheCheckController.java +++ b/fizz-core/src/main/java/we/controller/CacheCheckController.java @@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import we.dict.DictService; import we.plugin.auth.ApiConfigService; import we.plugin.auth.ApiConifg2appsService; import we.plugin.auth.AppService; @@ -54,6 +55,9 @@ public class CacheCheckController { @Resource private ApiConifg2appsService apiConifg2appsService; + @Resource + private DictService dictService; + @GetMapping("/gatewayGroups") public Mono gatewayGroups(ServerWebExchange exchange) { return Mono.just(JacksonUtils.writeValueAsString(gatewayGroupService.gatewayGroupMap)); @@ -83,4 +87,9 @@ public class CacheCheckController { public Mono apiConfig2appsConfigs(ServerWebExchange exchange) { return Mono.just(JacksonUtils.writeValueAsString(apiConifg2appsService.getApiConfig2appsMap())); } + + @GetMapping("/dicts") + public Mono dicts(ServerWebExchange exchange) { + return Mono.just(JacksonUtils.writeValueAsString(dictService.getDictMap())); + } } diff --git a/fizz-core/src/main/java/we/dict/Dict.java b/fizz-core/src/main/java/we/dict/Dict.java new file mode 100644 index 0000000..e9da1f3 --- /dev/null +++ b/fizz-core/src/main/java/we/dict/Dict.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package we.dict; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import we.util.JacksonUtils; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @author hongqiaowei + */ + +public class Dict { + + public static final int BOOLEAN = 1; + public static final int STRING = 2; + public static final int NUMBER = 3; + public static final int JSON = 4; + + public static final int DELETED = 1; + + public int isDeleted = 0; + + public int id; + + public String key; + + public int type; + + public String value; + + public boolean booleanVal; + + public String stringVal; + + public BigDecimal numberVal; + + public int intVal; + + public long longVal; + + public float floatVal; + + public double doubleVal; + + public String jsonVal; + + public Map valMap; + + public List valList; + + public long create; + + public long update; + + @JsonCreator + public Dict( + @JsonProperty("isDeleted") int isDeleted, + @JsonProperty("id") int id, + @JsonProperty("key") String key, + @JsonProperty("type") int type, + @JsonProperty("value") String value, + @JsonProperty("create") long create, + @JsonProperty("update") long update + ) { + this.isDeleted = isDeleted; + this.id = id; + this.key = key; + this.type = type; + this.value = value; + this.create = create; + this.update = update; + if (type == BOOLEAN) { + booleanVal = Boolean.parseBoolean(value); + } else if (type == STRING) { + stringVal = value; + } else if (type == NUMBER) { + numberVal = new BigDecimal(value); + if (value.indexOf('.') == -1) { + intVal = numberVal.intValue(); + longVal = numberVal.longValue(); + } else { + floatVal = numberVal.floatValue(); + doubleVal = numberVal.doubleValue(); + } + } else { // JSON + jsonVal = value; + if (value.startsWith("{")) { + valMap = JacksonUtils.readValue(jsonVal, Map.class); + } else { + valList = JacksonUtils.readValue(jsonVal, List.class); + } + } + } + + @Override + public String toString() { + return JacksonUtils.writeValueAsString(this); + } +} diff --git a/fizz-core/src/main/java/we/dict/DictService.java b/fizz-core/src/main/java/we/dict/DictService.java new file mode 100644 index 0000000..9e8018e --- /dev/null +++ b/fizz-core/src/main/java/we/dict/DictService.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package we.dict; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import we.FizzAppContext; +import we.config.AggregateRedisConfig; +import we.util.JacksonUtils; +import we.util.ReactiveResult; +import we.util.Result; +import we.util.Utils; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @author hongqiaowei + */ + +@Service +public class DictService { + + private static final Logger log = LoggerFactory.getLogger(DictService.class); + + private Map dictMap = new HashMap<>(64); + + @Resource(name = AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE) + private ReactiveStringRedisTemplate rt; + + @PostConstruct + public void init() throws Throwable { + initDict().subscribe( + r -> { + if (r.code == ReactiveResult.SUCC) { + lsnInitChange().subscribe( + res -> { + if (res.code == ReactiveResult.FAIL) { + log.error(res.toString()); + if (res.t == null) { + throw Utils.runtimeExceptionWithoutStack("lsn dict error"); + } + throw new RuntimeException(res.t); + } + } + ); + } else { + log.error(r.toString()); + if (r.t == null) { + throw Utils.runtimeExceptionWithoutStack("init dict error"); + } + throw new RuntimeException(r.t); + } + } + ); + } + + private Mono> initDict() { + Flux> dicts = rt.opsForHash().entries("fizz_dict"); + dicts.collectList() + .defaultIfEmpty(Collections.emptyList()) + .flatMap( + es -> { + if (FizzAppContext.appContext != null) { + for (Map.Entry e : es) { + String json = (String) e.getValue(); + Dict dict = JacksonUtils.readValue(json, Dict.class); + dictMap.put(dict.key, dict); + log.info("init dict: {}", dict); + } + } + return Mono.empty(); + } + ) + .doOnError( + t -> { + log.error("init dict", t); + } + ) + .block(); + return Mono.just(Result.succ()); + } + + private Mono> lsnInitChange() { + Result result = Result.succ(); + String channel = "fizz_dict_channel"; + rt.listenToChannel(channel) + .doOnError( + t -> { + result.code = ReactiveResult.FAIL; + result.t = t; + log.error("lsn {}", channel, t); + } + ) + .doOnSubscribe( + s -> { + log.info("success to lsn on {}", channel); + } + ) + .doOnNext( + msg -> { + if (FizzAppContext.appContext != null) { + String message = msg.getMessage(); + try { + Dict dict = JacksonUtils.readValue(message, Dict.class); + if (dict.isDeleted == Dict.DELETED) { + dictMap.remove(dict.key); + log.info("remove dict {}", dict.key); + } else { + Dict put = dictMap.put(dict.key, dict); + log.info("update dict {} with {}", put, dict); + } + } catch (Throwable t) { + log.error("message: {}", message, t); + } + } + } + ) + .subscribe(); + return Mono.just(result); + } + + public Map getDictMap() { + return dictMap; + } + + public Dict get(String key) { + return dictMap.get(key); + } +} diff --git a/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java b/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java index bd2ac2d..2178c09 100644 --- a/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java +++ b/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java @@ -96,7 +96,7 @@ public class ApiConfigService { if (res.t == null) { throw Utils.runtimeExceptionWithoutStack("lsn plugin config error"); } - throw new RuntimeException(r.t); + throw new RuntimeException(res.t); } } ); @@ -130,7 +130,7 @@ public class ApiConfigService { return Flux.just(e); } Object v = e.getValue(); - log.info("get api config data: {}", v.toString(), LogService.BIZ_ID, k.toString()); + log.info("init api config: {}", v.toString(), LogService.BIZ_ID, k.toString()); String json = (String) v; try { ApiConfig ac = JacksonUtils.readValue(json, ApiConfig.class); diff --git a/fizz-core/src/main/java/we/plugin/auth/AppService.java b/fizz-core/src/main/java/we/plugin/auth/AppService.java index bc4a830..3584c42 100644 --- a/fizz-core/src/main/java/we/plugin/auth/AppService.java +++ b/fizz-core/src/main/java/we/plugin/auth/AppService.java @@ -81,7 +81,7 @@ public class AppService { return Flux.just(e); } Object v = e.getValue(); - log.info(k.toString() + Consts.S.COLON + v.toString(), LogService.BIZ_ID, k.toString()); + log.info("init app: {}", v.toString(), LogService.BIZ_ID, k.toString()); String json = (String) v; try { App app = JacksonUtils.readValue(json, App.class); diff --git a/fizz-core/src/test/java/we/dict/DictTests.java b/fizz-core/src/test/java/we/dict/DictTests.java new file mode 100644 index 0000000..b17aece --- /dev/null +++ b/fizz-core/src/test/java/we/dict/DictTests.java @@ -0,0 +1,68 @@ +package we.dict; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import we.FizzAppContext; +import we.plugin.auth.ApiConfigService; +import we.plugin.auth.ApiConfigServiceProperties; +import we.redis.RedisProperties; +import we.redis.RedisServerConfiguration; +import we.redis.RedisTemplateConfiguration; +import we.util.JacksonUtils; +import we.util.ReflectionUtils; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + +/** + * @author hongqiaowei + */ + +@TestPropertySource("/application.properties") +@SpringJUnitConfig(classes = {RedisProperties.class, RedisTemplateConfiguration.class, RedisServerConfiguration.class}) +public class DictTests { + + @Resource + StringRedisTemplate stringRedisTemplate; + + @Resource + ReactiveStringRedisTemplate reactiveStringRedisTemplate; + + DictService dictService; + + @BeforeEach + void beforeEach() throws NoSuchFieldException { + dictService = new DictService(); + ReflectionUtils.set(dictService, "rt", reactiveStringRedisTemplate); + } + + @Test + void constructTest() throws JsonProcessingException { + String json = "{\"id\":1,\"key\":\"key\",\"type\":4,\"value\":\"{\\\"a0\\\":\\\"v0\\\",\\\"a1\\\":66}\",\"create\":1633756859538,\"update\":1633756859538,\"isDeleted\":1}"; + Dict dict = JacksonUtils.readValue(json, Dict.class); +// assertEquals(96.12347, dict.numberVal.doubleValue()); +// assertEquals("96.12347", dict.numberVal.toPlainString()); +// System.err.println(dict.toString()); + } + + @Test + void initTest() throws Throwable { + + FizzAppContext.appContext = new GenericApplicationContext(); + FizzAppContext.appContext.refresh(); + + Map dictsMap = new HashMap<>(); + dictsMap.put("key0", "{\"id\":1,\"key\":\"key0\",\"type\":2,\"value\":\"val0\",\"create\":1633756859538,\"update\":1633756859538,\"isDeleted\":1}"); + dictsMap.put("key1", "{\"id\":1,\"key\":\"key1\",\"type\":2,\"value\":\"val1\",\"create\":1633756859538,\"update\":1633756859538,\"isDeleted\":1}"); + stringRedisTemplate.opsForHash().putAll("fizz_dict", dictsMap); + + dictService.init(); + } +} diff --git a/fizz-core/src/test/java/we/plugin/auth/ApiConfigServiceTests.java b/fizz-core/src/test/java/we/plugin/auth/ApiConfigServiceTests.java index 8258d8c..3fc3c09 100644 --- a/fizz-core/src/test/java/we/plugin/auth/ApiConfigServiceTests.java +++ b/fizz-core/src/test/java/we/plugin/auth/ApiConfigServiceTests.java @@ -59,6 +59,5 @@ public class ApiConfigServiceTests { stringRedisTemplate.opsForHash().putAll("fizz_plugin_config", pluginsMap); apiConfigService.init(); - Thread.sleep(2000); } }