日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

apollo源码分析 感知_Kitty中的动态线程池支持Nacos,Apollo多配置中心了

發布時間:2024/7/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 apollo源码分析 感知_Kitty中的动态线程池支持Nacos,Apollo多配置中心了 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 回顧昨日
  • nacos 集成
    • Spring Cloud Alibaba 方式
    • Nacos Spring Boot 方式
  • Apollo 集成
  • 自研配置中心對接
  • 無配置中心對接
  • 實現源碼分析
    • 兼容 Apollo 和 Nacos NoClassDefFoundError
    • Apollo 自動刷新問題

回顧昨日

上篇文章 《一時技癢,擼了個動態線程池,源碼放 Github 了》發出后很多讀者私下問我這個能不能用到工作中,用肯定是可以用的,本身來說是對線程池的擴展,然后對接了配置中心和監控。

目前用的話主要存在下面幾個問題:

  • 還沒發布到 Maven 中央倉庫(后續會做),可以自己編譯打包發布到私有倉庫(臨時方案)
  • 耦合了 Nacos,如果你項目中沒有用 Nacos 或者用的其他的配置中心怎么辦?(本文內容)
  • 只能替換業務線程池,像一些框架中的線程池無法替換(構思中)

本文的重點就是介紹如何對接 Nacos 和 Apollo,因為一開始就支持了 Nacos,但是支持的方式是依賴了 Spring Cloud Alibaba ,如果是沒有用 Spring Cloud Alibaba 如何支持,也是需要擴展的。

Nacos 集成

Nacos 集成的話分兩種方式,一種是你的項目使用了 Spring Cloud Alibaba ,另一種是只用了 Spring Boot 方式的集成。

Spring Cloud Alibaba 方式

加入依賴:

com.cxytiandikitty-spring-cloud-starter-dynamic-thread-pool

然后在 Nacos 中增加線程池的配置,比如:

kitty.threadpools.executors[0].threadPoolName=TestThreadPoolExecutor
kitty.threadpools.executors[0].corePoolSize=4
kitty.threadpools.executors[0].maximumPoolSize=4
kitty.threadpools.executors[0].queueCapacity=5
kitty.threadpools.executors[0].queueCapacityThreshold=22

然后在項目中的 bootstrap.properties 中配置要使用的 Nacos data-id。

spring.cloud.nacos.config.ext-config[0].data-id=kitty-cloud-thread-pool.properties
spring.cloud.nacos.config.ext-config[0].group=BIZ_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true

Nacos Spring Boot 方式

如果你的項目只是用了 Nacos 的 Spring Boot Starter,比如下面:

com.alibaba.bootnacos-config-spring-boot-starter

那么集成的步驟跟 Spring Cloud Alibaba 方式一樣,唯一不同的就是配置的加載方式。使用@NacosPropertySource 進行加載。

@NacosPropertySource(dataId = NacosConstant.HREAD_POOL, groupId = NacosConstant.BIZ_GROUP, autoRefreshed = true, type = ConfigType.PROPERTIES)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

然后需要在 bootstrap.properties 中關閉 Spring Cloud Alibaba Nacos Config 的自動配置。

spring.cloud.nacos.config.enabled=false

Apollo 集成

Apollo 的使用我們都是用它的 client,依賴如下:

? com.ctrip.framework.apolloapollo-client1.4.0

集成 Thread-Pool 還是老的步驟,先添加 Maven 依賴:

com.cxytiandikitty-spring-cloud-starter-dynamic-thread-pool

然后配置線程池配置的 namespace:

apollo.bootstrap.namespaces=thread-pool-config

Properties 不用加后綴,如果是 yaml 文件那么需要加上后綴:

apollo.bootstrap.namespaces=thread-pool-config.yaml

如果你項目中用到了多個 namespace 的話,需要在線程池的 namespace 中指定,主要是監聽配置修改需要用到。

kitty.threadpools.apolloNamespace=thread-pool-config.yaml

自研配置中心對接

如果你們項目使用的是自研的配置中心那該怎么使用動態線程池呢?

最好的方式是跟 Nacos 一樣,將配置跟 Spring 進行集成,封裝成 PropertySource。

Apollo 中集成 Spring 代碼參考:https://github.com/ctripcorp/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java[1]

因為配置類是用的@ConfigurationProperties,這樣就相當于無縫集成了。

如果沒和 Spring 進行集成,那也是有辦法的,可以在項目啟動后獲取你們的配置,然后修改

DynamicThreadPoolProperties 配置類,再初始化線程池即可,具體步驟跟下面的無配置中心對接一致。DynamicThreadPoolManager 提供了 createThreadPoolExecutor()來創建線程池。

無配置中心對接

如果你的項目中沒有使用配置中心怎么辦?還是可以照樣使用動態線程池的。

直接將線程池的配置信息放在項目的 application 配置文件中即可,但是這樣的缺點就是無法動態修改配置信息了。

如果想有動態修改配置的能力,可以稍微擴展下,這邊我提供下思路。

編寫一個 Rest API,參數就是整個線程池配置的內容,可以是 Properties 文件也可以是 Yaml 文件格式。

這個 API 的邏輯就是注入我們的 DynamicThreadPoolProperties,調用 refresh()刷新 Properties 文件,調用 refreshYaml()刷新 Yaml 文件。

然后注入 DynamicThreadPoolManager,調用 refreshThreadPoolExecutor()刷新線程池參數。

實現源碼分析

首先,我們要實現的需求是同時適配 Nacos 和 Apollo 兩個主流的配置中心,一般有兩種做法。

第一種:將跟 Nacos 和 Apollo 相關的代碼獨立成一個模塊,使用者按需引入。

第二種:還是一個項目,內部做兼容。

我這邊采取的是第二種,因為代碼量不多,沒必要拆分成兩個。

需要在 pom 中同時增加兩個配置中心的依賴,需要設置成可選(optional=true)。

com.alibaba.cloudspring-cloud-alibaba-nacos-configtrue
com.ctrip.framework.apolloapollo-client1.4.0true

然后內部將監聽配置動態調整線程池參數的邏輯分開,ApolloConfigUpdateListener 和 NacosConfigUpdateListener。

在自動裝配 Bean 的時候按需裝配對應的 Listener。

@ImportAutoConfiguration(DynamicThreadPoolProperties.class)
@Configuration
public class DynamicThreadPoolAutoConfiguration {
@Bean
@ConditionalOnClass(value = com.alibaba.nacos.api.config.ConfigService.class)
public NacosConfigUpdateListener nacosConfigUpdateListener() {
return new NacosConfigUpdateListener();
}
@Bean
@ConditionalOnClass(value = com.ctrip.framework.apollo.ConfigService.class)
public ApolloConfigUpdateListener apolloConfigUpdateListener() {
return new ApolloConfigUpdateListener();
}

}

兼容 Apollo 和 Nacos NoClassDefFoundError

通過@ConditionalOnClass 來判斷當前項目中使用的是哪種配置中心,然后裝配對應的 Listener。上面的代碼看上去沒問題,在實際使用的過程去報了下面的錯誤:

Caused by: java.lang.NoClassDefFoundError: Lcom/alibaba/nacos/api/config/ConfigService;
at java.lang.Class.getDeclaredFields0(Native Method) ~[na:1.8.0_40]
at java.lang.Class.privateGetDeclaredFields(Class.java:2583) ~[na:1.8.0_40]
at java.lang.Class.getDeclaredFields(Class.java:1916) ~[na:1.8.0_40]
at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:755) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
... 22 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.alibaba.nacos.api.config.ConfigService
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_40]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_40]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_40]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_40]
... 26 common frames omitted

比如我的項目是用的 Apollo,然后我集成了動態線程池,在啟動的時候就報上面的錯誤了,錯誤原因是找不到 Nacos 相關的類。

但其實我已經用了@ConditionalOnClass 來判斷,這個是因為你的 DynamicThreadPoolAutoConfiguration 類是生效的,Spring 會去裝載 DynamicThreadPoolAutoConfiguration 類,DynamicThreadPoolAutoConfiguration 中有 NacosConfigUpdateListener 的實例化操作,而項目中又沒有依賴 Nacos,所以就報錯了。

這種情況我們需要將裝配的邏輯拆分的更細,直接用一個單獨的類去配置,將@ConditionalOnClass 放在類上。

這里我采用了靜態內部類的方式,如果項目中沒有依賴 Nacos,那么 NacosConfiguration 就不會生效,也就不會去初始化 NacosConfigUpdateListener。

@Configuration
@ConditionalOnClass(value = com.alibaba.nacos.api.config.ConfigService.class)
protected static class NacosConfiguration {
@Bean
public NacosConfigUpdateListener nacosConfigUpdateListener() {
return new NacosConfigUpdateListener();
}
}
@Configuration
@ConditionalOnClass(value = com.ctrip.framework.apollo.ConfigService.class)
protected static class ApolloConfiguration {
@Bean
public ApolloConfigUpdateListener apolloConfigUpdateListener() {
return new ApolloConfigUpdateListener();
}
}

這個地方我順便提一個點,就是為什么我們平時要多去看看開源框架的源碼。因為像這種適配多個框架的邏輯比較常見,那么一些開源框架中肯定也有類似的邏輯。如果你之前有看過其他的框架是怎么實現的,那么這里你就會直接采取那種方式。

比如 Spring Cloud OpenFeign 中對 Http 的客戶端做了多個框架的適配,你可以用 HttpClient 也可以用 Okhttp,這不就是跟我們這個一樣的邏輯么。

我們看下源碼就知道了,如下圖:

Apollo 自動刷新問題

在實現的過程中還遇到一個問題也跟大家分享下,就是 Apollo 中@ConfigurationProperties 配置類,在配置信息變更后不會自動刷新,需要配合 RefreshScope 或者 EnvironmentChangeEvent 來實現。

下圖是 Apollo 文檔的原話:

圖片

Nacos 刷新是沒問題的,只不過在收到配置變更的消息時,配置信息還沒刷新到 Bean 里面去,所以再刷新的時候單獨起了一個線程去做,然后在這個線程中睡眠了 1 秒鐘(可通過配置調整)。

如果按照 Apollo 文檔中給的方式,肯定是可以實現的。但是不太好,因為需要依賴 Spring Cloud Context。主要是考慮到使用者并不一定會用到 Spring Cloud,我們的基礎是 Spring Boot。

萬一使用者就是在 Spring Boot 項目中用了 Apollo, 然后又用了我的動態線程池,這怎么搞?

最后我采用了手動刷新的方式,當配置發生變更的時候,我會通過 Apollo 的客戶端,重新拉取整個配置文件的內容,然后手動刷新配置類。

config.addChangeListener(changeEvent -> {
ConfigFileFormat configFileFormat = ConfigFileFormat.Properties;
String getConfigNamespace = finalApolloNamespace;
if (finalApolloNamespace.contains(ConfigFileFormat.YAML.getValue())) {
configFileFormat = ConfigFileFormat.YAML;
// 去除.yaml后綴,getConfigFile時候會根據類型自動追加
getConfigNamespace = getConfigNamespace.replaceAll("." + ConfigFileFormat.YAML.getValue(), "");
}
ConfigFile configFile = ConfigService.getConfigFile(getConfigNamespace, configFileFormat);
String content = configFile.getContent();
if (finalApolloNamespace.contains(ConfigFileFormat.YAML.getValue())) {
poolProperties.refreshYaml(content);
} else {
poolProperties.refresh(content);
}
dynamicThreadPoolManager.refreshThreadPoolExecutor(false);
log.info("線程池配置有變化,刷新完成");
});

刷新邏輯:

public void refresh(String content) {
Properties properties = new Properties();
try {
properties.load(new ByteArrayInputStream(content.getBytes()));
} catch (IOException e) {
log.error("轉換Properties異常", e);
}
doRefresh(properties);
}
public void refreshYaml(String content) {
YamlPropertiesFactoryBean bean = new YamlPropertiesFactoryBean();
bean.setResources(new ByteArrayResource(content.getBytes()));
Properties properties = bean.getObject();
doRefresh(properties);
}
private void doRefresh(Properties properties) {
Map dataMap = new HashMap((Map) properties);
ConfigurationPropertySource sources = new MapConfigurationPropertySource(dataMap);
Binder binder = new Binder(sources);
binder.bind("kitty.threadpools", Bindable.ofInstance(this)).get();
}

目前只支持 Properties 和 Yaml 文件配置格式。

感興趣的 Star 下唄:https://github.com/yinjihuan/kitty[2]

關于作者:尹吉歡,簡單的技術愛好者,《Spring Cloud 微服務-全棧技術與案例解析》, 《Spring Cloud 微服務 入門 實戰與進階》作者, 公眾號 猿天地?發起人。個人微信 jihuan900,歡迎勾搭。

參考資料

[1]

PropertySourcesProcessor.java: https://github.com/ctripcorp/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java

[2]

kitty: https://github.com/yinjihuan/kitty

相關推薦

  • 噓!異步事件這樣用真的好么?

  • 一時技癢,擼了個動態線程池,源碼放Github了

  • 熬夜之作:一文帶你了解Cat分布式監控

  • 笑話:大廠都在用的任務調度框架我能不知道嗎???

  • 為什么參與開源項目的程序員找工作時特別搶手?

后臺回復?學習資料?領取學習視頻

如有收獲,點個在看,誠摯感謝

總結

以上是生活随笔為你收集整理的apollo源码分析 感知_Kitty中的动态线程池支持Nacos,Apollo多配置中心了的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。