先放出我的demo结构,有application.yml和application.properties
- 通过Yaml类来更新yml文件
主要的依赖时这个
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
我的实现是通过MyContextRefresher 这个类来完成的
package webswangguan.wangguan.refresher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.*;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import webswangguan.wangguan.jpa.entity.Route;
import webswangguan.wangguan.jpa.dao.RouteDao;
import java.io.*;
import java.net.URL;
import java.util.*;
/**
* 包: webswangguan.wangguan.refresher
* 作者: climber.luo
* 时间:2020.07.23 8:46
*/
@SuppressWarnings("all")
@RequestMapping
@RestController
@Async
public class MyContextRefresher {
@Autowired
private Environment environment;
// 读取yml 文件
private static URL resource = MyContextRefresher.class.getClassLoader().getResource("application.yml");
// 刷新配置的关键类
@Autowired
private ContextRefresher contextRefresher;
private static DumperOptions dumperOptions = new DumperOptions();
static {
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
dumperOptions.setPrettyFlow(false);
}
static Yaml yaml = new Yaml(dumperOptions);
@Autowired
private RouteDao routeDao;
/**
* 保存路由到数据库,但没更新
* @param route
*/
@GetMapping("addpropertis")
public void add(@RequestBody Route route) {
try {
routeDao.save(route);
refresher();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从数据库中更新路由
*
* @throws FileNotFoundException
*/
@GetMapping("refresh")
@Async
public void refresher() throws IOException {
storeYml(getMap(routeDao.findAll()));
contextRefresher.refresh();
}
/**
*将配置写到map,然后yaml.dump(map, writer),写到yml文件就行
之后就刷新配置
*/
private static void storeYml(Map map) throws IOException {
FileOutputStream stream = new FileOutputStream(resource.getFile());
OutputStreamWriter writer = new OutputStreamWriter(stream);
yaml.dump(map, writer);
stream.close();
writer.close();
}
private static Map getMap(List<Route> all) throws IOException {
FileInputStream stream = new FileInputStream(resource.getFile());
Map map = (Map) yaml.load(stream);
stream.close();
Map spring = (Map) map.get("spring");
Map cloud = (Map) spring.get("cloud");
Map gateway = (Map) cloud.get("gateway");
gateway.put("routes", getLinkedHashMaps(all));
cloud.put("gateway", gateway);
spring.put("cloud", cloud);
map.put("spring", spring);
return map;
}
private static ArrayList<LinkedHashMap> getLinkedHashMaps(List<Route> all) {
ArrayList<LinkedHashMap> routes = new ArrayList<>();
all.forEach(route -> {
LinkedHashMap<String, Object> route1 = new LinkedHashMap<>();
route1.put("id", route.getId());
route1.put("uri", route.getUri());
ArrayList<String> list = new ArrayList<>();
list.add(route.getPredicates());
route1.put("predicates", list);
ArrayList<String> list1 = new ArrayList<>();
list1.add(route.getFilters());
route1.put("filters", list1);
routes.add(route1);
});
return routes;
}
/**
* 通过id 删除路由
* @param id
* @throws IOException
*/
@GetMapping("/deleteroute/{id}")
public void deletefs(@PathVariable("id") String id) throws IOException {
routeDao.deleteById(id);
refresher();
}
/**
* 保存路由到数据库,并且更新配置
* @param route
* @throws IOException
*/
@GetMapping("updateproperties")
public void updateproperties(@RequestBody Route route) throws IOException {
// 更新数据库的route
routeDao.save(route);
// 刷新配置文件
refresher();
}
}
因为我是打算通过数据库来实现配置文件的,所以我是从数据库来查配置,然后更新到yml文件,然后通过
contextRefresher.refresh();来刷新配置。
2.通过application.properties来修改配置是UpDateProperties 这个类老实现的
package webswangguan.wangguan.refresher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import webswangguan.wangguan.jpa.dao.RouteDao;
import webswangguan.wangguan.jpa.entity.Route;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;
/**
* 此类(UpDateProperties)和 MyContextRefresher 类只能二选一,不可同时存在
* 一个是该yml 文件,一个是改 preperties 文件,
* <p>
* 修改配置文件的缺点是不能快速的完成配置的更新,至少2秒
* <p>
* 包: webswangguan.wangguan.refresher
* 作者: climber.luo
* 时间:2020.07.27 15:05
*/
@RequestMapping
@RestController
public class UpDateProperties {
private static URL url = UpDateProperties.class.getClassLoader().getResource("application.properties");
private InputStream resource = this.getClass().getClassLoader().getResourceAsStream("application.properties");
private final static String d = "spring.cloud.gateway.routes";
private static Properties properties = new Properties();
@Autowired
private ContextRefresher contextRefresher;
@Autowired
private RouteDao routeDao;
@GetMapping("properties")
@Scheduled(cron = "0 0 0 ? * *")
@Async
@Transactional
public void setproperties() {
try {
properties.load(resource);
// 之前的路由直接覆盖重新写入,避免出现问题,所以为 0
int e = 0;
List<Route> all = routeDao.findAll();
// 代码比yml 文件少,但是速度没啥区别,原因: contextRefresher.refresh() 消耗的时间比较多
for (Route route : all) {
properties.put(d + "[" + e + "]" + ".id", route.getId());
properties.put(d + "[" + e + "]" + ".uri", route.getUri());
properties.put(d + "[" + e + "]" + ".filters[0]", route.getFilters());
properties.put(d + "[" + e + "]" + ".predicates[0]", route.getPredicates());
e++;
}
OutputStream stream = new FileOutputStream(new File(url.getFile()));
properties.store(stream, "update");
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
contextRefresher.refresh();
}
}
由于我是在数据库里配置网关,所以这个配置是我个人需要的,这个配置起来就比yml 文件的map 中加map,list 啥的感觉简单点,没那么多层。只需要往properties中put()配置就可以了,最后通过文件输出流输出就可以,最终也是contextRefresher.refresh()来更新配置文件。
这个简单的将配置中心和网关gateway给强行整到一个项目里,由于是个小项目,可以这么玩,但一般不会这么玩。也就是将apollo或者说springCloud config 的功能单独提出来了。