本文介绍自建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}
重启服务。