在项目启动后热更新配置文件(application.yml和application.properties)

先放出我的demo结构,有application.yml和application.properties
在这里插入图片描述

  1. 通过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 的功能单独提出来了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值