PathNode(Path)
StandardWatchEventKind(WatchEvent)
Watchable(WatchKey WatchService WatchEvent)
WatchKey(PathNode WatchEvent WatchService)
WatchService(WatchKey Path)
WatchEvent
FileSystem(WatchService)
Path>Watchable

WatchEvent
StandardWatchEventKind(ENTRY_CREATE; ENTRY_DELETE; ENTRY_MODIFY; OVERFLOW)

WatchService  WatchKey

WatchService
1 register 注册监视目录 ,生成watchedKey(遍历子目录,每个文件都注册一个事件),添加到watchedKeys

1.1

    public WatchKey register(Path root, boolean fireCreatedEventOnIndex, WatchEvent.Kind<?>... events) {
if (events == null || events.length == 0)
throw new UnsupportedOperationException("null events");
if (this.service.isShutdown())
throw new IllegalStateException("服务已经关闭");
if (!root.exists())
throw new IllegalArgumentException("监视的目录不存在");
WatchKey key = new WatchKey(root, this, fireCreatedEventOnIndex, events);
resetKey(key);
return key;
}

1.1.1 WatchKey

   public WatchKey(final Path path, final WatchService watcher, boolean fireCreatedEventOnIndex,
WatchEvent.Kind<?>... events) {
valid = true;
this.watcher = watcher;
// 建立内存索引
this.root = new PathNode(path, true);
if (events != null) {
for (WatchEvent.Kind<?> event : events) {
filterSet.add(event);
}
}
LinkedList<WatchEvent<?>> changedEvents = new LinkedList<WatchEvent<?>>();
index(this.root, fireCreatedEventOnIndex, changedEvents);
this.changedEvents = changedEvents;
}

1.1.1.1 WatchKey  为每个目录下子文件建立一个ENTRY_CREATE 监听事件

  private void index(PathNode node, boolean fireCreatedEventOnIndex, LinkedList<WatchEvent<?>> changedEvents) {
File file = node.getPath().getFile();
if (!file.isDirectory()) {
return;
}
File[] subFiles = file.listFiles();
if (subFiles != null) {
for (File subFile : subFiles) {
PathNode subNode = new PathNode(new Path(subFile), false);
if (fireCreatedEventOnIndex) {
changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_CREATE, 1, subNode.getPath()));
}
node.addChild(subNode);
if (subNode.getPath().isDirectory()) {
index(subNode, fireCreatedEventOnIndex, changedEvents);
}
}
}
}

2  启动的一个线程,定时扫描watchedKeys,调用watchedKey.check(),
  如果有变化,watchedKeys 删掉watchedKey ,changedKeys add watchedKey

  

    public WatchService(long checkInterval) {
service = Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(new CheckThread(), checkInterval, checkInterval, TimeUnit.MILLISECONDS);
}
    private final class CheckThread implements Runnable {
public void run() {
check();
}
} /**
* 主动check
*/
public void check() {
synchronized (this) {
Iterator<WatchKey> it = watchedKeys.iterator();
while (it.hasNext()) {
WatchKey key = it.next();
try {
if (key.check()) {
changedKeys.add(key);
it.remove();
}
}
catch (Throwable t) {
log.error("检测WatchKey异常,key=" + key, t);
}
}
}
}

2.1 key.check()

  

    boolean check() {
if (this.changedEvents != null && this.changedEvents.size() > 0)
return true;
if (!this.valid)
return false;
List<WatchEvent<?>> list = new LinkedList<WatchEvent<?>>();
if (check(root, list)) {
this.changedEvents = list;
return true;
}
else {
return false;
}
}

2.1.1

  

    private boolean check(PathNode node, List<WatchEvent<?>> changedEvents) {
Path nodePath = node.getPath();
File nodeNewFile = new File(nodePath.getAbsolutePath());
if (nodePath != null) {
if (node.isRoot()) {
if (!nodeNewFile.exists())
return fireOnRootDeleted(changedEvents, nodeNewFile);//触发删除事件,添加到当前watchedKey的changedEvents
else {
return checkNodeChildren(node, changedEvents, nodeNewFile);
}
}
else {
return checkNodeChildren(node, changedEvents, nodeNewFile);
}
}
else
throw new IllegalStateException("PathNode没有path");
}

2.1.1.1 监听新增事件 修改事件,若有讲事件添加到当前watchedKey的changedEvents

  private boolean checkNodeChildren(PathNode node, List<WatchEvent<?>> changedEvents, File nodeNewFile) {
boolean changed = false;
Iterator<PathNode> it = node.getChildren().iterator();
// 用于判断是否有新增文件或者目录的现有名称集合
Set<String> childNameSet = new HashSet<String>();
while (it.hasNext()) {
PathNode child = it.next();
Path childPath = child.getPath();
childNameSet.add(childPath.getName());
File childNewFile = new File(childPath.getAbsolutePath());
// 1、判断文件是否还存在
if (!childNewFile.exists() && filterSet.contains(StandardWatchEventKind.ENTRY_DELETE)) {
changed = true;
changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_DELETE, 1, childPath));
it.remove();// 移除节点
}
// 2、如果是文件,判断是否被修改
if (childPath.isFile()) {
if (checkFile(changedEvents, child, childNewFile) && !changed) {
changed = true;
} }
// 3、递归检测目录
if (childPath.isDirectory()) {
if (check(child, changedEvents) && !changed) {
changed = true;
}
}
} // 查看是否有新增文件
File[] newChildFiles = nodeNewFile.listFiles();
if(newChildFiles!=null)
for (File newChildFile : newChildFiles) {
if (!childNameSet.contains(newChildFile.getName())
&& filterSet.contains(StandardWatchEventKind.ENTRY_CREATE)) {
changed = true;
Path newChildPath = new Path(newChildFile);
changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_CREATE, 1, newChildPath));
PathNode newSubNode = new PathNode(newChildPath, false);
node.addChild(newSubNode);// 新增子节点
// 如果是目录,递归调用
if (newChildFile.isDirectory()) {
checkNodeChildren(newSubNode, changedEvents, newChildFile);
}
}
}
return changed;
}

3 如何使用

 private void startCheckLocalDir(final String filePath) {
final WatchService watcher = FileSystem.getDefault().newWatchService(); Path path = new Path(new File(filePath));
// 注册事件
watcher.register(path, true, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_DELETE,
StandardWatchEventKind.ENTRY_MODIFY);
// 第一次运行,主动check
checkAtFirst(watcher);
singleExecutor.execute(new Runnable() {
public void run() {
log.debug(">>>>>>已经开始监控目录<<<<<<");
// 无限循环等待事件
while (isRun) {
// 凭证
WatchKey key;
try {
key = watcher.take();
}
catch (InterruptedException x) {
continue;
}
// reset,如果无效,跳出循环,无效可能是监听的目录被删除
if (!processEvents(key)) {
log.error("reset unvalid,监控服务失效");
break;
}
}
log.debug(">>>>>>退出监控目录<<<<<<");
watcher.close(); } });
}

3.1 处理触发的事件,并继续监听

 /**
* 处理触发的事件
*
* @param key
* @return
*/
@SuppressWarnings({ "unchecked" })
private boolean processEvents(WatchKey key) {
/**
* 获取事件集合
*/
for (WatchEvent<?> event : key.pollEvents()) {
// 事件的类型
// WatchEvent.Kind<?> kind = event.kind(); // 通过context方法得到发生事件的path
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path eventPath = ev.context(); String realPath = eventPath.getAbsolutePath();
if (ev.kind() == StandardWatchEventKind.ENTRY_CREATE || ev.kind() == StandardWatchEventKind.ENTRY_MODIFY) { String grandpaDir = null;
try {
grandpaDir = FileUtils.getGrandpaDir(realPath);
}
catch (Exception e1) { }
if (!Constants.BASE_DIR.equals(grandpaDir)) {
log.error("无效的文件进入监控目录: " + realPath);
continue;
}
existFiles.put(realPath, System.currentTimeMillis());
if (log.isInfoEnabled()) {
log.info(realPath + "文件被添加或更新");
}
}
else if (ev.kind() == StandardWatchEventKind.ENTRY_DELETE) {
String grandpaDir = null;
try {
grandpaDir = FileUtils.getGrandpaDir(realPath);
}
catch (Exception e1) { }
if (Constants.BASE_DIR.equals(grandpaDir)) {
// 删除的是文件
existFiles.remove(realPath);
if (log.isInfoEnabled()) {
log.info(realPath + "文件被被删除");
}
}
else {
// 删除的是目录
Set<String> keySet = new HashSet<String>(existFiles.keySet());
for (String filePath : keySet) {
if (filePath.startsWith(realPath)) {
existFiles.remove(filePath);
if (log.isInfoEnabled()) {
log.info(filePath + "文件被删除");
}
}
} }
}
}
return key.reset();//WatchService 继续监听
}

diamond源码阅读-目录监控的更多相关文章

  1. diamond源码阅读-diamond-client

    读取数据 DiamondManager manager = new DefaultDiamondManager("DEFAULT_GROUP", "zml", ...

  2. diamond源码阅读-获取服务器列表

    serverAddressProcessor public synchronized void start() { if (isRun) { return; } isRun = true; initH ...

  3. diamond源码阅读-diamond-server

    diamond-server 1 增加一条数据 /diamond-server/admin.do?method=postConfig 1.1 调用 this.configService.addConf ...

  4. diamond源码阅读-循环探测配置信息是否变化rotateCheckConfigInfo

    rotateCheckConfigInfo 这是一个定时任务,循环调用 /** * 循环探测配置信息是否变化,如果变化,则再次向DiamondServer请求获取对应的配置信息 */ private ...

  5. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

  6. 应用监控CAT之cat-client源码阅读(一)

    CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...

  7. 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介

    前言        古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...

  8. CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程

    最开始使用CI框架的时候,就打算写一个CI源码阅读的笔记系列,可惜虎头蛇尾,一直没有行动.最近项目少,总算是有了一些时间去写一些东西.于是准备将之前的一些笔记和经验记录下来,一方面权作备忘,另一方面时 ...

  9. Kubernetes 学习(九)Kubernetes 源码阅读之正式篇------核心组件之 Scheduler

    0. 前言 继续上一篇博客阅读 Kubernetes 源码,参照<k8s 源码阅读>首先学习 Kubernetes 的一些核心组件,首先是 kube-scheduler 本文严重参考原文: ...

随机推荐

  1. Flex this

    为了便于对比和叙述,我们先上一段最简单的js+html代码:<input type="button" value="test" id="htmB ...

  2. 细说JavaScript对象(1):对象的使用和属性

    JavaScript 中的一切都可以视为对象,除了两个特例:null 和 undefined. false.toString(); // 'false' [1, 2, 3].toString(); / ...

  3. [置顶] kubernetes资源类型--Service

    为了适应快速的业务需求,微服务架构已经逐渐成为主流,微服务架构的应用需要有非常好的服务编排支持.K8S中的核心要素Service便提供了一套简化的服务代理和发现机制,天然适应微服务架构. 实现原理 S ...

  4. ubuntu配置无密码登录

    1 本地生成ssh公钥和私钥, 2将公钥拷贝到ubuntu上的.ssh/authorized_keys 中

  5. python2 to3

    http://blog.csdn.net/zhzh213/article/details/53842790 有个内部工具叫做2to3.py位置在Python3/tool/script文件夹. 首先CD ...

  6. js获取页面传来参数的方法

    function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...

  7. Solr6.6.0 用 SimplePostTool索引文件

    一.背景介绍 Solr启动并运行之后,并不包含任何数据,在solr的安装目录下的bin目录中,有一个post工具,我们可以使用这个工具往solr上传数据,这个工具必须在命令行中执行,post工具是一个 ...

  8. Java 实现模板方法(TemplateMethod)模式

    类图 /** * 业务流程模板.提供基本框架 * @author stone * */ public abstract class BaseTemplate { public abstract voi ...

  9. POJ1258 Agri-Net MST最小生成树题解

    搭建一个最小代价的网络,最原始的最小生成树的应用. 这里使用Union find和Kruskal算法求解. 注意: 1 给出的数据是原始的矩阵图,可是须要转化为边表示的图,方便运用Kruskal,由于 ...

  10. Time.fixedDeltaTime 固定增量时间

    static var fixedDeltaTime : float Description描述 The interval in seconds at which physics and other f ...