背景
参加一个比赛,测评程序每次在测试的时候,都是随机分配3台服务器,IP
地址不固定。程序提供一个接口,每次测评开始前会传过来3个IP
地址,需要动态修改yml
配置文件中程序连接的redis
集群节点
添加依赖
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
修改文件的工具类
package com.weilc.chatservice.util;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class YmlUtil {
private final static DumperOptions OPTIONS = new DumperOptions();
private static File file;
private static InputStream ymlInputSteam;
private static Object CONFIG_MAP;
private static Yaml yaml;
static {
//将默认读取的方式设置为块状读取
OPTIONS.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
}
/**
* 使用其他方法之前必须调用一次 设置yml的输出文件,当没有设置输入流时可以不设置输入流,默认以此文件读入
*
* @param file 输出的文件
*/
public static void setYmlFile(File file) throws FileNotFoundException {
YmlUtil.file = file;
if (ymlInputSteam == null) {
setYmlInputSteam(new FileInputStream(file));
}
}
/**
* 使用其他方法之前必须调用一次 设置yml的输入流
*
* @param inputSteam 输入流
*/
public static void setYmlInputSteam(InputStream inputSteam) {
ymlInputSteam = inputSteam;
yaml = new Yaml(OPTIONS);
CONFIG_MAP = yaml.load(ymlInputSteam);
}
/**
* 根据键获取值
*
* @param key 键
* @return 查询到的值
*/
@SuppressWarnings("unchecked")
public static Object getByKey(String key) {
if (ymlInputSteam == null) {
return null;
}
String[] keys = key.split("\\.");
Object configMap = CONFIG_MAP;
for (String s : keys) {
if (configMap instanceof Map) {
configMap = ((Map<String, Object>) configMap).get(s);
} else {
break;
}
}
return configMap == null ? "" : configMap;
}
public static void saveOrUpdateByKey(String key, Object value) throws IOException {
KeyAndMap keyAndMap = new KeyAndMap(key).invoke();
key = keyAndMap.getKey();
Map<String, Object> map = keyAndMap.getMap();
map.put(key, value);
//将数据重新写回文件
yaml.dump(CONFIG_MAP, new FileWriter(file));
}
public static void removeByKey(String key) throws Exception {
KeyAndMap keyAndMap = new KeyAndMap(key).invoke();
key = keyAndMap.getKey();
Map<String, Object> map = keyAndMap.getMap();
Map<String, Object> fatherMap = keyAndMap.getFatherMap();
map.remove(key);
if (map.size() == 0) {
Set<Map.Entry<String, Object>> entries = fatherMap.entrySet();
for (Map.Entry<String, Object> entry : entries) {
if (entry.getValue() == map) {
fatherMap.remove(entry.getKey());
}
}
}
yaml.dump(CONFIG_MAP, new FileWriter(file));
}
private static class KeyAndMap {
private String key;
private Map<String, Object> map;
private Map<String, Object> fatherMap;
public KeyAndMap(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public Map<String, Object> getMap() {
return map;
}
public Map<String, Object> getFatherMap() {
return fatherMap;
}
@SuppressWarnings("unchecked")
public KeyAndMap invoke() {
if (file == null) {
System.err.println("请设置文件路径");
}
if (null == CONFIG_MAP) {
CONFIG_MAP = new LinkedHashMap<>();
}
String[] keys = key.split("\\.");
key = keys[keys.length - 1];
map = (Map<String, Object>) CONFIG_MAP;
for (int i = 0; i < keys.length - 1; i++) {
String s = keys[i];
if (map.get(s) == null || !(map.get(s) instanceof Map)) {
map.put(s, new HashMap<>(4));
}
fatherMap = map;
map = (Map<String, Object>) map.get(s);
}
return this;
}
}
}
测试
@PostMapping("/updateCluster")
public ResponseEntity updateCluster(@RequestBody String[] ips) throws Exception{
String redisNodes = "";
for (String ip : ips) {
redisNodes += ip + ":6379,";
redisNodes += ip + ":6380,";
}
//去除尾部空格
redisNodes = redisNodes.substring(0, redisNodes.length() - 1);
//修改配置文件
log.info("----------开始修改配置文件----------");
File file = new File("application.yml");
YmlUtil.setYmlFile(file);
log.info("yml节点nodes修改前:" + YmlUtil.getByKey("spring.redis.cluster.nodes"));
YmlUtil.saveOrUpdateByKey("spring.redis.cluster.nodes", redisNodes);
log.info("yml节点nodes修改后:" + YmlUtil.getByKey("spring.redis.cluster.nodes"));
//重启服务
String startSh = "cd ~ && sh start.sh run";
log.info("----------开始重启服务----------");
execLinux(startSh);
return ResponseEntity.ok().build();
}
private boolean execLinux(String cmd) {
try {
log.info("exec shell: " + cmd);
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(cmd);
int result = process.waitFor();
log.info("exec result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
我这里开发了一个接口用于接收IP
地址数组,然后开始拼接redis
集群的nodes
数据,最后使用工具类进行替换。然后执行重启服务的shell
脚本
问题
在实际的运行中,因为我们使用的是jar
包,上面测试类的第12
行代码需要读取配置文件的位置并修改,使用上面的代码读取不到,所以我们需要把配置文件单独拿出来,然后修改第12
行的代码,并且修改启动脚本,以指定配置文件的方式启动jar
包
修改代码
File file = new File("./application.yml");
修改启动脚本
java -jar demo.jar --spring.config.location=./application.yml