Jdk实现本地文件监听
监听实现: ConfigDirWatchContext
package com.jinko.apollo;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import de.schlichtherle.io.File;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static java.nio.file.StandardWatchEventKinds.*;
/**
* @author xxx
*/
public class ConfigDirWatchContext {
private static final Logger logger = LoggerFactory.getLogger(ConfigDirWatchContext.class);
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("ConfigDirWatchContextPool -%d").build(), new ThreadPoolExecutor.AbortPolicy());
private static WatchService WATCHER = null;
private static final String SUFFIX = ".properties";
private static final Map<String, Properties> CONFIGS = new ConcurrentHashMap<>();
private static final List<String> FAIL_PATH = new ArrayList<>(0);
private static final Path ROOT_PATH = Paths.get("/app/ecology/filesystem/config/");
private static final ConfigDirWatchContext SINGLE_INSTANCE = new ConfigDirWatchContext();
public static ConfigDirWatchContext newInstance() {
return SINGLE_INSTANCE;
}
public Map<String, Properties> getConfigs() {
return Collections.unmodifiableMap(CONFIGS);
}
public List<String> getFailList() {
return Collections.unmodifiableList(FAIL_PATH);
}
private ConfigDirWatchContext() {
recursiveRegister(ROOT_PATH);
this.watchStart();
}
private synchronized WatchService buildWatcher() {
if (Objects.nonNull(WATCHER)) {
return WATCHER;
}
try {
return WATCHER = FileSystems.getDefault().newWatchService();
} catch (Throwable e) {
logger.info("ConfigDirWatchService Throwable", e);
}
throw new RuntimeException("");
}
@SneakyThrows
private void recursiveRegister(Path path) {
// 这里的监听必须是目录
if (!path.toFile().exists() && !path.toFile().mkdirs()) {
logger.info("ConfigDirWatchService mkdir error {}", path.toAbsolutePath());
FAIL_PATH.add(path.toAbsolutePath().toString());
return;
}
// 创建WatchService,它是对操作系统的文件监视器的封装,相对之前,不需要遍历文件目录,效率要高很多
WatchService watcher = buildWatcher();
// 注册指定目录使用的监听器,监视目录下文件的变化;
// PS:Path必须是目录,不能是文件;
// StandardWatchEventKinds.ENTRY_MODIFY,表示监视文件的修改事件
path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY, ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
Files.list(path).forEach(child -> {
if (Files.exists(child)) {
if (Files.isDirectory(child)) {
recursiveRegister(child);
} else if (child.toString().endsWith(SUFFIX)) {
CONFIGS.put(getKey(child), read(child));
}
}
});
}
private String getKey(Path path) {
return ROOT_PATH.relativize(path).toString().replace(File.separator, ".");
}
private ConfigDirWatchContext watchStart() {
EXECUTOR.submit(() -> {
try {
// 创建一个线程,等待目录下的文件发生变化
logger.info("watchStart ... ");
while (true) {
// 获取目录的变化:
// take()是一个阻塞方法,会等待监视器发出的信号才返回。
// 还可以使用watcher.poll()方法,非阻塞方法,会立即返回当时监视器中是否有信号。
// 返回结果WatchKey,是一个单例对象,与前面的register方法返回的实例是同一个;
WatchKey key = buildWatcher().take();
// 处理文件变化事件:
// key.pollEvents()用于获取文件变化事件,只能获取一次,不能重复获取,类似队列的形式。
for (WatchEvent<?> event : key.pollEvents()) {
// event.kind():事件类型
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
//事件可能lost or discarded
continue;
}
Watchable watchable = key.watchable();
// 返回触发事件的文件或目录的路径(相对路径)
Path fullPath = Paths.get(watchable.toString(), event.context().toString());
logger.info("path={} , type={}", fullPath, event.kind());
handler(fullPath, event);
}
// 每次调用WatchService的take()或poll()方法时需要通过本方法重置
if (!key.reset()) {
break;
}
}
} catch (Throwable e) {
logger.info("ConfigDirWatchService Throwable", e);
}
});
return this;
}
private void handler(Path changePath, WatchEvent event) {
if (Objects.isNull(event) || Objects.isNull(changePath)) {
logger.info("hander error {} {} ", changePath, event);
return;
}
if (!Files.isDirectory(changePath)) {
handlerFile(changePath, event);
return;
}
if (ENTRY_CREATE.name().equals(event.kind().name())) {
/* 添加新的监听目录 */
recursiveRegister(changePath);
} else {
logger.info("not handler Directory {} {}", changePath, event.kind());
}
}
private void handlerFile(Path changePath, WatchEvent event) {
if (Objects.isNull(event) || Objects.isNull(changePath) || !changePath.toString().endsWith(SUFFIX)) {
logger.info("hander error {} {} ", changePath, event);
return;
}
if (ENTRY_CREATE.name().equals(event.kind().name()) || ENTRY_MODIFY.name().equals(event.kind().name())) {
CONFIGS.put(getKey(changePath), read(changePath));
} else if (ENTRY_DELETE.name().equals(event.kind().name())) {
CONFIGS.remove(getKey(changePath));
} else {
logger.info("handler {} {}", changePath, event.kind());
}
}
@SneakyThrows
private Properties read(Path path) {
Properties properties = new Properties();
properties.load(Files.newInputStream(path));
return properties;
}
@SneakyThrows
public static void main(String[] args) throws IOException {
Map<String, Properties> configs = new ConfigDirWatchContext()
.watchStart().getConfigs();
// Thread.sleep(100000000);
}
}
工具类: ConfigUtils
package com.jinko.apollo;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author xxx
*/
public class ConfigUtils {
private final ConfigDirWatchContext CONTEXT = ConfigDirWatchContext.newInstance();
private static final String SUFFIX = ".properties";
private final Map<String, Properties> CONFIGS = CONTEXT.getConfigs();
public static ConfigUtils newInstance() {
return new ConfigUtils();
}
public String get(String path, String key) {
return get(path, key, null);
}
public String get(String path, String key, String defaultValue) {
String realKey = String.format("%s%s", path, SUFFIX);
if (StringUtils.isBlank(path) || !CONFIGS.containsKey(realKey)) {
return defaultValue;
}
return CONFIGS.get(realKey).getProperty(key, defaultValue);
}
public Map<String, Properties> getConfigs() {
return CONFIGS;
}
public List<String> getFailPathList() {
return CONTEXT.getFailList();
}
@SneakyThrows
public static void main(String[] args) {
ConfigUtils configUtils = newInstance();
Map<String, Properties> configs = configUtils.getConfigs();
String key = configUtils.get("bestsign.config", "key");
String key1 = configUtils.get("", "key");
String key2 = configUtils.get(null, "key");
System.out.println(key);
Thread.sleep(100000);
}
}
package com.jinko.apollo;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author xuyuansheng
*/
public class ConfigUtils {
private final ConfigDirWatchContext CONTEXT = ConfigDirWatchContext.newInstance();
private static final String SUFFIX = ".properties";
private final Map<String, Properties> CONFIGS = CONTEXT.getConfigs();
public static ConfigUtils newInstance() {
return new ConfigUtils();
}
public String get(String path, String key) {
return get(path, key, null);
}
public String get(String path, String key, String defaultValue) {
String realKey = String.format("%s%s", path, SUFFIX);
if (StringUtils.isBlank(path) || !CONFIGS.containsKey(realKey)) {
return defaultValue;
}
return CONFIGS.get(realKey).getProperty(key, defaultValue);
}
public Map<String, Properties> getConfigs() {
return CONFIGS;
}
public List<String> getFailPathList() {
return CONTEXT.getFailList();
}
@SneakyThrows
public static void main(String[] args) {
ConfigUtils configUtils = newInstance();
Map<String, Properties> configs = configUtils.getConfigs();
String key = configUtils.get("bestsign.config", "key");
String key1 = configUtils.get("", "key");
String key2 = configUtils.get(null, "key");
System.out.println(key);
Thread.sleep(100000);
}
}
package com.jinko.apollo;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author xuyuansheng
*/
public class ConfigUtils {
private final ConfigDirWatchContext CONTEXT = ConfigDirWatchContext.newInstance();
private static final String SUFFIX = ".properties";
private final Map<String, Properties> CONFIGS = CONTEXT.getConfigs();
public static ConfigUtils newInstance() {
return new ConfigUtils();
}
public String get(String path, String key) {
return get(path, key, null);
}
public String get(String path, String key, String defaultValue) {
String realKey = String.format("%s%s", path, SUFFIX);
if (StringUtils.isBlank(path) || !CONFIGS.containsKey(realKey)) {
return defaultValue;
}
return CONFIGS.get(realKey).getProperty(key, defaultValue);
}
public Map<String, Properties> getConfigs() {
return CONFIGS;
}
public List<String> getFailPathList() {
return CONTEXT.getFailList();
}
@SneakyThrows
public static void main(String[] args) {
ConfigUtils configUtils = newInstance();
Map<String, Properties> configs = configUtils.getConfigs();
String key = configUtils.get("bestsign.config", "key");
String key1 = configUtils.get("", "key");
String key2 = configUtils.get(null, "key");
System.out.println(key);
Thread.sleep(100000);
}
}