依賴配置中心實現注有@ConfigurationProperties的bean相關屬性刷新

配置中心是什么

  配置中心,通過key=value的形式存儲環境變量。配置中心的屬性做了修改,項目中可以通過配置中心的依賴(sdk)立即感知到。需要做的就是如何在屬性發生變化時,改變帶有@ConfigurationProperties的bean的相關屬性。

配置中心原理

  在讀配置中心源碼的時候發現,里面維護了一個Environment,以及ZookeeperPropertySource。當配置中心屬性發生變化的時候,清空ZookeeperPropertySource,并放入最新的屬性值。

  

public class ZookeeperPropertySource extends EnumerablePropertySource<Properties>

  

  ZookeeperPropertySource重寫了equals和hahscode方法,根據這兩個方法可以判定配置中心是否修改了屬性。

  ?

動態刷新bean屬性原理

實現原理圖

  

動態刷新bean父類

public abstract class BaseConfigCenterBean implements InitializingBean {private static Logger LOGGER = LoggerFactory.getLogger(BaseConfigCenterBean.class);//配置中心是否生效protected boolean cfgCenterEffect = false;public boolean isCfgCenterEffect() {this.checkCfgCenterEffect();return cfgCenterEffect;}private void checkCfgCenterEffect() {boolean tmpCfgCenterEffect = !Objects.isNull(ConfigHelper.getEnvironment());if (tmpCfgCenterEffect) {// NOSONARString value = (String) ConfigHelper.getZookeeperPropertySource().getProperty("cfg.center.effect");if (StringUtils.isBlank(value)) {tmpCfgCenterEffect = false;} else {tmpCfgCenterEffect = Boolean.valueOf(value);}}cfgCenterEffect = tmpCfgCenterEffect;if (cfgCenterEffect) {String prefix = this.getConfigPrefix();cfgCenterEffect = Arrays.stream(ConfigHelper.getZookeeperPropertySource().getPropertyNames()).filter(keyName -> keyName.indexOf(prefix) == 0).count() > 0;if (!cfgCenterEffect) {LOGGER.info(String.format("配置中心沒有發現模塊=%s, prefix=%s的配置,將使用本地配置...", this.getModuleName(), prefix));}}}/*** 綁定自身目標**/protected void doBind() {Class<? extends BaseConfigCenterBean> clazz = this.getClass();if (AopUtils.isCglibProxy(this)) {clazz = (Class<? extends BaseConfigCenterBean>) AopUtils.getTargetClass(this);}BaseConfigCenterBean target = binding(isCfgCenterEffect(), clazz, this.getDefaultResourcePath());this.copyProperties(target);}private void copyProperties(BaseConfigCenterBean target) {ReflectionUtils.doWithFields(this.getClass(), field -> {field.setAccessible(true);field.set(this, field.get(target));}, field -> AnnotatedElementUtils.isAnnotated(field, ConfigField.class));}/*** 綁定其他目標** @param clazz 目標類**/protected <T> T doBind(Class<T> clazz) {T target = binding(isCfgCenterEffect(), clazz, this.getDefaultResourcePath());if (target instanceof InitializingBean) {try {((InitializingBean) target).afterPropertiesSet();} catch (Exception e) {LOGGER.error(String.format("屬性初始化失敗[afterPropertiesSet], class=%s", ClassUtils.getSimpleName(clazz), e));}}return target;}private <T> T binding(boolean cfgCenterEffect, Class<T> clazz, String defaultResourcePath) {Optional<PropertySource> propertySource = Optional.empty();if (cfgCenterEffect) {propertySource = Optional.ofNullable(ConfigHelper.getZookeeperPropertySource());} else {Optional<ResourcePropertySource> resourcePropertySource = ResourceUtils.getResourcePropertySource(defaultResourcePath);if (resourcePropertySource.isPresent()) {propertySource = Optional.ofNullable(resourcePropertySource.get());}}if (propertySource.isPresent()) {T target;try {target = RelaxedConfigurationBinder.with(clazz).setPropertySources(propertySource.get()).doBind();} catch (GeneralException e) {LOGGER.error(String.format("屬性綁定失敗, class=%s", ClassUtils.getSimpleName(clazz)), e);return null;}return target;}return null;}@Overridepublic void afterPropertiesSet() {Class<?> target = this.getClass();if (AopUtils.isAopProxy(this)) {target = AopUtils.getTargetClass(this);}LOGGER.info(String.format("%s->%s模塊引入配置中心%s...", this.getModuleName(), ClassUtils.getSimpleName(target), (isCfgCenterEffect() ? "生效" : "無效")));}public String getModuleName() {return StringUtils.EMPTY;}@Subscribepublic void listenRefreshEvent(ConfigCenterUtils.ConfigRefreshEvent refreshEvent) {if (!refreshEvent.getModuleName().equals(this.getModuleName())) {this.refreshForEvent();}}//通過事件進行刷新public abstract void refreshForEvent();//獲取本地配置默認路徑public abstract String getDefaultResourcePath();//獲取配置屬性的公共前綴public abstract String getConfigPrefix();
}

  1、isCfgCenterEffect方法主要判斷項目是否接入了配置中心并且配置中心配有bean中相關的屬性。

  2、binding方法主要根據isCfgCenterEffect方法的返回值去加載配置中心的properties還是本地的properties。

  3、getDefaultResourcePath是主要是獲取本地資源的默認路徑(在沒有接入配置中心的情況下)。

  4、getConfigPrefix方法返回bean中配置屬性的公共前綴(等同于@ConfigurationProperties中的prefix屬性)。

  5、refreshForEvent方法主要是在某個bean感知到配置中心更新屬性時異步通知其他bean進行屬性的更新。

bean屬性綁定工具類

  動態將propertysource綁定到帶有@ConfigurationProperties注解的bean中。

  參考?org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor

?

public class RelaxedConfigurationBinder<T> {private final PropertiesConfigurationFactory<T> factory;public RelaxedConfigurationBinder(T object) {this(new PropertiesConfigurationFactory<>(object));}public RelaxedConfigurationBinder(Class<?> type) {this(new PropertiesConfigurationFactory<>(type));}public static <T> RelaxedConfigurationBinder<T> with(T object) {return new RelaxedConfigurationBinder<>(object);}public static <T> RelaxedConfigurationBinder<T> with(Class<T> type) {return new RelaxedConfigurationBinder<>(type);}public RelaxedConfigurationBinder(PropertiesConfigurationFactory<T> factory) {this.factory = factory;ConfigurationProperties properties = getMergedAnnotation(factory.getObjectType(), ConfigurationProperties.class);javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();factory.setValidator(new SpringValidatorAdapter(validator));factory.setConversionService(new DefaultConversionService());if (!Objects.isNull(properties)) {//NOSONAR
            factory.setIgnoreNestedProperties(properties.ignoreNestedProperties());factory.setIgnoreInvalidFields(properties.ignoreInvalidFields());factory.setIgnoreUnknownFields(properties.ignoreUnknownFields());factory.setTargetName(properties.prefix());factory.setExceptionIfInvalid(properties.exceptionIfInvalid());}}public RelaxedConfigurationBinder<T> setTargetName(String targetName) {factory.setTargetName(targetName);return this;}public RelaxedConfigurationBinder<T> setPropertySources(PropertySource<?>... propertySources) {MutablePropertySources sources = new MutablePropertySources();for (PropertySource<?> propertySource : propertySources) {sources.addLast(propertySource);}factory.setPropertySources(sources);return this;}public RelaxedConfigurationBinder<T> setPropertySources(Environment environment) {factory.setPropertySources(((ConfigurableEnvironment) environment).getPropertySources());return this;}public RelaxedConfigurationBinder<T> setPropertySources(PropertySources propertySources) {factory.setPropertySources(propertySources);return this;}public RelaxedConfigurationBinder<T> setConversionService(ConversionService conversionService) {factory.setConversionService(conversionService);return this;}public RelaxedConfigurationBinder<T> setValidator(Validator validator) {factory.setValidator(validator);return this;}public RelaxedConfigurationBinder<T> setResolvePlaceholders(boolean resolvePlaceholders) {factory.setResolvePlaceholders(resolvePlaceholders);return this;}public T doBind() throws GeneralException {try {return factory.getObject();} catch (Exception ex) {throw new GeneralException("配置綁定失敗!", ex);}}
}

配置中心工具類

public class ConfigCenterUtils {private static Logger LOGGER = LoggerFactory.getLogger(ConfigCenterUtils.class);private static AsyncEventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(8));//NOSONARprivate static Properties cfgProperties;private static Environment environment;static {cfgProperties = new Properties();cfgProperties.putAll(ConfigHelper.getZookeeperPropertySource().getProperties());}public static void setEnvironment(Environment environment) {ConfigCenterUtils.environment = environment;}public static String getValue(String name) {try {return PropertiesUtil.getValue(name);} catch (Exception e) {LOGGER.info("配置中心無效, property name=" + name, e);}if (Objects.isNull(environment)) {LOGGER.info("environment無效,property name=" + name);return StringUtils.EMPTY;}if (!environment.containsProperty(name)) {LOGGER.info("environment無配置 property name=" + name);return StringUtils.EMPTY;}return environment.getProperty(name);}public synchronized static boolean propertySourceShouldRefresh(String moduleName, ZookeeperPropertySource newPropertySource) {if (!cfgProperties.equals(newPropertySource.getProperties())) {cfgProperties.clear();cfgProperties.putAll(newPropertySource.getProperties());eventBus.post(new ConfigRefreshEvent(moduleName));return true;}return false;}public static <T> T createToRefreshPropertiesBean(Class<T> clazz) {Enhancer enhancer = new Enhancer();// 設置代理對象父類
        enhancer.setSuperclass(clazz);// 標識Spring-generated proxiesenhancer.setInterfaces(new Class[]{SpringProxy.class});// 設置增強enhancer.setCallback((MethodInterceptor) (target, method, args, methodProxy) -> {ToRefresh toRefresh = AnnotationUtils.findAnnotation(method, ToRefresh.class);if (Objects.isNull(toRefresh) || StringUtils.isBlank(toRefresh.method())) {return methodProxy.invokeSuper(target, args);}Method refreshMethod = ReflectionUtils.findMethod(target.getClass(), toRefresh.method());if (Objects.isNull(refreshMethod)) {return methodProxy.invokeSuper(target, args);}refreshMethod = BridgeMethodResolver.findBridgedMethod(refreshMethod);refreshMethod.setAccessible(true);refreshMethod.invoke(target, null);return methodProxy.invokeSuper(target, args);});T target = (T) enhancer.create();// 創建代理對象
MethodIntrospector.selectMethods(clazz, (ReflectionUtils.MethodFilter) method -> AnnotatedElementUtils.isAnnotated(method, ToInitial.class)).stream().findFirst().ifPresent(method -> {method.setAccessible(true);try {method.invoke(target, null);} catch (Exception e) {LOGGER.error(String.format("初始化異常,class=%s ...", ClassUtils.getSimpleName(clazz)), e);}});return target;}public static void registerListener(BaseConfigCenterBean refreshableBean) {eventBus.register(refreshableBean);}public static class ConfigRefreshEvent {private String moduleName;public ConfigRefreshEvent(String moduleName) {this.moduleName = moduleName;}public String getModuleName() {return moduleName;}public void setModuleName(String moduleName) {this.moduleName = moduleName;}}
}

  這個工具主要作用:

  1、判斷配置中心的屬性是否發生了變化

  2、為BaseConfigCenterBean子類創建代理類,使屬性在getter方法時檢測屬性是否應該刷新。

  3、提供將BaseConfigCenterBean類型的對象的注冊為guava eventbus的監聽對象,使之具有根據刷新事件自動刷新自身屬性。

bean后置處理器

public class ConfigCenterBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (AnnotatedElementUtils.isAnnotated(bean.getClass(), ConfigCenterBean.class)) {BaseConfigCenterBean refreshableBean = (BaseConfigCenterBean) ConfigCenterUtils.createToRefreshPropertiesBean(bean.getClass());ConfigCenterUtils.registerListener(refreshableBean);return refreshableBean;}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}

  該后置處理器的作用是對所有BaseConfigCenterBean類型的bean進行處理,生成代理bean,并注冊為guava eventbus相應的listener。

pojo屬性綁定配置中心優雅方案1

@ConfigCenterBean
@ConfigurationProperties(prefix = "wx.temporary.qrcode")
@Component
public class QrcodeConstants extends BaseConfigCenterBean {private static Logger LOGGER = LoggerFactory.getLogger(QrcodeConstants.class);//渠道@ConfigField //標識該屬性來自配置中心private List<Scene> channels;//業務
    @ConfigFieldprivate List<Scene> bizs;//業務和渠道映射關系
    @ConfigFieldprivate Map<String, String> biz2Channel;private Map<String, Scene> channelMap;private Map<String, Scene> bizMap;public List<Scene> getChannels() {return channels;}public void setChannels(List<Scene> channels) {this.channels = channels;}public List<Scene> getBizs() {return bizs;}public void setBizs(List<Scene> bizs) {this.bizs = bizs;}@ToRefresh(method = "toRefresh")public Map<String, Scene> getChannelMap() {return channelMap;}@ToRefresh(method = "toRefresh")public Map<String, Scene> getBizMap() {return bizMap;}@ToRefresh(method = "toRefresh")public Map<String, String> getBiz2Channel() {return biz2Channel;}public void setBiz2Channel(Map<String, String> biz2Channel) {this.biz2Channel = biz2Channel;}@ToInitialprivate void refreshQrcodeProperties() {try {super.doBind();//屬性處理if (CollectionUtils.isEmpty(channels)) {this.channelMap = Maps.newHashMap();} else {this.channelMap = channels.stream().collect(Collectors.toMap(channel -> channel.getType(), Function.identity()));}if (CollectionUtils.isEmpty(bizs)) {this.bizMap = Maps.newHashMap();} else {this.bizMap = bizs.stream().collect(Collectors.toMap(biz -> biz.getType(), Function.identity()));}LOGGER.info(String.format("%s 刷新成功..., 當前配置=%s...", this.getModuleName(), this));} catch (Exception e) {LOGGER.error("QrcodeConstants 對象屬性綁定失敗...", e);}}private void toRefresh() {try {if (isCfgCenterEffect()) {ZookeeperPropertySource propertySource = ConfigHelper.getZookeeperPropertySource();if (ConfigCenterUtils.propertySourceShouldRefresh(this.getModuleName(), propertySource)) {this.refreshQrcodeProperties();}}} catch (Exception e) {LOGGER.error("QrcodeConstants 對象屬性刷新失敗", e);}}//刷新事件調用
    @Overridepublic void refreshForEvent() {this.refreshQrcodeProperties();}//本地資源文件
    @Overridepublic String getDefaultResourcePath() {return "config/qrcode.properties";}//屬性配置 公共前綴(和@ConfigurationProperties prefix 屬性一致)
    @Overridepublic String getConfigPrefix() {return "wx.temporary.qrcode";}//模塊名稱
    @Overridepublic String getModuleName() {return "微信臨時二維碼配置";}@Overridepublic String toString() {return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE, false, false, QrcodeConstants.class);}public static class Scene {private String type;private String desc;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE, false, false, Scene.class);}}
}

pojo屬性綁定配置中心優雅方案2

@ConfigCenterBean
@Component
public class QrcodeConstants extends BaseConfigCenterBean {private static Logger LOGGER = LoggerFactory.getLogger(QrcodeConstants.class);//業務和渠道映射關系private Map<String, String> biz2Channel;//渠道private Map<String, Scene> channelMap;//業務private Map<String, Scene> bizMap;private QrcodeProperties qrcodeProperties;@ToRefresh(method = "toRefresh")public Map<String, Scene> getChannelMap() {return channelMap;}@ToRefresh(method = "toRefresh")public Map<String, Scene> getBizMap() {return bizMap;}@ToRefresh(method = "toRefresh")public Map<String, String> getBiz2Channel() {return biz2Channel;}public void setBiz2Channel(Map<String, String> biz2Channel) {this.biz2Channel = biz2Channel;}public QrcodeProperties getRawQrcodeProperties() {return qrcodeProperties;}@ToInitialprivate void refreshQrcodeProperties() {try {QrcodeProperties qrcodeProperties = super.doBind(QrcodeProperties.class);if (Objects.isNull(qrcodeProperties)) {LOGGER.error(String.format("沒有加載到%s配置,請檢查配置...", this.getModuleName()));return;}this.qrcodeProperties = qrcodeProperties;//屬性處理if (CollectionUtils.isEmpty(qrcodeProperties.channels)) {this.channelMap = Maps.newHashMap();} else {this.channelMap = qrcodeProperties.channels.stream().collect(Collectors.toMap(channel -> channel.getType(), Function.identity()));}if (CollectionUtils.isEmpty(qrcodeProperties.bizs)) {this.bizMap = Maps.newHashMap();} else {this.bizMap = qrcodeProperties.bizs.stream().collect(Collectors.toMap(biz -> biz.getType(), Function.identity()));}if (CollectionUtils.isEmpty(qrcodeProperties.getBiz2Channel())) {this.biz2Channel = Maps.newHashMap();} else {this.biz2Channel = qrcodeProperties.getBiz2Channel();}LOGGER.info(String.format("%s 刷新成功..., 當前配置=%s...", this.getModuleName(), this));} catch (Exception e) {LOGGER.error("QrcodeConstants 對象屬性綁定失敗...", e);}}private void toRefresh() {try {if (isCfgCenterEffect()) {ZookeeperPropertySource propertySource = ConfigHelper.getZookeeperPropertySource();if (ConfigCenterUtils.propertySourceShouldRefresh(this.getModuleName(), propertySource)) {this.refreshQrcodeProperties();}}} catch (Exception e) {LOGGER.error("QrcodeConstants 對象屬性刷新失敗", e);}}@Overridepublic void refreshForEvent() {this.refreshQrcodeProperties();}@Overridepublic String getDefaultResourcePath() {return "config/qrcode.properties";}@Overridepublic String getConfigPrefix() {return "wx.temporary.qrcode";}@Overridepublic String getModuleName() {return "微信臨時二維碼配置";}@Overridepublic String toString() {return new ToStringBuilder(this).append("biz2Channel", biz2Channel).append("channelMap", channelMap).append("bizMap", bizMap).toString();}@ConfigurationProperties(prefix = "wx.temporary.qrcode")public static class QrcodeProperties {//渠道private List<Scene> channels;//業務private List<Scene> bizs;//業務和渠道映射關系private Map<String, String> biz2Channel;public List<Scene> getChannels() {return channels;}public void setChannels(List<Scene> channels) {this.channels = channels;}public List<Scene> getBizs() {return bizs;}public void setBizs(List<Scene> bizs) {this.bizs = bizs;}public Map<String, String> getBiz2Channel() {return biz2Channel;}public void setBiz2Channel(Map<String, String> biz2Channel) {this.biz2Channel = biz2Channel;}}public static class Scene {private String type;private String desc;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE, false, false, Scene.class);}}
}

  方案1和方案2略有不同,針對一些屬性,我們需要做一些邏輯處理。方案1中將源屬性和邏輯之后的屬性都放在了同一類中,方案二則是將源屬性單獨放到一個靜態類中,最終處理過后的屬性放在了目標類中。另外二者的doBind方法也是有區別的,仔細看一下BaseConfigCenterBean這個類就可以了。

?

? ? ?就先分享這么多了,更多分享請關注我們的技術公眾吧!!!

  參考文章:算法和技術SHARING

轉載于:https://www.cnblogs.com/hujunzheng/p/9697282.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/531221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531221.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java接口簽名(Signature)實現方案

預祝大家國慶節快樂&#xff0c;趕快迎接美麗而快樂的假期吧&#xff01;&#xff01;&#xff01; 前言 在為第三方系統提供接口的時候&#xff0c;肯定要考慮接口數據的安全問題&#xff0c;比如數據是否被篡改&#xff0c;數據是否已經過時&#xff0c;數據是否可以重復提交…

Git rebase命令實戰

一、前言 一句話&#xff0c;git rebase 可以幫助項目中的提交歷史干凈整潔&#xff01;&#xff01;&#xff01; 二、避免合并出現分叉現象 git merge操作 1、新建一個 develop 分支 2、在develop分支上新建兩個文件 3、然后分別執行 add、commit、push 4、接著切換到master分…

HttpServletRequestWrapper使用技巧(自定義session和緩存InputStream)

一、前言 javax.servlet.http.HttpServletRequestWrapper 是一個開發者可以繼承的類&#xff0c;我們可以重寫相應的方法來實現session的自定義以及緩存InputStream&#xff0c;在程序中可以多次獲取request body的內容。 二、自定義seesion import javax.servlet.http.*;publi…

spring注解工具類AnnotatedElementUtils和AnnotationUtils

一、前言 spring為開發人員提供了兩個搜索注解的工具類&#xff0c;分別是AnnotatedElementUtils和AnnotationUtils。在使用的時候&#xff0c;總是傻傻分不清&#xff0c;什么情況下使用哪一個。于是我做了如下的整理和總結。 二、AnnotationUtils官方解釋 功能 用于處理注解&…

windows系統nexus3安裝和配置

一、前言 為什么要在本地開發機器上安裝nexus&#xff1f;首先聲明公司內部是有自己的nexus倉庫&#xff0c;但是對上傳jar包做了限制&#xff0c;不能暢快的上傳自己測試包依賴。于是就自己在本地搭建了一個nexus私服&#xff0c;即可以使用公司nexus私服倉庫中的依賴&#xf…

Springmvc借助SimpleUrlHandlerMapping實現接口開關功能

一、接口開關功能 1、可配置化&#xff0c;依賴配置中心 2、接口訪問權限可控 3、springmvc不會掃描到&#xff0c;即不會直接的將接口暴露出去 二、接口開關使用場景 和業務沒什么關系&#xff0c;主要方便查詢系統中的一些狀態信息。比如系統的配置信息&#xff0c;中間件的狀…

log4j平穩升級到log4j2

一、前言 公司中的項目雖然已經用了很多的新技術了&#xff0c;但是日志的底層框架還是log4j&#xff0c;個人還是不喜歡用這個的。最近項目再生產環境上由于log4j引起了一場血案&#xff0c;于是決定升級到log4j2。 二、現象 雖然生產環境有多個結點分散高并發帶來的壓力&…

Springboot集成ES啟動報錯

報錯內容 None of the configured nodes are available elasticsearch.yml配置 cluster.name: ftest node.name: node-72 node.master: true node.data: true network.host: 112.122.245.212 http.port: 39200 transport.tcp.port: 39300 discovery.zen.ping.unicast.hosts: [&…

高效使用hibernate-validator校驗框架

一、前言 高效、合理的使用hibernate-validator校驗框架可以提高程序的可讀性&#xff0c;以及減少不必要的代碼邏輯。接下來會介紹一下常用一些使用方式。 二、常用注解說明 限制說明Null限制只能為nullNotNull限制必須不為nullAssertFalse限制必須為falseAssertTrue限制必須為…

kafka-manager配置和使用

kafka-manager配置 最主要配置就是用于kafka管理器狀態的zookeeper主機。這可以在conf目錄中的application.conf文件中找到。 kafka-manager.zkhosts"my.zookeeper.host.com:2181" 當然也可以聲明為zookeeper集群。 kafka-manager.zkhosts"my.zookeeper.host.co…

kafka告警簡單方案

一、前言 為什么要設計kafka告警方案&#xff1f;現成的監控項目百度一下一大堆&#xff0c;KafkaOffsetMonitor、KafkaManager、 Burrow等&#xff0c;具體參考&#xff1a;kafka的消息擠壓監控。由于本小組的項目使用的kafka集群并沒有被公司的kafka-manager管理&#xff0c;…

RedisCacheManager設置Value序列化器技巧

CacheManager基本配置 請參考博文&#xff1a;springboot2.0 redis EnableCaching的配置和使用 RedisCacheManager構造函數 /*** Construct a {link RedisCacheManager}.* * param redisOperations*/ SuppressWarnings("rawtypes") public RedisCacheManager(RedisOp…

Nginx配置以及域名轉發

工程中的nginx配置 #user nobody; worker_processes 24; error_log /home/xxx/opt/nginx/logs/error.log; pid /home/xxx/opt/nginx/run/nginx.pid;events {use epoll;worker_connections 102400; }http {include /home/xxx/opt/nginx/conf.d/mime.types;default_…

java接口簽名(Signature)實現方案續

一、前言 由于之前寫過的一片文章 &#xff08;java接口簽名(Signature)實現方案 &#xff09;收獲了很多好評&#xff0c;此次來說一下另一種簡單粗暴的簽名方案。相對于之前的簽名方案&#xff0c;對body、paramenter、path variable的獲取都做了簡化的處理。也就是說這種方式…

支付寶敏感信息解密

支付寶官方解密文檔&#xff1a;https://docs.alipay.com/mini/introduce/aes String response "小程序前端提交的";//1. 獲取驗簽和解密所需要的參數 Map<String, String> openapiResult JSON.parseObject(response,new TypeReference<Map<String, St…

HashMap 源碼閱讀

前言 之前讀過一些類的源碼&#xff0c;近來發現都忘了&#xff0c;再讀一遍整理記錄一下。這次讀的是 JDK 11 的代碼&#xff0c;貼上來的源碼會去掉大部分的注釋, 也會加上一些自己的理解。 Map 接口 這里提一下 Map 接口與1.8相比 Map接口又新增了幾個方法&#xff1a;   …

SpringMvc接口中轉設計(策略+模板方法)

一、前言 最近帶著兩個兄弟做支付寶小程序后端相關的開發&#xff0c;小程序首頁涉及到很多查詢的服務。小程序后端服務在我司屬于互聯網域&#xff0c;相關的查詢服務已經在核心域存在了&#xff0c;查詢這塊所要做的工作就是做接口中轉。參考了微信小程序的代碼&#xff0c;發…

SpringSecurity整合JWT

一、前言 最近負責支付寶小程序后端項目設計&#xff0c;這里主要分享一下用戶會話、接口鑒權的設計。參考過微信小程序后端的設計&#xff0c;會話需要依靠redis。相關的開發人員和我說依靠Redis并不是很靠譜&#xff0c;redis在業務高峰期不穩定&#xff0c;容易出現問題&…

Springboot定時任務原理及如何動態創建定時任務

一、前言 上周工作遇到了一個需求&#xff0c;同步多個省份銷號數據&#xff0c;解綁微信粉絲。分省定時將銷號數據放到SFTP服務器上&#xff0c;我需要開發定時任務去解析文件。因為是多省份&#xff0c;服務器、文件名規則、數據規則都不一定&#xff0c;所以要做成可配置是有…

轉載:ThreadPoolExecutor 源碼閱讀

前言 之前研究了一下如何使用ScheduledThreadPoolExecutor動態創建定時任務(Springboot定時任務原理及如何動態創建定時任務)&#xff0c;簡單了解了ScheduledThreadPoolExecutor相關源碼。今天看了同學寫的ThreadPoolExecutor 的源碼解讀&#xff0c;甚是NB&#xff0c;必須轉…