diamond源码阅读-目录监控
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源码阅读-目录监控的更多相关文章
- diamond源码阅读-diamond-client
读取数据 DiamondManager manager = new DefaultDiamondManager("DEFAULT_GROUP", "zml", ...
- diamond源码阅读-获取服务器列表
serverAddressProcessor public synchronized void start() { if (isRun) { return; } isRun = true; initH ...
- diamond源码阅读-diamond-server
diamond-server 1 增加一条数据 /diamond-server/admin.do?method=postConfig 1.1 调用 this.configService.addConf ...
- diamond源码阅读-循环探测配置信息是否变化rotateCheckConfigInfo
rotateCheckConfigInfo 这是一个定时任务,循环调用 /** * 循环探测配置信息是否变化,如果变化,则再次向DiamondServer请求获取对应的配置信息 */ private ...
- Pytorch版本yolov3源码阅读
目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...
- 应用监控CAT之cat-client源码阅读(一)
CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...
- 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介
前言 古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...
- CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程
最开始使用CI框架的时候,就打算写一个CI源码阅读的笔记系列,可惜虎头蛇尾,一直没有行动.最近项目少,总算是有了一些时间去写一些东西.于是准备将之前的一些笔记和经验记录下来,一方面权作备忘,另一方面时 ...
- Kubernetes 学习(九)Kubernetes 源码阅读之正式篇------核心组件之 Scheduler
0. 前言 继续上一篇博客阅读 Kubernetes 源码,参照<k8s 源码阅读>首先学习 Kubernetes 的一些核心组件,首先是 kube-scheduler 本文严重参考原文: ...
随机推荐
- shell中的cut命令
转:http://blog.sina.com.cn/s/blog_5e77c61f0100hqky.html cut是以每一行为一个处理对象的,这种机制和sed是一样的.(关于sed的入门文章将在近期 ...
- ArcMAP中如何将16位保存的卫星底图,转变为8位表示
首先说明,这种转换将会去除影像的投影像素的定义,并在转换后变为黑色的部分.16位的存储,一方面也是定义透明非数据像素点表示的方便.但是这种定义直接加大了影像的大小,不便于与CAD等软件进行交换数据.
- iOS教程:如何使用Core Data – 预加载和引入数据
这是接着上一次<iOS教程:Core Data数据持久性存储基础教程>的后续教程,程序也会使用上一次制作完成的. 再上一个教程中,我们只做了一个数据模型,之后我们使用这个数据模型中的数据创 ...
- 发掘StateListAnimator的全部潜能
原文地址:https://blog.stylingandroid.com/statelistanimator/ 原文作者:Leave a reply 译文出自:安卓巴士 译者: MrlLee 校对者: ...
- JavaScript入门:006—JS函数的定义
JS函数的声明. 声明函数的格式例如以下: function 函数名(參数列表){ //函数语句: return 返回值; } 来看详细的函数声明.1.普通函数 <script type=&qu ...
- mysql 数据库设计(转)
本规范适用于mysql 5.1或以上版本使用 数据库范式 第一范式(1NF)确保每列保持原子性 第一范式(1NF):数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项. ...
- Gacutil.exe(全局程序集缓存工具)
全局程序集缓存 .NET Framework (current version) 其他版本 安装有公共语言运行时的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存.全局程序集缓存中存储了专门 ...
- foreach 加& 什么意思?
foreach 加&遍历的同时改变原数组即修改数据或者增加数据 foreach 加& 什么意思? 注意:如果我要改变数组某一个值 直接遍历的话原数组是不会变的 下面提供两种方法 1.我 ...
- PIVOT 和 UPIVOT 的使用(行转列)
PIVOT 通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列值执行聚合.UNPIVOT 与 PIVOT 执行相反的操作,将表值表达式的列转换为列 ...
- myBatis插入oracle获取主键
<insert id="insert" parameterType="com.inspur.biz.entry.SpLackApply"> < ...