1

问题:

  • 主要问题:并行这些工作的最佳策略是什么?
  • 想法:如何使用其他机制(如第二个校验和(Adler32?)

塞纳里奥:

我正在用java编写一种同步工具。基本上,它从网络服务器下载一个存储库,该存储库代表本地机器上的文件/目录结构,并以压缩形式定义所需文件的源,并结合哈希值来验证文件。我猜是一个基本的东西。

要求:

  • 多平台java桌面应用
  • 最佳速度和并行化

示例结构:(最好使用游戏模组来描述)

示例存储库文件

{"name":"subset1", "mods":[
    {
        "modfolder":"mod1",
        "modfiles":[
            {
                "url":"http://www.example.com/file2.7z",
                "localpath":"mod1/file2",
                "size":5,
                "sizecompressed":3,
                "checksum":"46aabad952db3e21e273ce"
            },
            {
                "url":"http://www.example.com/file1.7z",
                "localpath":"mod1/file1",
                "size":9,
                "sizecompressed":4,
                "checksum":"862f90bafda118c4d3c5ee6477"
            }
        ]
    },
    {
        "modfolder":"mod2",
        "modfiles":[
            {
                "url":"http://www.example.com/file3.7z",
                "localpath":"mod2/file3",
                "size":8,
                "sizecompressed":4,
                "checksum":"cb1e69de0f75a81bbeb465ee0cdd8232"
            },
            {
                "url":"http://www.example.com/file1.7z",
                "localpath":"mod2/file1",
                "size":9,
                "sizecompressed":4,
                "checksum":"862f90bafda118c4d3c5ee6477"
            }
        ]
    }
]}

客户端文件结构,应该是在同步之后

    mod1/
         file2
         file1
    mod2/
         file3
         file1

// mod1/file2 == mod2/file2

关于存储库的一个特殊之处: 从服务器获取的存储库仅代表更大存储库的子集,因为用户只需要一个子树,它正在变化(也重叠)。有时存储库由 mod1 和 mod2 组成,有时由 mod1 和 mod3 等组成。

要做的工作:

  • 下载存储库并解析它(Net I/O)
  • 在进程结束时将不在存储库中的文件标记为删除(由于相同的校验和,文件可能被复制)(文件 I/O)
  • 如果文件存在:检查现有文件的校验和(校验和缓存)(文件 I/O)
  • 如果文件不存在:检查其他子树中相同文件的校验和缓存以复制文件而不是下载文件(轻文件 I/O)
  • 以压缩形式下载单个文件 (Net I/O)
  • 提取压缩文件(文件 I/O)
  • 未压缩文件的校验和(文件 I/O)
  • 缓存与文件关联的校验和。(轻文件 I/O)

我的解决方案:(许多不同的生产者/消费者)

  • 校验和缓存正在使用 MapDBs 持久映射。
  • ATM 仅使用 md5 校验和。
  • 队列:每个 Workertype 都有一个阻塞队列(生产者/消费者)
  • 线程池:每个 Workertype 都有一个固定的线程池,例如 3 个下载器,2 个校验和,...
  • Worker 将当前作业分发到其他队列:Downloader -> Extract -> Checksum

工人类型:

  • Localfile Worker:检查本地文件结构(使用校验和缓存),将工作重定向到 Download-Worker、Delete-Worker
  • 复制:将具有相同校验和的文件复制到目标
  • 下载:下载文件
  • 校验和:校验和文件并插入校验和缓存
  • 删除:删除文件
  • 提取:提取压缩文件
4

1 回答 1

2

并行这些工作的最佳策略是什么?

你有 I/O。而且,如果一个作业已经在一个目录上进行,那么另一个作业可能不能同时在同一个目录上运行。

所以,你需要在这里锁定。建议:在文件系统上使用锁定目录,并使用目录而不是文件来锁定。为什么?因为目录创建是原子的(第一个原因),并且因为 Java 6 不支持原子文件创建(第二个原因)。事实上,您甚至可能需要两个锁定目录:一个用于内容下载,另一个用于内容处理。

您已经完成了下载与处理的分离,所以我在这里没有更多要说的;)

但是,我不确定您为什么要缓存校验和?它对我来说似乎没有那么有用...

另外,我不知道您必须处理的文件有多大,但是为什么还要检查现有目录内容等而不是提取新目录并重命名?IE:

  • 提取新目录newdir
  • 校验和;
  • 移动dstdirdstdir.old;
  • 移动newdirdstdir;
  • 废品dstdir.old

这甚至意味着您可以并行化报废,但这是太多的 I/O 并行化......您将不得不限制执行实际 I/O 的线程数。

编辑这是我将如何分开处理:

  • 首先,档案本身不再有校验和,但档案中有一个文件,其中包含每个文件的 MD5 和(例如,MD5SUMS);
  • 两个阻塞队列:下载->替换,替换->报废;
  • 一个处理器负责下载;完成后,它会填充下载 -> 替换队列;
  • 另一个处理器从下载中选择一个任务 -> 替换队列;此任务按顺序执行取消归档和校验和;如果两者都正确,如上所述,它会重命名现有目录,将提取的目录重命名为预期的目录,并将报废任务放在替换 -> 报废队列上;
  • 第三个,也是最后一个处理器,从报废队列中挑选一个任务,并删除之前的档案。

请注意,如果校验和那么重,则可以并行化。

于 2013-06-19T11:06:06.330 回答