Windows下inotifywait和rsync全自动同步

从新做的第二版,吊炸天!结尾增加专用版打包demo。

现在要做一件非常重要的事情,把windows下的文件完全映射到远程机器(远程机器有rsync即可),文件大小和文件数量不能影响映射效率,网络正常和话性能必须要好。

注意:这里讨论的是非cygwin环境,cygwin可以解决很多问题,但不是windows用户的标配。

首先要监测文件变化,然后实时捕获变化然后映射变化的文件。Linux内核支持inotifywait,幸好windows下也有开发,感谢github。没有比rsync更吊的东西,文件数和大小丝毫不会影响效率。windows也支持管道符。基本条件都具备可以开搞了。

这个过程中有很多坑,我就直接出结论了,inotifywait+rsync+php+bat实现,多NB懂的人自然懂,这是一个通用框架,你可以自己定义各种任务。

特点:

  1. 我这测试代码同步到服务器10毫秒以内,相当于直接用IDE编辑服务器文件(RSE确实也可以,和这个能是一个级别?)
  2. 不用怀疑安全问题,rysnc的功能数量90%的人只用到了10%。
  3. 整套程序是否有性能问题?不用怀疑了,全部是触发型,流数据处理都是阻塞的!
  4. 文件创建删除,移动,改名都是一系列事件,本程序对于一些列事件只同步一次。
  5. rsync同步整个目录很省事,性能也在合理范围之内(4000个文件,40MB大小,从顶层目录同步也在在毫秒级),但是经过研究搞定了只同步修改的那个文件!
  6. 配置文件较之前版本大幅优化,已经不能更优美了,配置中rsync项直接拼接到命令行,所以有额外参数加到这里即可,比如--port=873。
  7. 可以在inotifywait层设置忽略,可以在php层设置忽略(配置的ignore项),可以在rsync层设置忽略。
  8. 文件修改和重定向(>操作)可能是修改了文件的inode导致两次MODIFY事件,追加(>>)是一次MODIFY事件,本程序用$maxInterval变量完美解决,在$maxInterval之内同一个文件只会同步一次,(你可能会说也可以用pthreads搞定,那你去搞吧,别忘了java和php多线程编程我是专家级别)。
  9. windows下搞这个之前肯定没人听说过吧(我搜遍google没找到类似的东西)。

不足:

  1. $maxInterval以秒为单位,如果以毫秒为单位应该可以用GNU的stat解决,暂时没时间搞了。
  2. 如果一次同步操作时间过长后面的以队列形式按序操作,这个应该是合理的事情,除非多线程搞。

有什么问题可以自己测试,比如为什么不能用bat接收管道数据处理?为什么不用xargs之类的工具(windows有GNU整套工具)?这都是坑的范围我就不再多说什么。也不要问为什么没有删除操作。

首先安装inotifywait,地址:https://github.com/thekid/inotify-win,这里有1.6编译好的exe(旧版本有很多问题,不建议使用)
windows版rsync下载地址:https://www.itefix.no/i2/cwrsync

注意:rsync服务端需要给客户端ip加host,否则服务端会反解这个IP,这个耗时非常长,如果没有host导致结果就是连接相当慢。

最最重要的是如下两个脚本,这两个脚本是相当NB的,没有一定经验是搞不出来的。
rsyncauto.bat

@echo off
inotifywait -mrq --format "%%e %%w\%%f %%T" --excludei "(\.svn(\\.+)?|\.settings(\\.+)?|\.project|.buildpath)" "C:\ares\work\java\hbase-joyport\src\joyport" "C:\ares\work\php"|php "C:\Ares\Program\cwRsync_5.3.0_Free\rsync.php"

管道接收只能自己写,xargs和类似的工具都不能用,因为你要接收的是实时监控的非连续流数据。
rsync.php

<?php
error_reporting ( E_ALL );
ini_set ( 'max_execution_time', 0 );
$f = fopen ( 'php://stdin', 'r' );
stream_set_blocking ( $f, 1 );
stream_set_timeout ( $f, 86400 * 365 * 100 );
/**
 * key is local directory
 * cmd window with 150 should be good
 */
$map = array (
		'C:\ares\work\php\test' => array (
				'rsync' => 'test@xxx.xxx.xxx.xxx::test'
		),
		'C:\ares\work\php\test1' => array (
				'rsync' => 'test1@xxx.xxx.xxx.xxx::test1',
				'ignore' => array (
						'log/*',
						'cache/*'
				)
		)
);
// same file will not be synced in this interval.Unit is second
$maxInterval = 1;
$lastTime = 0;
$lastFiles = array ();
$i = 0;
while ( true ) {
	$line = trim ( fgets ( $f ) );
	$line = explode ( " ", $line, 3 );
	if ('ISDIR' == substr ( $line [0], - 5 )) {
		$line [1] .= '\\';
	}
	$time = strtotime ( $line [2] );
	if ($time - $lastTime >= $maxInterval) {
		$lastFiles = array ();
		$lastTime = $time;
	}
	foreach ( $map as $k => $v ) {
		if (0 === strpos ( $line [1], $k )) {
			if (in_array ( $line [1], $lastFiles )) {
				continue;
			}
			$lastFiles [] = $line [1];
			$file = str_replace ( '\\', '/', substr ( $line [1], strlen ( $k ) + 1 ) );
			if (! empty ( $v ['ignore'] )) {
				foreach ( $v ['ignore'] as $v1 ) {
					if (fnmatch ( $v1, $file )) {
						continue 2;
					}
				}
			}
			$cmd = 'set CYGWIN=nodosfilewarning && cd /d ' . $k . ' && ';
			$cmd .= 'rsync -avz -R -d --exclude=".svn*" --exclude=".settings*" --exclude=".buildpath" --exclude=".project" ';
			$cmd .= '--password-file "' . __DIR__ . '/rsync.pwd" ' . $file . ' ' . $v ['rsync'];
			$suffix = '';
			$do = false;
			if (0 === strpos ( $line [0], 'MODIFY' )) {
				$do = true;
				$suffix = " => $v[rsync]";
			}
			$i ++;
			echo "$i $line[0] $line[1]$suffix";
			if ($do) {
				shell_exec ( $cmd );
			}
			echo " ok\n";
		}
	}
}
fclose ( $f );

执行效果:

捕获

第一列是自增值,第二列是操作,第三列是本地文件,"=>"符号表示进行了同步操作,符号后面是rsync的远程服务器,"ok"在本次操作完成的时候才会输出。

在使用过程中修改了n次,最终使用版本打包成一个完整的示例,需要根据自己需求自行修改。

rsyncauto

Windows下inotifywait和rsync全自动同步》上有5条评论

  1. 这个监控程序是有BUG的。 因为Windows系统下面的文件可以包含空格, 另外,目录也可以包含空格。 所以bug就出在 $line = explode ( ” “, $line, 3 ); 可以修改成这样: inotifywait -mrq –format “%%e | %%w\%%f | %%T” , 然后php的部分改成 $line = explode ( ” | “, $line, 3 );

  2. $file第一次赋值的时候有点问题,后面需要添加:
    if(empty($file)){
    $file=’.’;
    }
    if (‘ISDIR’ == substr ( $line [0], – 5 )) {
    $file .= ‘/’;
    }

  3. wordpress编辑需要处理转义符太麻烦,直接写评论里。最后截图操作包括新建文件改名,删除,新建文件夹改名删除,移动,包括移动到忽略的子目录(配置的ignore),编辑文件,从ignore创建文件并移出来等操作。

发表评论

电子邮件地址不会被公开。

*