zoukankan      html  css  js  c++  java
  • 使用svndumpfilter exclude来清理svn库的废弃文件实现差别备份

     

    先啰嗦下为什么要使用svndumpfilter…

    svn库用久了以后就会越来越大,进行整体文件打包备份的时候,发现压力山大…尤其是美术团队也在使用svn进行重要美术资源管理的时候…….几百g的资源同步备份的盛况对于机器来说是难以承受的压力,尤其是当存在多种备份方式(比如本地多机器拷贝、远程机器上传备份)。几十kb的速度去承担几百g的备份是痛苦的事情。

    公司的运维又不给力,围观了下他们部署在我们服务器的批处理备份代码,就一个简单的对svn的仓库目录直接用winrar压缩,然后用rsync同步,这是多么粗糙的方案,当然也不是说错误,只是说过于暴力了点,且可控性太差,当想要过滤某些文件的时候就无法实现了。

    一开始我想的方案也是压缩打包,用python实现,这样过滤文件的时候,可以在逻辑上加处理,这样可以屏蔽下想要过滤的东西,代码如下:

    #-*- coding=gbk -*-
    
    import tarfile
    import os
    import time
    import threading
    import datetime
    import shutil
    import subprocess
    from myEmail import Mailer
    
    ignoreNames = ["ArtGroup", ".svn"]
    TargetDir = "D:\02_ucgame_Svn_Repositories"
    BackUp = "svn_backup_%s.tar.gz"
    ShareDir = "z:\backup"
    mailTitle = "[UCCDTest] svn backup analyze %s"
    receivers = [
                "account@qq.com",
                "account@qq.com",             "account@qq.com",             "account@qq.com",              ]
    def delShareFile():
        # 删除共享文件夹里的备份文件
        # 该文件夹路径为z:\backup,里面均是压缩文件包
        # 故,删除策略为反向排序,取文件的修改时间作为排序的关键字处理
        # 删除时,保留最新的3个文件
        files = [os.path.join(ShareDir, x) for x in os.listdir(ShareDir)]
        if len(files) > 2:
            sorted(files, key = lambda file:os.stat(file).st_mtime, reverse = True)
            DelFiles =  files[:-2]
            for nfile in DelFiles:
                os.remove(nfile)
        
    def delLocalFile():
        # 删除本地文件夹里的备份文件
        # 该文件夹路径为当前脚本所在路径,里面均是压缩文件包
        # 故,删除策略为反向排序,取文件的修改时间作为排序的关键字处理
        # 删除时,保留最新的1个文件(不删除该文件的目的是,避免误操作的文件丢失,同时,为后续操作保留一个缓存文件)
        files = [os.path.join(os.getcwd(), x) for x in os.listdir(".") if "svn_backup" in x]
        if len(files) > 1:
            sorted(files, key = lambda file:os.stat(file).st_mtime, reverse = True)
            DelFiles = files[:-1]
            for nfile in DelFiles:
                os.remove(nfile)
        
    def addFile(tf, files, root):
        # 往压缩包里面添加文件,在添加时,均处理过文件的地址,添加了替换的replace方法的目的是相对路径时,避免出现多余的符号
        st = time.time()
        length = len(files)
        for ids, nfile in enumerate(files):
            if time.time() - st >= 3:
                print "
    adding file at root [%s]: [%s / %s], package is [%s MB]" % (root, ids, length, os.stat(tarPackage).st_size / (1024 * 1024))
                st = time.time()
            fp = os.path.join(root, nfile).replace(".\","")
            key = root.split("\")
            if len(key) > 1:
                key = key[1]
                if key not in ret:
                    ret[key] = os.stat(fp).st_size
                else:
                    ret[key] += os.stat(fp).st_size
            tf.add(fp)
    
    def checkDir(Names):
        # 检测需要过滤得文件名或路径名,这里主要用来过滤svn主路径下的特殊文件夹名字
        for nName in ignoreNames:
            if nName not in Names:
                pass
            else:
                return False
        return True
    
    def maketar(dist, tar):
        # 扫描缓存文件夹,对待处理的文件夹进行过滤,并调用添加方法,将待压缩文件添加到压缩包内
        st = time.time()
        cur = os.getcwd()
        os.chdir(dist)
    
        for root, dir, files in os.walk("."):
            if not checkDir(root):
                continue
            addFile(tar, files, root)
        tar.close()
        os.chdir(cur)
    
    def uploadFile(tar):
        args = ["rsync","-avzPu", "--password-file=passwd.pd","<p.txt", "svn_backup_*", "cwRsync@127.0.0.0::mysvnbackup"]
        os.system("".join(args))
        #p = subprocess.Popen(args, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
        #print p.communicate()[1]
        #print p.stderr.read()
        
        
    
    def getMailBody(rd):
        msg = '<table border="1"><tr><th>RootDirName</th><th>PackageSize</th></tr>'
    
        for nk in sorted(rd.items(), key = lambda pair: pair[1], reverse = True):
            if nk[1] < 1024:
                msg += '<tr><td>%s</td><td>%.2fByte</td></tr>' % (nk[0], float(nk[1]))            
            elif nk[1] < 1024 * 1024:
                msg += '<tr><td>%s</td><td>%.2fKB</td></tr>' % (nk[0], float(nk[1]) / 1024)
            elif nk[1] < 1024 * 1024 *1024:
                msg += '<tr><td>%s</td><td>%.2fMB</td></tr>' % (nk[0], float(nk[1]) / ( 1024 * 1024 ))
            else:
                msg += '<tr><td>%s</td><td><font color="red">%.2fGB</font></td></tr>' % (nk[0], float(nk[1]) / ( 1024 * 1024 * 1024 ))
            
        msg+= '</table>'
        return msg
    
    global ret
    ret = {}
    # 获取时间参数,用来评估多线程实现的模块的性能,暂时无用,详见makeTar_multipleThreads.py
    st = time.time()
    # 获取当前日期,用于生成压缩包的名字
    ts = datetime.datetime.now().strftime("%Y-%m-%d")
    mailTitle = mailTitle % ts
    # 开始打包
    
    print "start making package."
    tarPackage = os.path.join(os.getcwd(),  BackUp % ts)
    tar = tarfile.open(tarPackage, "w:gz")
    maketar(TargetDir, tar)
    print "finished making package. used time - %s" % (time.time() - st)
    # 分析文件夹状态,发送邮件
    mailer = Mailer()
    msg = getMailBody(ret)
    for nmail in receivers:
        mailer.send(nmail, mailTitle, msg)
        print "send mail to ", nmail
        
    
    # 删除z盘的备份文件
    delShareFile()
    # 上传备份文件到z盘
    shutil.copy(tarPackage, ShareDir)
    uploadFile(tarPackage)
    # 删除本地的文件
    delLocalFile()

    仅仅实现了过滤备份并不靠谱,因为svn里面的资源和版本其实仍然在,直接找到svn文件夹,删除文件当然也可以,但是会导致svn库里面出现空版本或其他问题,所以还是要利用svn本身的管理工具去处理。搜索了半天,发现了svndumpfilter,可以利用他的exclude指令去过滤文件,然后把过滤后的文件结构导入svn即可,但实际按网又介绍的又各种出错,一般都是什么错误的文件头,如下图

    image

    反复研究和google尝试(baidu查不到的只能找google了)后,终于确认下列的流程是可以操作的:

    1. 使用svnadmin把整个要处理的svn库的项目导出来

    命令为:svnadmin dump repos1 > repos1.dump

    repos1 为你的对应库名,repos1.dump为导出的文件名,可以随意命名,只要自己记得。

    2. 使用svndumpfilter exclude命令过滤项目中废弃的文件夹,可以填写多个文件路径,该命令是根据路径名字过滤的。

    命令为:svndumpfilter exlude directory1 directory2 directory3 <repos1.dump> filtered-repos1.dump

    如上,路径可以随意填写,但一定要正确存在,比如你导出的是/test/test1,你要过滤的是test1下面的test2,那么这里的directory1应该写为test1/test2,如果还有其他要一并过滤的,可以空格添加其他directory2,directory3即可。

    注意,路径中如果包含有空格等,需要用双引号包起来。避免识别错误。

    路径配置完成后,一定要用<>把之前第一步导出来的备份dump文件括起来,表示过滤是在该文件的结构中进行。

    而最后一个filtered-repos1.dump文件就很好理解了,就是过滤出来的新的结构备份文件。

    过滤后的效果如下图:

    image

    3. 之后再新建一个库,导进去即可,如果要继续使用原来的库名,将原来的库删除后重建,导入即可,导入命令用svnadmin load。

    当然备份打包还可以用svn推荐的svnadmin dump或svnadmin hotcopy文件备份,但速度太慢,还是直接整体文件拷贝比较快,虽然不够合理,但如果选择完全没人操作的时间还是可以的,这样恢复也快。对于我们的项目来说,目前还是选用文件打包备份快点,也可以和运维那边的管理方式结合起来,省的麻烦。。

  • 相关阅读:
    对自己负责~~
    继续负责
    问题的一天
    1个月=22年
    刚才写的没显示?
    布置任务
    心情很糟
    考试结束
    没有负责哈
    php获取任意时间的时间戳
  • 原文地址:https://www.cnblogs.com/ckat/p/3642826.html
Copyright © 2011-2022 走看看