在监控系统中要做一个监控文件的功能:实时监控日志文件,若发生变化则使用websocket通知前端(web页面),并返回新增的日志记录。
出事代码是这样的:
/**
* 文件监控处理
*
* [@param](/user/param) socket
*/
function monitor_handler(socket){
var files_to_monitor =config.files_to_monitor;
for(var key in files_to_monitor){
console.log('开始:'+key);
fs.watchFile(files_to_monitor[key].file_path , function(curr, prev){
console.log('触发:'+key + ',处理文件'+files_to_monitor[key].file_path);
if (curr.mtime.getTime() !== prev.mtime.getTime()) {
notify_client(key, files_to_monitor[key].file_path, curr, socket);
}
});
}
}
乍看上去,代码很简单,config.files_to_monitor的结构是这样的:
files_to_monitor : {
'data' : {
label : '日志文件1',
file_path : '/home/tmp2/data',
},
'data2' : {
label : '日志文件2',
file_path : '/home/tmp2/data2',
},
'data3' : {
label : '日志文件3',
file_path : '/home/tmp2/data3',
},
}
也就是需要监控配置的每个文件,然后在它发生变化后通过指定的key来emit前端websocket事件。所以fs.watchFile的回调会调用这个key,如果说到这里你能明白的话,那就不用往下看了。
按照上述代码,测试时,我分别改变三个文件,然后观察websocket的debug信息,结果是这样的:
root[@sumory-laptop](/user/sumory-laptop):/home/tmp2# echo data '模拟数据' > data
root[@sumory-laptop](/user/sumory-laptop):/home/tmp2# echo data '模拟数据' > data2
root[@sumory-laptop](/user/sumory-laptop):/home/tmp2# echo data '模拟数据' > data3
触发:data3,处理文件/home/tmp2/data3
debug - websocket writing 5:::{"name":"data3","args":[{"updatetime":"2012-3-25 19:12:04","content":"data fdsffsfdsfsfdfsfdsfsdfdsfsfds\n"}]}
触发:data3,处理文件/home/tmp2/data3
debug - websocket writing 5:::{"name":"data3","args":[{"updatetime":"2012-3-25 19:12:07","content":"data fdsffsfdsfsfdfsfdsfsdfdsfsfds\n"}]}
触发:data3,处理文件/home/tmp2/data3
debug - websocket writing 5:::{"name":"data3","args":[{"updatetime":"2012-3-25 19:12:10","content":"data 模拟数据\n"}]}
好吧,2B了,永远只会触发data3:
- 起初还以为代码有问题,挨个字母检查,没问题啊;
- 仔细查看nodejs的fs部分文档,函数也没有用错;
- 增加一配置项,发现只会调用最后一项配置,也就是for循环的最后一项。
恍然大悟,fs.watchFile监控并触发事件不是通过回调的嘛,回调在声明时不会调用,只有文件发生变化是才会调用,这是的key不是已经循环到最后了,当然也就会永远只触发最后一个(好吧,就这么一个坑,我就粪不顾身地随便跳进去了…)。
改写后正确的结果:
/**
* 文件监控处理
*
* [@param](/user/param) socket
*/
function monitor_handler(socket){
var files_to_monitor =config.files_to_monitor;
for(var key in files_to_monitor){
console.log('开始:'+key);
watch_file(key, files_to_monitor[key].file_path, socket);
}
}
function watch_file(key, full_file_path, socket){
fs.watchFile(full_file_path, function(curr, prev){
console.log('触发:'+key + ',处理文件'+full_file_path);
if (curr.mtime.getTime() !== prev.mtime.getTime()) {
notify_client(key, full_file_path, curr, socket);
}
});
}
恶心得蛋都碎了…以后还是留点神吧…
for(var key in files_to_monitor){ console.log('开始:’+key); fs.watchFile(files_to_monitor[key].file_path , foobar.bind(null, key)); }
function foobar(key, curr, prev){
console.log('触发:'+key + ',处理文件'+files_to_monitor[key].file_path);
if (curr.mtime.getTime() !== prev.mtime.getTime()) {
notify_client(key, files_to_monitor[key].file_path, curr, socket);
}
}
改成这样好点吧,不然每次watch_file运行都会创建一个函数对象
@sumory 看這個 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind 不過.bind應該也會生成一個func… 所以內存消耗貌似一樣,我傻逼了
参看下这个,让它立即执行把参数传进去
var numbers= [];
for (var i = 0; i < 10; i++) {
numbers[i] = (function(tmp) {
return function() {
return tmp;
}
}(i));
}
for (var j = 0; j < 10; j++) {
print(numbers[j]()); //返还0,1,2.....9
}
/**
- 文件监控处理
- @param socket */ function monitor_handler(socket){ var files_to_monitor =config.files_to_monitor; for(var key in files_to_monitor){ (function(){ console.log('开始:’+key); fs.watchFile(files_to_monitor[key].file_path , function(curr, prev){ console.log('触发:’+key + ',处理文件’+files_to_monitor[key].file_path); if (curr.mtime.getTime() !== prev.mtime.getTime()) { notify_client(key, files_to_monitor[key].file_path, curr, socket); } }); })() } } 这样就行了