本地缓存数据存入文件中

演示代码

本地缓存数据存入文件

  public static void testQ15() {

        LinkedBlockingDeque<ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>> failureStatusCache = new LinkedBlockingDeque<>(4);

        LinkedBlockingDeque<ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>> successStatusCache = new LinkedBlockingDeque<>(4);

        LinkedBlockingDeque<ConcurrentHashMap<String, Integer>> pushedStateCache = new LinkedBlockingDeque<>(4);

        ObjectOutputStream oos = null;

//        初始化数据
        ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>> mapF = new ConcurrentHashMap<>();
        mapF.put("失败数据", new ConcurrentHashMap<Integer, Integer>() {{
            put(1, 0);
        }});
        failureStatusCache.add(mapF);
        ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>> mapS = new ConcurrentHashMap<>();
        mapS.put("成功数据", new ConcurrentHashMap<Integer, Integer>() {{
            put(2, 0);
        }});
        successStatusCache.add(mapS);
        pushedStateCache.add(new ConcurrentHashMap<String, Integer>() {{
            put("哈哈哈", 3);
        }});
        log.info("【SoulCache - saveData】持久化数据到文件");
        try {
            oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(PersistencePath.createFile("tzh"))));
            oos.writeObject(failureStatusCache);
            oos.writeObject(successStatusCache);
            oos.writeObject(pushedStateCache);
            log.info("【SoulCache - saveData】持久化数据到文件 | 结束");
        } catch (Exception e) {
            log.error("保存持久化文件异常", e);
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException ioe) {
                log.error("IO流关闭异常", ioe);
            }
        }
    }

从文件提取数据到本地缓存

  • 提取数据顺序必须和存入顺序一致,不然会报错
public static void testQ16() {
        ObjectInputStream ois = null;
        try {
            String path = PersistencePath.getPath("tzh");
            log.info("【SoulCache - loadData】加载到内存开始... | path:{}", path);

            ois = new ObjectInputStream(Files.newInputStream(Paths.get(path)));
            LinkedBlockingDeque<ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>> failureStatusCache = (LinkedBlockingDeque<ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>>) ois.readObject();
            LinkedBlockingDeque<ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>> successStatusCache = (LinkedBlockingDeque<ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>>) ois.readObject();
            LinkedBlockingDeque<ConcurrentHashMap<String, Integer>> pushedStateCache = (LinkedBlockingDeque<ConcurrentHashMap<String, Integer>>) ois.readObject();
            System.out.println("失败数据:"+failureStatusCache.getFirst().toString());
            System.out.println("成功数据:"+successStatusCache.getFirst().toString());
            System.out.println(pushedStateCache.getFirst().toString());
            log.info("【SoulCache - loadData】加载到内存 结束");
        } catch (Exception e) {
            log.error("加载内存持久化文件异常", e);
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
                PersistencePath.delFile("tzh");
            } catch (IOException ioe) {
                log.error("IO流关闭异常", ioe);
            }
        }
    }

工具类

public class PersistencePath {

	private static final Logger logger = LoggerFactory.getLogger(PersistencePath.class);

	/**
	 * 定时快照
	 *      ps:只针对状态报告
	 */
	private static AtomicInteger aofVersion = new AtomicInteger(0);

	/**
	 * Docker容器中对应持久化目录,挂载到宿主机的相同名称目录
	 *   /test:./PersistenceData
	 */
	private final static String path = "/Users/zeki/test/";

	//服务id  需要在不同的实现工程中自己去注入值   例如在网关项目中代表网关id
	public static String serverId="1";
	//服务名称   需要在不同的实现工程中自己去注入值
	public static String serverName="titan";

	private static String getAofVersion(){
		String _v = DateUtil.formatDateyyyyMMddHHmmss(System.currentTimeMillis()) + "_" + aofVersion.get();
		aofVersion.incrementAndGet();
		return _v;
	}

	/**
	 * 持久化文件的名字  与服务名、服务id有关
	 *    ps:  例子    视频网关集群里面网关id为1的持久化文件名称为  sms-vms-gateway1.obj
	 * @return
	 */
	private static String buildFileName(){
		return String.format("%s%s.obj", Optional.ofNullable(serverName).orElse(defaultServerName()), serverId);
	}

	/**
	 * 持久化文件的名字  与服务名、服务id有关
	 *    ps:  例子    视频网关集群里面网关id为1的持久化文件名称为  sms-vms-gateway1.obj20210407120000_1
	 * @return
	 */
	private static String buildFileName_AOF(){
		return buildFileName().concat(getAofVersion());
	}

	/**
	 * 持久化文件的名字  与服务名、服务id有关
	 *    ps:  例子    视频网关集群里面网关id为1的持久化文件名称为  sms-vms-gateway1.obj
	 * @param prefixName 可能一个应用系统里面会有多个持久化文件   通过这个参数指定名称
	 * @return
	 */
	private static String buildFileName(String prefixName){
		return String.format("%s%s_%s.obj", Optional.ofNullable(serverName).orElse(defaultServerName()), serverId, prefixName);
	}

	/**
	 * 获取文件路径
	 * @return 文件存在,返回文件路径,不存在,返回null
	 * @throws IOException
	 */
	public static String getPath() {
		String fileName = buildFileName();
		File file = new File(path, fileName);
		if (file.exists()) {
			return path + fileName;
		}
		logger.warn("getPath {}{} 不存在",path,fileName);
		return null;
	}

	/**
	 * 获取文件路径
	 * @return 文件存在,返回文件路径,不存在,返回null
	 * @throws IOException
	 */
	public static String getPath_AOF() {
		String fileName = buildFileName_AOF();
		File file = new File(path, fileName);
		if (file.exists()) {
			return path + fileName;
		}
		return null;
	}

	/**
	 * 获取文件路径
	 * @return 文件存在,返回文件路径,不存在,返回null
	 * @param prefixName 可能一个应用系统里面会有多个持久化文件   通过这个参数指定名称
	 * @throws IOException
	 */
	public static String getPath(String prefixName) {
		String fileName = buildFileName(prefixName);
		File file = new File(path, fileName);
		if (file.exists()) {
			return path + fileName;
		}
		return null;
	}

	/**
	 * 创建文件
	 * @return
	 * @throws IOException
	 */
	public static String createFile() throws IOException {
		String fileName = buildFileName();
		File file = new File(path, fileName);
		if (!file.exists()) {	//文件不存在,创建
			file.createNewFile();
		}
		return path + fileName;
	}

	/**
	 * 创建文件
	 * @return
	 * @throws IOException
	 */
	public static String createFile_AOF() throws IOException {
		String fileName = buildFileName_AOF();
		File file = new File(path, fileName);
		if (!file.exists()) {	//文件不存在,创建
			file.createNewFile();
		}
		return path + fileName;
	}

	/**
	 * 创建文件
	 * @param prefixName 可能一个应用系统里面会有多个持久化文件   通过这个参数指定名称
	 * @return
	 * @throws IOException
	 */
	public static String createFile(String prefixName) throws IOException {
		String fileName = buildFileName(prefixName);
		File file = new File(path, fileName);
		if (!file.exists()) {	//文件不存在,创建
			file.createNewFile();
		}
		return path + fileName;
	}

	/**
	 * 删除文件
	 * @return
	 * @throws IOException
	 */
	public static boolean delFile() {
		String fileName = buildFileName();
		File file = new File(path, fileName);
		if (file.exists()) {
			return file.delete();
		}
		return false;
	}

	/**
	 * 删除文件
	 * @param prefixName 可能一个应用系统里面会有多个持久化文件   通过这个参数指定名称
	 * @return
	 * @throws IOException
	 */
	public static boolean delFile(String prefixName) {
		String fileName = buildFileName(prefixName);
		File file = new File(path, fileName);
		if (file.exists()) {
			FileUtil.rename(file, String.format("%s_bak_%s_%s",
					fileName,
					cn.hutool.core.date.DateUtil.today(),
					IdUtil.fastSimpleUUID()), false, true);
			return true;
		}
		return false;
	}

	/**
	 * 默认服务名
	 */
	private static String defaultServerName() {
		return Optional.ofNullable(SpringUtil.getApplicationContext()).map(ApplicationContext::getApplicationName).orElse("NULL");
	}
}

实战

初始化操作

  • implements CommandLineRunner项目开始则执行以下代码,做个钩子,项目关闭时会触发该线程业务逻辑
@Override
	public void run(String... args) {
		// 狗子
		Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));
	}

执行 本地缓存数据存入文件 的线程任务

  • 这里的init方法是项目一启动会执行,将文件数据放进本地缓存中初始化
  • 这里的run方法是线程执行业务,项目结束运行,钩子触发该业务将本地缓存数据存放进文件中
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Component
public class ShutdownHook implements Runnable{

	private final static Logger log = LoggerFactory.getLogger(ShutdownHook.class);
	private static final List<Persistence> canBeHookList = new ArrayList<>();

	@Value("${server-node.id}")
	private String nodeId;
	@Value("${spring.application.name}")
	private String applicationName;

	@Resource
	private MonitorThread monitorThread;

	@PostConstruct
	public void init() {
		PersistencePath.serverId = nodeId;
		PersistencePath.serverName = applicationName;
		//对内存数据进行初始化
		SmsQueueManage.getInstance().loadLocalData();
		MemoryParamCache.getInstance().loadLocalData();
	}

	@Override
	public void run() {
		Thread.currentThread().setName("ShutdownHook_Thread");
		log.info("【sms-rules退出 - Thread:{}】- 钩子回调 开始...",Thread.currentThread().getName());

		monitorThread.stopMonitorAndAllThread();

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			log.error(Thread.currentThread().getName() + ": 线程休眠异常" ,e);
		}
		log.info("等待持久化队列管理类 size:{}",canBeHookList.size());
		for (Persistence q : canBeHookList) {
			q.saveData();
		}
		log.error("【sms-rules退出 - Thread:{}】- 钩子回调 完毕",Thread.currentThread().getName());
	}

	public static void add(Persistence p) {
		canBeHookList.add(p);
	}
	public static void remove(Persistence p) {
		canBeHookList.remove(p);
	}
}

持久化工具类

public class MemoryParamCache implements Persistence {

    private final static Logger log = LoggerFactory.getLogger(MemoryParamCache.class);

    /**
     * 号段持久化文件名称前缀
     */
    private static final String memoryParamCachePrefixFileName = "memoryParamCache";

    private volatile static MemoryParamCache instance;

    private MemoryParamCache(){
        ShutdownHook.add(this);
    }

    public static MemoryParamCache getInstance(){
        if (null == instance) {
            synchronized (MemoryParamCache.class) {
                if (null == instance) {
                    instance = new MemoryParamCache();
                }
            }
        }
        return instance;
    }

    @Override
    public void saveData() {
        ObjectOutputStream oos = null;
        String path = "";
        try {
            path = PersistencePath.createFile(memoryParamCachePrefixFileName);
            log.info("【MemoryParamCache - saveData】持久化内存参数到文件 | 开始 | path:{}",path);
            oos = new ObjectOutputStream(new FileOutputStream(path));
            oos.writeObject(RuleSymbol.cityMap);
            log.info("【MemoryParamCache - saveData】持久化内存参数到文件 | 结束 | path:{} | cityMap:{}",path , RuleSymbol.cityMap.size());
        } catch (Exception e) {
            log.error("【MemoryParamCache - 持久化内存参数到文件】异常 | path:{}",path, e);
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException ioe) {
                // ignore
            }
        }
    }

    /**
     * 磁盘上的缓存数据加载到内存
     */
    public void loadLocalData() {
        ObjectInputStream ois = null;
        String path = "";
        try {
            path = PersistencePath.getPath(memoryParamCachePrefixFileName);
            log.info("【MemoryParamCache - loadLocalData】内存参数缓存文件-->-->-->-->加载到内存开始... | path:{}",path);
            ois = new ObjectInputStream(new FileInputStream(path));
            RuleSymbol.cityMap = (ConcurrentHashMap<String, String>) ois.readObject();
            log.info("【MemoryParamCache - loadLocalData】内存参数缓存文件-->-->-->-->加载到内存 结束 | path:{} | cityMap:{}",path, RuleSymbol.cityMap.size());
        }catch (Exception e) {
            log.error("【sms-rules启动 - 加载持久化后的内存参数】异常 | path:{}", path,e);
        }finally {
            if(ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    // ignore
                }
                boolean b = PersistencePath.delFile(memoryParamCachePrefixFileName);
                log.info("【MemoryParamCache - loadLocalData】STEP-TWO 删除缓存数据文件:{}",b);
            }
        }
    }


}
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值