本文介紹自建Apollo配置中心如何遷移到MSE Nacos配置中心。
前提條件
已創建MSE Nacos集群,且Nacos版本為專業版。具體操作,請參見創建Nacos引擎。
遷移說明
Apollo的模型和MSE Nacos的模型如下:
Apollo模型 | MSE Nacos模型 |
|
|
本遷移文檔將單個Apollo環境中的所有配置以文件的方式導入到Nacos中的一個命名空間,將以如下規則進行映射:
不同的Apollo環境可以映射為不同的Nacos實例,也可以映射到同一個Nacos實例下的不同命名空間之中,可自行選擇。
Apollo的
cluster
對應Nacos中的group
,Apollo中的{appId}.{namespace}.{format}
對應Nacos中的dataId
。Apollo中的
appId
將映射為Nacos配置中的應用appName
。
步驟一:導出Apollo配置
導出工具導出zip包
登錄自建Apollo控制臺。
本文以官方Demo地址為例。
在我的應用頁面,單擊目標應用名稱。
在目標應用詳情頁的右上方,選擇管理員工具 > 配置導出導入。在配置導出導入頁面,勾選選擇導出的環境,然后單擊導出。
不同的環境請分獨立導出不同文件,文件名以.zip
結尾。
SQL導出文件
低版本Apollo沒有文件導出功能,對于舊版本Apollo,可以從數據庫中直接將配置導出,請按照以下SQL進行配置的導出,可以選擇導出為JSON格式,或者Excel格式,文件名分別以.json
和.xlsx
結尾。
select distinct a.NamespaceId,b.NamespaceName,c.`Comment` AS 'NamespaceDesc' ,b.AppId,b.ClusterName,a.Key,a.Type ,a.Value,a.Comment,a.LineNum from ITEM a left join Namespace b on a.NamespaceId=b.id left join AppNamespace c on c.Name=b.NamespaceName where a.IsDeleted=0 order by a.NamespaceId,a.LineNum ;
步驟二:文件轉換
對Apollo配置進行了導出,可以獲得一個配置導出文件(文件以.zip,.json,.xlsx結尾),需要將文件轉換為可以直接在MSE Nacos控制臺中進行導入的文件格式。
下載轉換工具:curl -O https://msesync.oss-cn-hangzhou.aliyuncs.com/ApolloConfigTransfer.jar。
執行轉換程序:java -DsourceFilePath={apollo導出文件的完整路徑} -jar ApolloConfigTransfer.jar。
程序執行成功后,可以在當前目錄下獲得一個名為nacos_config_import_{apollo文件名}_{時間戳}.zip的文件。
步驟三:導入Nacos配置
步驟四:更改依賴
在應用項目中將Apollo的依賴更改為Spring Cloud Alibaba的依賴。
修改前:
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>{apollo.version}</version>
</dependency>
修改后:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.10</version>
</dependency>
如果您使用的是Spring Cloud Alibaba 2022.x以及2023.x版本建議升級到2023.0.1.2版本。
如果您使用的是Spring Cloud Alibaba 2021.x版本建議升級到2021.0.6.1版本。
如果您使用的是Spring Cloud Alibaba 2.x版本建議升級到2.2.10版本。
步驟五:改造代碼
應用程序使用Apollo動態配置有以下幾種用法。
@Value注解
該種用法是在一個SpringBean中通過@Value
注解引用一個屬性值,spring cloud alibaba框架可以平滑支持,無需代碼改動。
@RestController
@RefreshScope
public class DemoController {
@Value("testKey")
String testKey = "value";
@RequestMapping("/valuekey")
public String getNacosTestKey() {
return testKey;
}
}
@ApolloConfig注解
該種用法是通過@ApolloConfig
,注冊一個SpringBean,并且將對象的字段對應的屬性值自動注入該對象中,可以通過Spring的標準注解@ConfigurationProperties
直接替換。
@Configuration
@ConfigurationProperties
public class CNStackInfoConfig {
private String name;
private int customerCount;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCustomerCount() {
return customerCount;
}
public void setCustomerCount(int customerCount) {
this.customerCount = customerCount;
}
}
Apollo Config API
該種用法是直接調用Apollo的API獲取某個指定namespace中的單個屬性值。
ConfigService.getAppConfig().getProperty("testkey", "defaulyv1");
ConfigService.getAppConfig("namespace").getProperty("testkey", "defaulyv1");
在Nacos中,可以通過二次封裝支持。示例如下:
@Autowired
NacosConfigPropertiesService nacosConfigPropertiesService;
private void method(){
String value=nacosConfigPropertiesService.getProperties("appId1.application.properties", "group","key","defaultValue");
}
package com.example.demo.service;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.client.config.common.GroupKey;
import com.alibaba.nacos.client.config.listener.impl.PropertiesListener;
import com.alibaba.nacos.common.utils.StringUtils;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@Component
public class NacosConfigPropertiesService {
Map<String, Properties> namespacePropertiesMap = new HashMap<>();
@PostConstruct
private void postConstruct() {
try {
initProperties("appId1.application.properties", "group");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Autowired
private NacosConfigManager nacosConfigManager;
public String getProperties(final String dataId, final String group, String key, String defaultValue) {
return namespacePropertiesMap.get(GroupKey.getKey(dataId, group)).getProperty(key, defaultValue);
}
private void initProperties(final String dataId, final String group) throws Exception {
String aDefault = nacosConfigManager.getConfigService()
.getConfigAndSignListener(dataId, group, 3000L, new PropertiesListener() {
@Override
public void innerReceive(Properties properties) {
namespacePropertiesMap.put(GroupKey.getKey(dataId, group), properties);
}
});
if (StringUtils.isBlank(aDefault)) {
namespacePropertiesMap.put(GroupKey.getKey(dataId, group), new Properties());
} else {
Properties properties = new Properties();
properties.load(new StringReader(aDefault));
namespacePropertiesMap.put(GroupKey.getKey(dataId, group), properties);
}
}
}
ApolloConfigChangeListener回調
該種用法支持在Apollo中指定前綴的key發生變更時回調當前方法。
Apollo中的用法:
/**
* notify listener
* @param configChangeEvent
*/
@ApolloConfigChangeListener(interestedKeyPrefixes = {"data."})
public void apolloNotify(ConfigChangeEvent configChangeEvent) {
System.out.println(configChangeEvent.changedKeys());
}
Nacos中的用法:
private void apolloNotify(ConfigChangeEvent event) {
//notify.
System.out.println(event.getChangeItems());
}
@PostConstruct
public void registerListener() throws Exception {
interestedPrefix("appId1.application.properties","default","data.");
}
private void interestedPrefix(String dataId, String group, String prefix) throws Exception {
NacosPrefixKeyPropertiesListener prefixKeyPropertiesListener = new NacosPrefixKeyPropertiesListener(prefix) {
@Override
public void configChanged(ConfigChangeEvent event) {
notifyOnChange(event);
}
};
nacosConfigManager.getConfigService().addListener(dataId, group, prefixKeyPropertiesListener);
}
Nacos屬性前綴監聽器:
public abstract class NacosPrefixKeyPropertiesListener extends AbstractConfigChangeListener {
private String prefix;
public NacosPrefixKeyPropertiesListener(String prefix) {
this.prefix = prefix;
}
@Override
public final void receiveConfigChange(ConfigChangeEvent event) {
Iterator<ConfigChangeItem> iterator = event.getChangeItems().iterator();
while (iterator.hasNext()) {
if (!iterator.next().getKey().startsWith(prefix)) {
iterator.remove();
}
}
if (event.getChangeItems().isEmpty()) {
return;
}
configChanged(event);
}
public abstract void configChanged(ConfigChangeEvent event);
}
步驟六:修改連接地址
引入Nacos配置
##原有apollo的配置 #環境-Dappllo.env={env} apollo.meta=http://127.0.0.1:8070 apollo.bootstrap.enabled=true #指定引入的命名空間 apollo.bootstrap.namespaces=application,namespace1 #指定集群 apollo.bootstrap.cluster=default #指定應用的 app.id=app1
修改為Nacos的地址
#指定加載的配置dataId及group spring.config.import=nacos:app1.application.properties?group=default&refreshEnabled=true,nacos:app1.namespace1.properties?group=default&refreshEnabled=true #指定MSE Nacos的地址 spring.cloud.nacos.config.server-addr=mse-xxx-p.nacos-ans.mse.aliyuncs.com #指定MSE Nacos的命名空間 spring.cloud.nacos.config.namespace={mse nacos中指定環境對應namespaceid,比如5babe1ee-1352-xxxx-bd7b-7e7ce892e2ab}
重啟服務。