Hexo拥有强大的插件系统,在博客构建的各个环节都有各种各样的插件来满足不同的需求,对于部署博客这个过程来说也不例外。本博客由于使用了云服务器和独立的域名,不依赖于github,最后使用nginx直接访问生成的html内容,综合考虑后选择采用rsync进行部署。

什么是rsync

rsync是linux系统下的数据镜像备份工具,可通过LAN/WAN快速同步多台主机间的文件。rsync使用所谓的“rsync算法”来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。

CentOS系统默认安装了rsync,使用命令rsync --version来查看版本。此外windows也有相应的移植比如cwRsync和 Cygwin。

rsync的几种传输方式

为了更好地表达命令的格式,一些替代符号的意义如下:

  • [options] :附加的命令选项
  • [user]:用于操作的用户名
  • [host]:远程主机地址或主机名
  • [src]:源文件的路径
  • [dest]:目标文件的路径

主机本地传输

Rsync不仅可以远程同步数据,还可以本地同步数据(类似于cp),但不同于cp或scp的一点是,rsync不像cp/scp一样会覆盖以前的数据(如果数据已经存在),它会先判断已经存在的数据和新数据有什么不同,只有不同时才会把不同的部分覆盖掉。

使用的命令格式如下

1
rsync [options] [src] [dest]

使用rsync认证协议进行传输

网上大多数的rsync使用教程都是在描述使用rsync认证协议进行传输。在传输没有建立之前,这种形式有server和client的概念,server端需要启动一个rsync守护进程(daemon/service),等待接受client发起的连接。一旦连接建立,Client(客户端)/Server(服务器)的这两个角色的差别,就被Sender(发送者)/Receiver(接收者)所取代。

对于移植到windows的rsync软件来说,你可能会发现有server版本和client版本,但是server和client都是可以主动进行”推送“的。这个概念对于初次接触rsync的人员来说比较容易混淆。

使用的命令格式如下

1
2
rsync [options] [user]@[host]::[src] [dest] # 拉取远程文件
rsync [options] [src] [user]@[host]::[dest] # 推送本地文件

使用远程shell进行操作

rsync也支持本地主机借助ssh和rcp等工具使用远程shell和远程主机进行通信。使用这种形式进行传输则不需要任何一方开启守护进程,另外要注意rsync本身的配置文件rsync.conf并不会在这种形式下起作用。

使用的命令格式如下

1
2
3
# 注意区别在于两个':'还是一个:"
rsync [options] [user]@[host]:[src] [dest] # 拉取远程文件
rsync [options] [src] [user]@[host]:[dest] # 推送本地文件

使用远程shell临时启动rsync daemon

通过远程shell也能临时启动一个rsync daemon,这不同于使用rsync协议,它不要求远程主机上事先启动rsync服务,而是临时派生出rsync daemon,它是单用途的一次性daemon,仅用于临时读取daemon的配置文件,当此次rsync同步完成,远程shell启动的rsync daemon进程也会自动消逝。使用时要求options部分必须明确指定”–rsh”选项或其短选项”-e”。

rsync常用参数

rsync的参数非常多,详细请参考相关文档以及本身的help命令。这里仅列出一下比较常用的选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-v, --verbose 详细模式输出
-q, --quiet 精简输出模式
-c, --checksum 打开校验开关,强制对文件传输进行校验
-a, --archive 归档模式,表示以递归方式传输文件,并保持所有文件属性
-r, --recursive:递归到目录中
-z, --compress 对备份的文件在传输时进行压缩处理
--delete 删除DST中SRC没有的文件
--ignore-errors 忽略IO错误
--rsync-path=PATH 指定远程服务器上的rsync命令所在路径信息
--remove-source-files:要求删除源端已经成功传输的文件
-o, --owner:保持owner属性
-g, --group:保持group属性
--chmod=Dg+s,ug+w,Fo-w,+X 指定传输后的文件权限
语法为[D|F][ugo][+-][rwxs],表示为目录(D)和文件(F)增加(+)减少(-)权限

最常用的选项组合是”avz”,即压缩和显示部分信息,并以归档模式传输。另外从windows传输到linux系统时,控制文件权限的--chmod也是非常有用的。

插件hexo-deployer-rsync

这个插件的地址为https://github.com/hexojs/hexo-deployer-rsync,安装还是非常便捷容易的,但是和其他很多Hexo插件一样,使用说明少之又少。对于不熟悉rsync的用户来说,初次使用很容易找不到头绪,到底是使用rsync的什么形式进行传输?如果是rsync协议那么本机作为server还是远程服务器作为server?…这还是在基于了解rsync的传输方式之后才能想到的问题,如果不了解rsync的基础更是一头雾水,总之很恶心。

由于多种使用方式的存在,以及当时网上能查到的相关内容非常之烂,翻遍了各个文章,还尝试在本机启动了一个rsync server,弄得一团糟。

源码

很烦,最后没法了直接看这个插件的源码,没想到源码短到只有50行。这里就直接贴一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
'use strict';

var chalk = require('chalk');
var spawn = require('hexo-util/lib/spawn');
var pathFn = require('path');

module.exports = function(args) {
if (!args.host || !args.user || !args.root) {
var help = '';

help += 'You should configure deployment settings in _config.yml first!\n\n';
help += 'Example:\n';
help += ' deploy:\n';
help += ' type: rsync\n';
help += ' host: <host>\n';
help += ' user: <user>\n';
help += ' root: <root>\n';
help += ' port: [port] # Default is 22\n';
help += ' delete: [true|false] # Default is true\n';
help += ' args: <rsync args>\n';
help += ' verbose: [true|false] # Default is true\n';
help += ' ignore_errors: [true|false] # Default is false\n\n';
help += 'For more help, you can check the docs: ' + chalk.underline('http://hexo.io/docs/deployment.html');

console.log(help);
return;
}

if (!args.hasOwnProperty('delete')) args.delete = true;
if (!args.hasOwnProperty('verbose')) args.verbose = true;
if (!args.hasOwnProperty('ignore_errors')) args.ignore_errors = false;

var params = [
'-az',
process.platform === 'win32' ? pathFn.basename(this.public_dir) + '/' : this.public_dir,
args.user + '@' + args.host + ':' + args.root
];

if (args.port && args.port > 0 && args.port < 65536) {
params.splice(params.length - 2, 0, '-e');
params.splice(params.length - 2, 0, 'ssh -p ' + args.port);
}

if (args.verbose) params.unshift('-v');
if (args.ignore_errors) params.unshift('--ignore-errors');
if (args.delete) params.unshift('--delete');
if (args.args) params.unshift(args.args);

return spawn('rsync', params, {verbose: true});
};

可以看到最终这个插件就只是调用了一下rsync命令,剩下的都是命令内容的拼接。

为了更加清晰得说明问题,把这个插件的配置参数也贴出来

1
2
3
4
5
6
7
8
host: Address of remote host
user: Username
root: Root directory of remote host
port: Port
delete: Delete old files on remote host
args: Rsync arguments
verbose: Display verbose messages
ignore_errors: Ignore errors

源码简析

按照代码顺序解释如下:

  1. host、user、root这三个参数必填,否则将会打印You should configure deployment settings in _config.yml first! ...提示你输入必填参数。
  2. delete默认为true,verbose默认为true,ignore_errors默认为false
  3. 创建参数集合,固定使用-az这两个参数;根据平台获取博客public目录的路径;拼装远程目录路径
  4. 参数集合判断加入端口、ssh
  5. 判断增加verbose、ignore_errors、delete选项对应参数
  6. 使用组装好的参数调用rsync命令

很明显就是要去使用ssh远程shell进行操作,因此只要本机和服务器都装上rsync即可,不需要启动daemon进程。

使用注意

对于windows系统来说有几个需要注意的点。

推荐使用cwRsync,貌似是要收费,但是你懂的。注意要安装client版本,使用server版本可能会出现报错,原因与ssh命令有关,可以发现client版本bin目录下自带了一个ssh.exe。因为插件直接执行rsync命令,所以安装完成后还需要把安装目录添加到系统环境变量中,这样才能直接使用。

此外由于软件本身是linux移植过来的,在填写路径的时候无法识别类似d:\\dir\\public的win风格路径格式,cwRsync使用/cygdrive/d/dir/public的格式来读取win路径下的内容。