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. CentOS 6与CentOS 7的区别收集

    说明: 1.CentOS与Ubuntu没有什么可比性,底层都是Linux,并且Ubuntu在YY广泛的使用,这些并不能说明那个强大哪个不行,只要能解决问题的都是好家伙. 2.市面上教程基本都是基于6, ...

  2. 【转】Kriging插值法

    einyboy 原文LINK Kriging插值法 克里金法是通过一组具有 z 值的分散点生成估计表面的高级地统计过程.与插值工具集中的其他插值方法不同,选择用于生成输出表面的最佳估算方法之前,有效使 ...

  3. 【Java】java注解@Transient的作用, 配合JPA中时间段的查询

    java注解@Transient的作用 @Transient标注的属性,不会被ORM框架映射到数据库中. 用于数据库表字段和java实体属性不一致的时候,标注在属性上使用. 例如时间段的查询 查询 R ...

  4. 关于Android导入开源项目:Error:Unable to load class 'org.gradle.api.publication.maven.internal.DefaultMavenFa

    在导入开源项目时,有可能会要求需要maven插件,并报出一下错误: 对于没有安装maven插件的开发者来说,要去下载一个maven插件也许不困难,但是,有时候可能会像我一样懒,不去下载插件,那么我们可 ...

  5. 设计模式之迭代器模式(PHP实现)

    github地址:https://github.com/ZQCard/design_pattern/** * 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常 ...

  6. 数据库学习--wildfly配置postgreSQL数据源

    前言 实验室最近在做一个物品管理系统的小项目,其中涉及到postgreSQL的使用,刚开始部署到wildfly服务器上时遇到了若干问题,终于在导师的讲解下大体上明白了 ,特此记录分享学习一下. 配置数 ...

  7. ASP.net的身份验证方式有哪些?

    [转] ASP.net的身份验证方式有哪些?分别是什么原理? Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验 ...

  8. 发布android apk,Error running app: No target device found.

    https://developer.android.com/studio/run/device.html\ 一台android设备不识别,android文档还挺全 需要安装usb驱动链接里有

  9. HTML5 Canvas 奔跑的小狗

    效果如上图,共六个图像切换,形成小狗动态奔跑效果.完整代码和图片请从  https://files.cnblogs.com/files/xiandedanteng/runningDog.rar 下载. ...

  10. 《学习bash》笔记--进程处理

    1.进程ID和作业编号  当通过附加&号后执行命令时.shell会响应例如以下: $ ls & [1] 3318 当中[1]是作业号,3318是进程号. 一个后台进程完毕时.shell ...