臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

一、一句话理解

rsync 就像“智能搬运”:它会比较本地和服务器的文件,只上传新增或内容真正修改过的部分,比宝塔逐个上传快得多。

二、理论:它是什么

当前通过宝塔上传时,浏览器需要逐个处理 public 里的文件。文件越多,重复请求越多,即使总容量不大也会很慢。

rsync 会对比两个位置:

  • 本地:Quartz 生成的 public 文件夹
  • 服务器:/www/AfykeBlog/afyke-blog
  • 传输通道:SSH

本地和服务器之间需要先建立网络连接,rsync 才能把文件传过去。本教程使用 SSH 作为这条安全的连接通道。

2.1 SSH 是什么

SSH 的全称是 Secure Shell(安全外壳协议),是一种通过网络安全地登录和操作远程计算机的通信协议。

可以把它理解为:坐在自己的 Mac 前,通过一条加密线路,使用服务器的终端。

SSH 在连接过程中主要负责:

  • 找到服务器:根据 IP 地址和 SSH 端口发起连接。
  • 验证登录身份:使用密码或 SSH 密钥确认你有权登录。
  • 加密通信内容:防止登录凭证、命令和传输文件在网络中被直接窃取。
  • 提供远程操作通道:可以执行服务器命令,也可以为 scp、sftp 和 rsync 传输文件。

例如:

ssh -p 22 root@47.99.178.186

这条命令表示:通过 SSH 的 22 端口,使用 root 账号登录 47.99.178.186 这台服务器。

在后面的 rsync 命令中,两者的分工是:

  • rsync 负责比较文件,判断哪些内容需要同步。
  • SSH 负责验证身份,并提供加密的文件传输通道。

一句话记忆:SSH 是安全连接和操作服务器的协议。

如果服务器目标目录是空的,第一次使用时需要上传全部文件。如果服务器已有通过宝塔上传的同内容文件,使用 -c 比较内容后可以跳过它们。之后修改一篇文章,通常只会传输相关页面和资源。

三、理论:它是怎么工作的

部署过程是:

Markdown 笔记
    ↓
Quartz 构建
    ↓
生成 public 文件夹
    ↓
rsync 比较本地与服务器文件内容
    ↓
只上传发生变化的文件

rsync 不负责生成博客,它只负责同步文件。因此每次部署前,仍需先运行 Quartz 构建命令。

四、实践:部署前准备

本教程暂时按以下信息操作:

  • 服务器地址:47.99.178.186
  • SSH 用户:root
  • SSH 端口:22
  • 服务器网站目录:/www/AfykeBlog/afyke-blog
  • 本地生成目录:public

如果你的 SSH 端口不是 22,需要把后面命令里的 22 换成实际端口。

4.1 打开本地博客目录

构建和同步命令必须在 Mac 本地终端中执行,不是在 SSH 登录后的服务器终端中执行。

本机 Quartz 项目的实际路径是:

/Users/chenweili/Documents/quartz

打开 Mac 的“终端”,输入:

cd /Users/chenweili/Documents/quartz

接着查看当前目录:

pwd

再确认这里存在 public:

ls public

能够看到博客文件,就说明位置正确。

IMPORTANT

/www/AfykeBlog/afyke-blog 是服务器上的接收目录,不是 Mac 上的 Quartz 项目。服务器的 /root/Public 也与 Quartz 生成的本地 public 无关。

4.2 构建最新博客

运行:

npx quartz build

这会根据当前 Markdown 笔记重新生成 public。

五、第一次测试连接

运行:

ssh -p 22 root@47.99.178.186

第一次连接可能出现确认提示,输入:

yes

然后输入服务器的 SSH 密码。

输入密码时,终端不会显示圆点或星号,这是正常现象。直接输入完成后按回车即可。

连接成功后,运行:

exit

退出服务器,回到 Mac。

六、最小部署例子

6.1 先预演,不实际上传

运行:

rsync --8-bit-output -azcni --no-times -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

这里的 -n 表示“只预览”,不会真的修改服务器。-c 会比较文件实际内容,-i 只列出发生变化的项目,--no-times 忽略文件和目录的修改时间差异,--8-bit-output 用于在 Mac 终端中正常显示中文文件名。

IMPORTANT

预演命令中含有 n,只会显示差异,不会上传文件。如果只重复运行预演,服务器始终不会更新,同一批变更每次都会继续显示。

确认目标目录正确后,再进行正式上传。

6.2 正式同步

运行:

rsync --8-bit-output -azci --no-times --progress -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

输入 SSH 密码后开始同步。

以后每次更新博客,只需先运行:

npx quartz build

然后运行:

rsync --8-bit-output -azci --no-times --progress -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

七、让服务器与本地完全一致

普通同步不会删除服务器上的旧文件。例如你在本地删除了一篇文章,服务器可能还会保留原来的网页。

确认 /www/AfykeBlog/afyke-blog 只存放 Quartz 生成文件后,可以增加 --delete:

rsync --8-bit-output -azcni --no-times --delete -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

先通过 -n 查看准备删除的文件。确认无误后,去掉 n:

rsync --8-bit-output -azci --no-times --delete --progress -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

WARNING

--delete 会删除服务器目录中、本地 public 不存在的内容。如果网站目录里还放着手工上传的文件,不要使用这个参数。

八、命令中的参数是什么意思

  • -a:递归同步目录,并尽量保留文件信息
  • -v:显示详细处理过程;当前推荐命令不使用,避免输出过多目录
  • -z:传输时压缩内容
  • -c:计算校验和,按文件实际内容判断是否变化
  • -n:只预演,不实际修改
  • -i:按变化类型列出需要更新或删除的项目
  • --no-times:不同步文件和目录的修改时间,避免把“内容相同、只有时间不同”的文件列为变更
  • --8-bit-output:让 Mac 自带的旧版 rsync 在终端中直接显示中文文件名
  • --progress:显示传输进度
  • --delete:删除服务器上多余的旧文件
  • -e "ssh -p 22":使用 SSH 的 22 端口连接

public/ 最后的 / 很重要。它表示同步 public 里面的内容,而不是在服务器中再建立一层 public 文件夹。

-azcni 是简写组合,等价于:

-a -z -c -n -i

预演时使用 -azcni;正式同步时去掉代表预演的 n,改为 -azci。

精简预演输出中可能看到:

<fcsT.... index.xml
*deleting   旧文件.html
  • <f:该文件需要从 Mac 上传到服务器。
  • c:文件内容校验和不同。
  • s:文件大小不同。
  • *deleting:使用 --delete 时,该服务器文件准备被删除。
  • 没有列出任何项目:本地与服务器已经一致。

九、常见问题

9.1 Connection refused

通常表示 SSH 端口不正确、SSH 服务未开启,或者防火墙没有放行对应端口。

9.2 Permission denied

这表示服务器和 SSH 端口已经连通,但身份验证失败。先确认登录命令中使用的是正确账号:

ssh -p 22 root@47.99.178.186

这里需要输入的是服务器 root 账号的登录密码,不是 Mac 密码、宝塔面板密码或数据库密码。输入密码时终端不显示字符是正常现象。

如果账号和密码确认无误,再检查服务器是否禁止 root 通过 SSH 密码登录。

9.3 ls public 提示没有这个目录

先看终端提示符。如果类似:

[root@服务器名]#

说明当前仍在服务器中。执行:

exit

回到 Mac 后,再进入本地 Quartz 项目并检查:

cd /Users/chenweili/Documents/quartz
ls public

Linux 区分大小写。服务器的 /root/Public 是另一个目录,不是 Quartz 的 public。

9.4 change_dir "/root//public" failed

这个错误表示 rsync 在服务器的 /root 中寻找 public,通常是因为把本应在 Mac 上执行的同步命令,执行在了 SSH 服务器中。

此时也等于让服务器通过公网 IP 再连接自己,没有实际意义。先执行 exit,然后在 Mac 的 /Users/chenweili/Documents/quartz 目录重新运行。

9.5 服务器已经有文件,为什么第一次 rsync 还是列出很多文件

rsync 不知道文件以前是通过宝塔上传的,它只能比较当前的文件信息。宝塔上传后的修改时间往往与 Mac 本地不同,所以默认比较会把它们列为需要处理的文件。

本教程在命令中加入 -c,改为比较文件实际内容。如果内容完全相同,就不会重新上传。

9.6 第二次同步仍显示很多 xfer,是否重新上传了全部文件

不一定。Quartz 重新构建时可能会改写大量文件的修改时间。不带 -c 时,rsync 会让它们进入传输检查流程,但仍可能只传输差异数据块。

以本次实际输出为例:

sent 145043 bytes
received 112308 bytes
total size is 14347377
speedup is 55.75

网站总大小约为 14.35 MB,Mac 实际发送约 145 KB,总通信量约 257 KB。这表示并没有完整重传全部文件。

使用 -c 后,只有内容真正变化的文件才需要传输。代价是本地和服务器都要读取文件并计算校验和,但当前网站只有十几 MB,开销很小。

9.7 怎样看懂预演结果

例如:

178 files to consider
sent 8204 bytes  received 20 bytes
total size is 14347377  speedup is 1744.57
  • 178 files to consider:检查了 178 个文件,不是上传了 178 个文件。
  • sent 和 received:本次实际通信量,包括文件数据、校验和与控制信息。
  • total size:所有本地网站文件的总大小,不是本次上传量。
  • speedup:总文件量与实际通信量的比值。数字越大,表示避免传输的内容越多。

如果中间没有列出任何文件名,说明本地和服务器的文件内容已经一致,不需要正式同步。

9.8 为什么重复预演时,每次都显示同一批变更

预演命令中的 n 是 dry-run,只显示计划执行的变更,不会实际上传或删除文件。

因此,只重复运行 -azcni 时,服务器内容并没有更新,同一批差异会一直显示。确认预演结果后,必须去掉 n,使用 -azci 执行正式同步。

正式同步完成后,不要重新构建,立即再运行预演。如果没有新的本地改动,此时应该不再列出文件。

9.9 为什么会显示大量 .f..t....

.f..t.... 表示文件内容相同,只有修改时间不同。Quartz 重新构建时会改写大量输出文件的时间,因此可能产生大量这类输出。

当前推荐命令使用 --no-times,不同步修改时间。这样 .f..t.... 不会再显示,只保留内容、大小或删除状态真正发生变化的项目。

9.10 中文文件名在终端中显示为乱码

如果终端中的中文路径显示为 \#207、\#224 等转义字符,通常是 Mac 自带的旧版 rsync 对高位字符进行了转义显示。

命令中加入下列参数:

--8-bit-output

它只改变终端显示方式,不会改变或重新编码服务器文件。

如果加上后仍不能显示中文,再检查 ~/.zshrc 末尾是否包含:

unset LC_ALL
export LANG=en_US.UTF-8
export LC_CTYPE=en_US.UTF-8

修改后执行:

source ~/.zshrc

9.11 rsync: command not found

说明服务器没有安装 rsync,需要根据服务器系统安装对应的软件包。

9.12 服务器目录没有写入权限

当前命令使用 root,通常有权限。如果换成其他用户,需要为该用户配置网站目录写入权限。

9.13 同步成功但网站没有变化

依次检查:

  1. 是否先运行了 npx quartz build
  2. 上传目标是否为网站实际运行目录
  3. 浏览器或 CDN 是否缓存了旧页面
  4. Nginx 网站根目录是否指向 /www/AfykeBlog/afyke-blog

9.14 首页能打开,但所有文章都是 404

如果 index.html 可以正常显示,但点击文章后进入 Quartz 的“私有笔记或笔记不存在”页面,通常不是文章没有上传,而是 Nginx 缺少无扩展名路由规则。

Quartz 生成的文章链接通常不带 .html。例如浏览器访问:

/博客搭建笔记/quartz/quartz个人博客使用教程

服务器中对应的真实文件却是:

/博客搭建笔记/quartz/quartz个人博客使用教程.html

如果 Nginx 只查找原始地址,就找不到实际的 .html 文件,于是返回 404.html。首页本身对应 index.html,所以仍然可以正常显示。

9.14.1 快速验证

在出现 404 的文章地址末尾手动添加 .html。如果添加后能正常打开,就可以确认是 Nginx 路由规则问题。

9.14.2 宝塔中的修复方法

进入:

网站 → 对应站点 → 设置 → 配置文件

确认网站根目录正确:

root /www/AfykeBlog/afyke-blog;
index index.html;

本次宝塔站点的实际配置为:

listen 100;
server_name 47.99.178.186;
root /www/AfykeBlog/afyke-blog;

当前配置中没有 location /。在下面这段配置之后:

#REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
include /www/server/panel/vhost/rewrite/47.99.178.186_100.conf;
#REWRITE-END

加入:

# Quartz 文章链接不带 .html,自动匹配实际 HTML 文件
location / {
    try_files $uri $uri.html $uri/ =404;
}

关键是 $uri.html:当原始地址不存在时,Nginx 会继续寻找同名的 .html 文件。

原配置中已经存在下面这条错误页规则,不要重复添加:

error_page 404 /404.html;

WARNING

如果以后配置中已经存在 location /,应修改已有规则,不能再添加第二个,否则 Nginx 会因为配置冲突而无法重新加载。

保存配置并重新加载 Nginx,然后使用 Command + Shift + R 强制刷新浏览器页面。

9.14.3 注意访问端口

该站点监听的是 100 端口:

listen 100;

因此访问地址必须包含 :100:

http://47.99.178.186:100/

如果访问地址中没有 :100,请求会进入其他监听 80 端口的站点配置,此处修改不会生效。

十、rsync 和 Git 部署怎么选

rsync 更适合当前情况:

  • 配置简单
  • 可以直接代替宝塔网页上传
  • 只同步变化的文件
  • 不需要在服务器配置完整的构建环境

Git 部署更适合后期自动化:

  • 本地把源码推送到 GitHub 或 Gitee
  • 自动化程序拉取代码
  • 自动构建 Quartz
  • 自动把生成结果发布到服务器

Git 部署配置更多。建议先把 rsync 跑通,之后再考虑全自动部署。

十一、总结

日常更新只需要三步。

第一步,进入 Mac 本地的 Quartz 项目目录:

cd /Users/chenweili/Documents/quartz

第二步,构建最新博客:

npx quartz build

第三步,根据需要选择同步方式。

普通增量同步:更新新增或修改的文件,但不删除服务器上多出的旧文件。

rsync --8-bit-output -azci --no-times --progress -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

与本地 public 完全保持一致:更新有变化的文件,并删除服务器上本地 public 不存在的旧文件。

先预演:

rsync --8-bit-output -azcni --no-times --delete -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

确认列出的上传和删除内容无误后,再正式同步:

rsync --8-bit-output -azci --no-times --delete --progress -e "ssh -p 22" public/ root@47.99.178.186:/www/AfykeBlog/afyke-blog/

WARNING

只有确认 /www/AfykeBlog/afyke-blog 专门存放 Quartz 生成文件时,才使用 --delete。如果目录里还有手工上传且本地 public 中没有的文件,它们也会被删除。

一句话记忆:Quartz 负责生成网页,rsync 负责把发生变化的网页快速同步到服务器。