python集成inotify-rsync实现跨服务器文件同步

news2025/12/14 22:05:45

1、实现功能

        通过结合 Python 的 watchdog 库(类似 Linux 的 inotify 机制)和 rsync 命令,实现了文件系统变化的实时监控和增量同步。下面详细解释其工作原理和运行方式:

2、核心工作原理

2.1、文件监控

        使用watchdog库监控源目录的文件变化(创建、修改、删除、移动)。

        1)监听事件类型:modified、created、deleted、moved;

        2)所有变更会被收集到pending_changes集合中,避免重复处理。

2.2、增量同步

        通过rsync命令将变化的文件同步到远程服务器:

        1)使用--delete参数确保目标目录与源目录完全一致。

        2)通过--exclude参数支持忽略特定文件或目录(如临时文件、日志)。

2.3、事件合并

        设置 1 秒的延迟(sync_delay),将短时间内的多次变更合并为一次同步操作,减少不必要的网络传输。

3、环境准备

Step1、python环境

# 1. 安装 rsync 
sudo apt update -y 
sudo apt install rsync -y 

# 2. 验证安装 
rsync --version

Step2、python环境

pip install watchdog # 用于文件监控

Step3、主机环境

SSH相关操作:

        将本机公钥内容添加到被连接的目标主机 ~/.ssh/authorized_keys 文件中。其中,authorized_keys文件权限要可读写;

# SSH秘钥对生成
ssh-keygen -t rsa -b 4096 -C "your_name" # 生成ssh-key

# 如何实现ssh免密登录
方案1:
ssh-copy-id -i id_rsa_linux.pub name@ip # 将本机公钥内容就添加到服务器authorized_keys文件中了,name@ip为远程主机用户名、IP

方案2:
将本机公钥id_rsa.pub拷贝到远程目标主机
cat id_rsa.pub >> ~/.ssh/authorized_keys # 将公钥添加到authorized_keys
chmod -R 600 ~/.ssh/authorized_keys # 添加权限

# SSH秘钥连接测试是否成功
ssh name@ip 

4、示例代码

#!/usr/bin/env python3
# coding: utf-8
"""
pip install watchdog  # 用于文件监控
sudo apt install rsync -y
"""


import os
import time
import subprocess
import logging
import argparse
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileSystemMovedEvent, EVENT_TYPE_MODIFIED, EVENT_TYPE_CREATED, \
    EVENT_TYPE_DELETED, EVENT_TYPE_MOVED

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("sync_monitor.log"),
        logging.StreamHandler()
    ]
)


class RsyncHandler(FileSystemEventHandler):
    def __init__(self, source_dir, target_host, target_dir, exclude_patterns=None, ssh_key=None):
        self.source_dir = os.path.abspath(source_dir)
        self.target_host = target_host
        self.target_dir = target_dir
        self.exclude_patterns = exclude_patterns or []
        self.ssh_key = ssh_key
        self.last_sync_time = time.time()
        self.pending_changes = set()
        self.sync_delay = 1  # 合并1秒内的变更

    def enqueue_change(self, event):
        """将变更加入待处理队列"""
        path = event.src_path
        if isinstance(event, FileSystemMovedEvent):
            path = event.dest_path

        rel_path = os.path.relpath(path, self.source_dir)
        self.pending_changes.add(rel_path)

        # 安排延迟同步,合并短时间内的多次变更
        if time.time() - self.last_sync_time > self.sync_delay:
            self.perform_sync()

    def on_modified(self, event):
        if not event.is_directory:
            logging.info(f"修改: {event.src_path}")
            self.enqueue_change(event)

    def on_created(self, event):
        logging.info(f"创建: {event.src_path}")
        self.enqueue_change(event)

    def on_deleted(self, event):
        logging.info(f"删除: {event.src_path}")
        self.enqueue_change(event)

    def on_moved(self, event):
        logging.info(f"移动: {event.src_path} -> {event.dest_path}")
        self.enqueue_change(event)

    def build_rsync_command(self):
        """构建rsync命令"""
        cmd = ["rsync", "-avz", "--delete"]

        # 添加排除模式
        for pattern in self.exclude_patterns:
            cmd.extend(["--exclude", pattern])

        # 添加SSH密钥选项
        if self.ssh_key:
            cmd.extend(["-e", f"ssh -i {self.ssh_key}"])

        # 添加源目录和目标
        cmd.extend([f"{self.source_dir}/", f"{self.target_host}:{self.target_dir}/"])

        return cmd

    def perform_sync(self):
        """执行rsync同步"""
        if not self.pending_changes:
            return

        logging.info(f"同步变更: {', '.join(self.pending_changes)}")
        self.pending_changes.clear()

        try:
            cmd = self.build_rsync_command()
            logging.debug(f"执行命令: {' '.join(cmd)}")

            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=True
            )

            logging.info(f"同步成功: {result.stdout.strip()}")
            self.last_sync_time = time.time()

        except subprocess.CalledProcessError as e:
            logging.error(f"同步失败: {e.stderr.strip()}")
        except Exception as e:
            logging.error(f"发生错误: {str(e)}")


def main():
    parser = argparse.ArgumentParser(description="文件同步监控工具 (Inotify + Rsync)")
    parser.add_argument("source", help="源目录路径")
    parser.add_argument("target_host", help="目标主机 (user@host)")
    parser.add_argument("target_dir", help="目标目录路径")
    parser.add_argument("-e", "--exclude", action="append", help="排除模式 (可重复使用)")
    parser.add_argument("-k", "--ssh-key", help="SSH私钥路径")
    parser.add_argument("-i", "--initial-sync", action="store_true", help="启动时执行初始同步")

    args = parser.parse_args()

    # 验证源目录存在
    source_dir = os.path.abspath(args.source)
    if not os.path.isdir(source_dir):
        logging.error(f"源目录不存在: {source_dir}")
        return

    logging.info(f"开始监控目录: {source_dir}")
    logging.info(f"目标服务器: {args.target_host}:{args.target_dir}")

    # 创建事件处理器
    event_handler = RsyncHandler(
        source_dir,
        args.target_host,
        args.target_dir,
        args.exclude,
        args.ssh_key
    )

    # 执行初始同步
    if args.initial_sync:
        logging.info("执行初始同步...")
        event_handler.perform_sync()

    # 创建观察者
    observer = Observer()
    observer.schedule(event_handler, path=source_dir, recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()


if __name__ == "__main__":
    main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2396079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring AI 系列之使用 Spring AI 开发模型上下文协议(MCP)

1. 概述 现代网页应用越来越多地集成大型语言模型(LLMs)来构建解决方案,这些解决方案不仅限于基于常识的问答。 为了增强 AI 模型的响应能力,使其更具上下文感知,我们可以将其连接到外部资源,比如搜索引擎…

[Python] Python运维:系统性能信息模块psutil和系统批量运维管理器paramiko

初次学习,如有错误还请指正 目录 系统性能信息模块psutil 获取系统性能信息 CPU信息 内存信息 磁盘信息 网络信息 其他信息 进程信息 实用的IP地址处理模块IPy IP地址、网段的基本处理 多网络计算方法 系统批量运维管理器paramiko paramiko 的安装 Li…

Linux 简单模拟实现C语言文件流

🌇前言 在 C语言 的文件流中,存在一个 FILE 结构体类型,其中包含了文件的诸多读写信息以及重要的文件描述符 fd,在此类型之上,诞生了 C语言 文件相关操作,如 fopen、fclose、fwrite 等,这些函数…

小程序使用npm包的方法

有用的链接 npm init -y 这个命令很重要, 会初始化 package.json 再重新打开微信小程序开发工具 选择工具中npm构建 在程序中引用时在main.js中直接使用包名的方式引用即可 如安装的是generator包,npm构建后就会生成 const myPackage require(***-generato…

Rust 学习笔记:发布一个 crate 到 crates.io

Rust 学习笔记:发布一个 crate 到 crates.io Rust 学习笔记:发布一个 crate 到 crates.io提供有用的文档注释常用标题文档注释作为测试注释所包含的项目 使用 pub use 导出一个方便的公共 API设置 crates.io 账户添加 metadata 到一个新的 crate发布到 c…

Vert.x学习笔记-EventLoop与Context的关系

Vert.x学习笔记 1. EventLoop 的核心作用2. Context 的核心作用3. EventLoop 与 Context 的关系1. 事件循环(EventLoop)的核心职责2. 上下文(Context)的核心职责3. 事件循环与上下文的关系(1)一对一绑定&am…

2025030给荣品PRO-RK3566开发板单独升级Android13的boot.img

./build.sh init ./build.sh -K ./build.sh kernel 【导入配置文件】 Z:\Android13.0\rockdev\Image-rk3566_t\config.cfg 【更新的内核】 Z:\Android13.0\rockdev\Image-rk3566_t\boot.img 【导入分区表,使用原始的config.cfg会出错的^_】 Z:\Android13.0\rockdev\…

由enctype-引出post与get的关系,最后深究至请求/响应报文

本篇载自我的笔记,本次为第二次复习。我觉得我有能力理一下思路了。 --- 笔记截图。 enctype HTML 表单的 enctype(Encode Type,编码类型)属性用于控制表单数据在提交到服务器时的编码方式,不同取值的详细解析如下&a…

搭建基于VsCode的ESP32的开发环境教程

一、VsCode搜索ESP-IDF插件 根据插件处搜索找到ESP-IDF并安装 安装完成 二、配置安装ESP-IDF 配置IDF 按照如下配置,点击安装 安装完成 三、使用案例程序 创建一个闪光灯的例子程序,演示程序编译下载。 选择blink例子,闪烁LED的程序 选…

【MFC】初识MFC

目录 01 模态和非模态对话框 02 静态文本 static text 01 模态和非模态对话框 首先我们需要知道模态对话框和非模态对话框的区别: 模态对话框是一种阻塞时对话框,它会阻止用户与应用程序的其他部分进行交互,直到用户与该对话框进行交互并关…

如何通过数据分析优化项目决策

通过数据分析优化项目决策需从明确数据分析目标、选择适当的数据分析工具、确保数据质量、建立数据驱动文化等方面入手,其中,明确数据分析目标是优化决策过程的基础,只有清晰明确的数据分析目标才能指导有效的数据采集与分析,避免…

2024年数维杯国际大学生数学建模挑战赛B题空间变量协同估计方法研究解题全过程论文及程序

2024年数维杯国际大学生数学建模挑战赛 B题 空间变量协同估计方法研究 原题再现: 在数理统计学中,简单采样通常假设来自相同总体的采样点彼此独立。与数理统计相反,空间统计假设空间变量的采样点是相依的,并在其值中表现出某些趋…

leetcode hot100刷题日记——34.将有序数组转换为二叉搜索树

First Blood:什么是平衡二叉搜索树? 二叉搜索树(BST)的性质 左小右大:每个节点的左子树中所有节点的值都小于该节点的值,右子树中所有节点的值都大于该节点的值。 子树也是BST:左子树和右子树也…

网页自动化部署(webhook方法)

实现步骤: 宝塔安装宝塔WebHook 2.5插件。 github 上配置网页仓库(或可在服务器的网页根目录clone)。 配置宝塔WebHook 2.5 添加hook脚本; 编辑添加syncJC脚本; #!/bin/bash # 定义网站根目录 WEBROOT"/www…

AU6825集成音频DSP的2x32W数字型ClaSSD音频功率放大器(替代TAS5825)

1.特性 ● 输出配置 - 立体声 2.0: 2 x 32W (8Ω,24V,THD N 10%) - 立体声 2.0: 2 x 26W (8Ω,21V,THD N 1%) ● 供电电压范围 - PVDD:4.5V -26.4V - DVDD: 1.8V 或者 3.3V ● 静态功耗 - 37mA at PVDD12V ● 音频性能指标 - THDN ≤ 0.02% at 1W,1kHz - SNR ≥ 107dB (A-wei…

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1商用服务体验全流程

华为云 Flexus 与 DeepSeek-V3/R1 的深度整合,构建了一套 “弹性算力 智能引擎” 的协同体系。 Flexus 系列云服务器基于柔性计算技术,通过动态资源调度(如 Flexus X 实例)实现 CPU / 内存的实时弹性分配,尤其适合大模…

C# NX二次开发-查找连续倒圆角面

在QQ群里有人问怎么通过一个选择一个倒圆角面来自动选中一组倒圆角面。 可以通过ufun函数 UF_MODL_ask_face_type 和 UF_MODL_ask_face_props 可判断处理选择相应的一组圆角面。 代码: Tag[] 查找连续倒圆角面(Tag faceTag) {theUf.Modl.AskFaceType(faceTag, out int typ…

今天遇到的bug

先呈现一下BUG现象。 这主要是一个传参问题,参数一直传不过去。后来我才发现,问题所在。 我们这里用的RquestBody接收参数,所有请求的参数需要用在body体中接收,但是我们用postman,用的是字符串查询方式传参&#x…

长安链智能合约命令解析(全集)

创建命令解析 ./cmc client contract user create \ --contract-namefact \ --runtime-typeWASMER \ --byte-code-path./testdata/claim-wasm-demo/rust-fact-2.0.0.wasm \ --version1.0 \ --sdk-conf-path./testdata/sdk_config.yml \ --admin-key-file-paths./testdata/cryp…

一、OpenCV的基本操作

目录 1、OpenCV的模块 2、OpenCV的基础操作 2.1图像的IO操作 2.2绘制几何图形 2.3获取并修改图像中的像素点 2.4 获取图像的属性 2.5图像通道的拆分与合并 2.6色彩空间的改变 3、OpenCV的算数操作 3.1图像的加法 3.2图像的混合 3.3总结 1、OpenCV的模块 2、OpenCV的基…