diff --git a/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzBeanFactoryPostProcessor.java b/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzBeanFactoryPostProcessor.java index 44a06e1..439cbb4 100644 --- a/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzBeanFactoryPostProcessor.java +++ b/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzBeanFactoryPostProcessor.java @@ -17,13 +17,10 @@ package com.fizzgate.beans.factory.config; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fizzgate.config.FizzConfigConfiguration; -import com.fizzgate.config.RedisReactiveProperties; import com.fizzgate.context.config.annotation.FizzRefreshScope; -import com.fizzgate.context.event.FizzRefreshEvent; -import com.fizzgate.util.*; - +import com.fizzgate.util.Consts; +import com.fizzgate.util.JacksonUtils; +import com.fizzgate.util.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -38,13 +35,11 @@ import org.springframework.context.EnvironmentAware; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; import org.springframework.data.redis.core.ReactiveStringRedisTemplate; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import java.util.*; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; /** * @author hongqiaowei @@ -54,25 +49,31 @@ public class FizzBeanFactoryPostProcessor implements BeanFactoryPostProcessor, E private static final Logger LOGGER = LoggerFactory.getLogger(FizzBeanFactoryPostProcessor.class); - private ApplicationContext applicationContext; + private static ApplicationContext applicationContext; + private ConfigurableEnvironment environment; - private final Map property2beanMap = new HashMap<>(); + private final Map property2beanMap = new HashMap<>(); private ReactiveStringRedisTemplate reactiveStringRedisTemplate; + + protected static ApplicationContext getApplicationContext() { + return applicationContext; + } + @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String fizzConfigEnable = environment.getProperty("fizz.config.enable", Consts.S.TRUE); if (fizzConfigEnable.equals(Consts.S.TRUE)) { - initReactiveStringRedisTemplate(); - initFizzPropertySource(); + // initReactiveStringRedisTemplate(); + // initFizzPropertySource(); initBeanProperty2beanMap(beanFactory); } } - private void initReactiveStringRedisTemplate() { + /* private void initReactiveStringRedisTemplate() { RedisReactiveProperties redisReactiveProperties = new RedisReactiveProperties() { }; @@ -92,9 +93,9 @@ public class FizzBeanFactoryPostProcessor implements BeanFactoryPostProcessor, E } reactiveStringRedisTemplate = ReactiveRedisHelper.getStringRedisTemplate(redisReactiveProperties); - } + } */ - private void initFizzPropertySource() { + /* private void initFizzPropertySource() { MutablePropertySources propertySources = environment.getPropertySources(); Map sources = new HashMap<>(); MapPropertySource fizzPropertySource = new MapPropertySource(FizzConfigConfiguration.PROPERTY_SOURCE, sources); @@ -191,7 +192,7 @@ public class FizzBeanFactoryPostProcessor implements BeanFactoryPostProcessor, E if (result.code == Result.FAIL) { throw new RuntimeException(result.msg, result.t); } - } + } */ private void initBeanProperty2beanMap(ConfigurableListableBeanFactory beanFactory) { ClassLoader beanClassLoader = beanFactory.getBeanClassLoader(); diff --git a/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzEnvironmentPostProcessor.java b/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzEnvironmentPostProcessor.java new file mode 100644 index 0000000..08d1bd3 --- /dev/null +++ b/fizz-core/src/main/java/com/fizzgate/beans/factory/config/FizzEnvironmentPostProcessor.java @@ -0,0 +1,199 @@ +package com.fizzgate.beans.factory.config; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fizzgate.config.FizzConfigConfiguration; +import com.fizzgate.config.RedisReactiveProperties; +import com.fizzgate.context.event.FizzRefreshEvent; +import com.fizzgate.util.Consts; +import com.fizzgate.util.JacksonUtils; +import com.fizzgate.util.ReactiveRedisHelper; +import com.fizzgate.util.Result; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.boot.logging.DeferredLog; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.SmartApplicationListener; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author hongqiaowei + */ + +public class FizzEnvironmentPostProcessor implements EnvironmentPostProcessor, SmartApplicationListener { + + private static final DeferredLog LOGGER = new DeferredLog(); + + private static Logger LOG = null; + + + private ConfigurableEnvironment environment; + + private ReactiveStringRedisTemplate reactiveStringRedisTemplate; + + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + String fizzConfigEnable = environment.getProperty("fizz.config.enable", Consts.S.TRUE); + if (fizzConfigEnable.equals(Consts.S.TRUE)) { + String host = environment.getProperty("aggregate.redis.host"); + String clusterNodes = environment.getProperty("aggregate.redis.clusterNodes"); + if (StringUtils.isNotBlank(host) || StringUtils.isNotBlank(clusterNodes)) { + this.environment = environment; + initReactiveStringRedisTemplate(); + initFizzPropertySource(); + } + } + } + + private void initReactiveStringRedisTemplate() { + RedisReactiveProperties redisReactiveProperties = new RedisReactiveProperties() { + }; + + String host = environment.getProperty("aggregate.redis.host"); + if (StringUtils.isBlank(host)) { + redisReactiveProperties.setType(RedisReactiveProperties.CLUSTER); + redisReactiveProperties.setClusterNodes(environment.getProperty("aggregate.redis.clusterNodes")); + } else { + redisReactiveProperties.setHost(host); + redisReactiveProperties.setPort(Integer.parseInt(environment.getProperty("aggregate.redis.port"))); + redisReactiveProperties.setDatabase(Integer.parseInt(environment.getProperty("aggregate.redis.database"))); + } + + String password = environment.getProperty("aggregate.redis.password"); + if (StringUtils.isNotBlank(password)) { + redisReactiveProperties.setPassword(password); + } + + reactiveStringRedisTemplate = ReactiveRedisHelper.getStringRedisTemplate(redisReactiveProperties); + } + + private void initFizzPropertySource() { + MutablePropertySources propertySources = environment.getPropertySources(); + Map sources = new HashMap<>(); + MapPropertySource fizzPropertySource = new MapPropertySource(FizzConfigConfiguration.PROPERTY_SOURCE, sources); + propertySources.addFirst(fizzPropertySource); + + Result result = Result.succ(); + Flux> fizzConfigs = reactiveStringRedisTemplate.opsForHash().entries("fizz_config"); + fizzConfigs.collectList() + .defaultIfEmpty(Collections.emptyList()) + .flatMap( + es -> { + if (es.isEmpty()) { + LOGGER.info("no fizz configs"); + } else { + String value = null; + try { + for (Map.Entry e : es) { + String key = (String) e.getKey(); + value = (String) e.getValue(); + Map config = JacksonUtils.readValue(value, new TypeReference>(){}); + sources.put(key, config.get(key)); + } + } catch (Throwable t) { + result.code = Result.FAIL; + result.msg = "init fizz configs error, json: " + value; + result.t = t; + } + } + return Mono.empty(); + } + ) + .onErrorReturn( + throwable -> { + result.code = Result.FAIL; + result.msg = "init fizz configs error"; + result.t = throwable; + return true; + }, + result + ) + .block(); + + if (result.code == Result.FAIL) { + throw new RuntimeException(result.msg, result.t); + } + if (!sources.isEmpty()) { + LOGGER.info("fizz configs: " + JacksonUtils.writeValueAsString(sources)); + } + + String channel = "fizz_config_channel"; + reactiveStringRedisTemplate.listenToChannel(channel) + .doOnError( + t -> { + result.code = Result.FAIL; + result.msg = "lsn " + channel + " channel error"; + result.t = t; + LOGGER.error("lsn channel " + channel + " error", t); + } + ) + .doOnSubscribe( + s -> { + LOGGER.info("success to lsn on " + channel); + } + ) + .doOnNext( + msg -> { + if (LOG == null) { + LOG = LoggerFactory.getLogger(FizzEnvironmentPostProcessor.class); + } + String message = msg.getMessage(); + try { + Map changedPropertyValueMap = new HashMap<>(); + List> changes = JacksonUtils.readValue(message, new TypeReference>>(){}); + for (Map change : changes) { + int isDeleted = (int) change.remove("isDeleted"); + Map.Entry propertyValue = change.entrySet().iterator().next(); + String property = propertyValue.getKey(); + Object v = null; + if (isDeleted == 1) { + sources.remove(property); + } else { + v = propertyValue.getValue(); + sources.put(property, v); + } + changedPropertyValueMap.put(property, v); + } + LOG.info("new fizz configs: " + JacksonUtils.writeValueAsString(sources)); + ApplicationContext applicationContext = FizzBeanFactoryPostProcessor.getApplicationContext(); + FizzRefreshEvent refreshEvent = new FizzRefreshEvent(applicationContext, FizzRefreshEvent.ENV_CHANGE, changedPropertyValueMap); + applicationContext.publishEvent(refreshEvent); + } catch (Throwable t) { + LOG.error("update fizz config " + message + " error", t); + } + } + ) + .subscribe(); + + if (result.code == Result.FAIL) { + throw new RuntimeException(result.msg, result.t); + } + } + + @Override + public boolean supportsEventType(Class eventType) { + return ApplicationPreparedEvent.class.isAssignableFrom(eventType); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationPreparedEvent) { + LOGGER.replayTo(FizzEnvironmentPostProcessor.class); + } + } +} \ No newline at end of file diff --git a/fizz-spring-boot-starter/src/main/resources/META-INF/spring.factories b/fizz-spring-boot-starter/src/main/resources/META-INF/spring.factories index f521152..dcba522 100644 --- a/fizz-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/fizz-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -50,4 +50,8 @@ com.fizzgate.global_resource.GlobalResourceService,\ com.fizzgate.dedicated_line.DedicatedLineWebServer # Application Listeners -org.springframework.context.ApplicationListener=com.fizzgate.context.event.FizzApplicationListener +org.springframework.context.ApplicationListener=\ +com.fizzgate.context.event.FizzApplicationListener,\ +com.fizzgate.beans.factory.config.FizzEnvironmentPostProcessor + +org.springframework.boot.env.EnvironmentPostProcessor=com.fizzgate.beans.factory.config.FizzEnvironmentPostProcessor