nacos中cluster.conf读取解析记录
读取cluster文件
在源码中读取集群配置记录是在 com.alibaba.nacos.core.listener.StartingApplicationListener 的 contextPrepared 方法中发起的
那这个StartingApplicationListener 是怎么被调用的呢,我们可以看到这个StartingApplicationListener 实现了 NacosApplicationListener 接口,但是谁调用了它呢
从idea 的5个usages帮我们看到这里 ,这个启动监听类有5个没调用的地方;鼠标点击5 usages这里会给出如下图所示的调用链路,我们可以清晰的看到这里面被我用红色框选的地方就是我们实际被调用发起的。
点击打开SpringApplicationRunListener ,从名称我们可以看出,它是一个启动监听类结构如下图,至于SpringApplicationRunListener 怎么被调用我这里就不讲述了,需要知道的同学自行查阅相关资料。
我们往下看代码,就会发现我们的StartingApplicationListener在代码块中调用并初始化的,那么contextPrepared 是在什么时候被调用呢?
我们通读这个监听类,就会发现原来有一个地方,在代码的70行contextPrepared就会找到,使用了for循环遍历了nacosApplicationListeners并调用 NacosApplicationListener 的contextPrepared
读到这里我们就知道了它是怎么被初始化和调用的,细心的同学也会发现 NacosApplicationListener接口的所有方法都对应在 SpringApplicationRunListener 的每个方法。
cluster.conf解析说明
我们走进logClusterConf 方法,发现这里有个判断,意思是只有非单机模式才会读取cluster.conf
!EnvUtil.getStandaloneMode()
核心方法:
EnvUtil.readClusterConf();
我们点进入看看readClusterConf ,这里面有两个需要注意点,一个是 analyzeClusterConf 解析,还有一个是文件没找到的情况下的处理,EnvUtil.getMemberList()
public static List<String> readClusterConf() throws IOException {
// 这里有一个读取文件解析流的过程
try (Reader reader = new InputStreamReader(new FileInputStream(new File(getClusterConfFilePath())),
StandardCharsets.UTF_8)) {
// 这里解析对应的cluster.conf
return analyzeClusterConf(reader);
} catch (FileNotFoundException ignore) {
List<String> tmp = new ArrayList<>();
// 如果文件不存在的话,
String clusters = EnvUtil.getMemberList();
if (StringUtils.isNotBlank(clusters)) {
String[] details = clusters.split(",");
for (String item : details) {
tmp.add(item.trim());
}
}
return tmp;
}
}
private static final String DEFAULT_ADDITIONAL_PATH = "conf";
private static final String DEFAULT_ADDITIONAL_FILE = "cluster.conf";
/**
* 这里的意思是 读取在nacos.home目录下的 conf 目录里面的cluster.conf文件路径
*/
public static String getClusterConfFilePath() {
return Paths.get(getNacosHome(), DEFAULT_ADDITIONAL_PATH, DEFAULT_ADDITIONAL_FILE).toString();
}
/**
* 读取nacos.home 路径,如果没有配置默认读取user.home下面的nacos文件夹
*/
public static String getNacosHome() {
if (StringUtils.isBlank(nacosHomePath)) {
String nacosHome = System.getProperty(NACOS_HOME_KEY);
if (StringUtils.isBlank(nacosHome)) {
nacosHome = Paths.get(System.getProperty(NACOS_HOME_PROPERTY), NACOS_HOME_ADDITIONAL_FILEPATH).toString();
}
return nacosHome;
}
return nacosHomePath;
}
我们看看analyzeClusterConf 做了什么
public static List<String> analyzeClusterConf(Reader reader) throws IOException {
List<String> instanceList = new ArrayList<String>();
List<String> lines = IoUtils.readLines(reader);
String comment = "#";
for (String line : lines) {
String instance = line.trim();
// 这里的意思是如果一行数据开头就是 # ,如:#123456789 这样式的就不读了
if (instance.startsWith(comment)) {
// # it is ip
continue;
}
// 这里如果是行内有#号的,只解析开头到#号的内容,如: 0.0.0.0 # ip1 ,这样式的只解析前面的0.0.0.0
// 192.168.71.52:8848 # Instance A
if (instance.contains(comment)) {
instance = instance.substring(0, instance.indexOf(comment));
instance = instance.trim();
}
//这里是一行多个解析,用英文状态下的逗号","分割
// support the format: ip1:port,ip2:port # multi inline
int multiIndex = instance.indexOf(Constants.COMMA_DIVISION);
if (multiIndex > 0) {
instanceList.addAll(Arrays.asList(instance.split(Constants.COMMA_DIVISION)));
} else {
//support the format: 192.168.71.52:8848
instanceList.add(instance);
}
}
return instanceList;
}
那么,没有文件的情况下,怎么处理这个呢
public static String getMemberList() {
String val;
if (environment == null) {
// 从系统的环境变量取值为 nacos.member.list 配置
val = System.getenv(MEMBER_LIST_PROPERTY);
if (StringUtils.isBlank(val)) {
// 如果没有就从,启动配置参数中查找 nacos.member.list 配置
val = System.getProperty(MEMBER_LIST_PROPERTY);
}
} else {
// 如果spring的配置上下文存在,就从配置中获取 nacos.member.list 配置
val = getProperty(MEMBER_LIST_PROPERTY);
}
return val;
}
使用cluster.conf
其实可以看到在logClusterConf 方法中,只是读取了对应的信息并输出,并没有做什么应用,那它是怎么真实工作的呢?
List<String> clusterConf = EnvUtil.readClusterConf();
LOGGER.info("The server IP list of Nacos is {}", clusterConf);
反查,对readClusterConf方法调用的地方会发现,还有一个类,也就是FileConfigMemberLookup
在FileConfigMemberLookup的readClusterConfFromDisk 方法中,我们可以清晰的看到,读取了配置并做了解析处理为一个Member对象的去重集合(HashSet);
在FileConfigMemberLookup 类中有两个调用readClusterConfFromDisk的地方,一处是启动的时候,doStart方法,调用做出对应的处理;一处是使用FileWatcher监听文件变更后,重新调用读取最新的配置信息。
在afterLookup 实际调用的是 ServerMemberManager.memberChange 事件,该事件行为上去对比已有的列表和现在新读取的列表有什么不同,并将新的变化同步到文件中并广播出去。