如何将 jar 转换为 rsyncable jar?

2022-09-03 15:43:20

我有一个由Gradle Shadow插件生成的胖子/uber JAR。我经常需要通过网络发送胖JAR,因此,对我来说,只发送文件的增量而不是cca 40 MB的数据很方便。rsync是实现此目的的一个很好的工具。但是,我的源代码中的一个小变化会导致最终的胖JAR发生很大的变化,因此rsync并没有尽其所能。

我可以将胖JAR转换为rsync友好的JAR吗?

我对解决方案/解决方法的想法:

  • 将沉重的重量放在rsync上,并以某种方式告诉它它可以使用压缩文件(我没有找到任何方法来做到这一点)。
  • 将不可同步的 jar 转换为 rsyncable jar
  • 告诉 Gradle Shadow 生成可同步的 jar(目前还不可能))

可能相关的问题:


答案 1

有两种方法可以做到这一点,这两种方法都涉及关闭压缩。首先,Gradle然后使用jar方法将其关闭...

您可以使用gradle执行此操作(此答案实际上来自OP)

shadowJar {
    zip64 true
    entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED
    exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
    manifest {
        attributes 'Main-Class': 'com.my.project.Main'
    }
}

jar {
    manifest {
        attributes(
                'Main-Class': 'com.my.project.Main',
        )
    }
}

task fatJar(type: Jar) {
    manifest.from jar.manifest
    classifier = 'all'
    from {
        configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
    } {
        exclude "META-INF/*.SF"
        exclude "META-INF/*.DSA"
        exclude "META-INF/*.RSA"
    }
    with jar
}

这里的关键是压缩已关闭,即

org.gradle.api.tasks.bundling.ZipEntryCompression.STORED

您可以在此处找到文档

https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/bundling/ZipEntryCompression.html#STORED

是的,您可以在新存档上将其速度提高约40%,在已经rsync的jar存档上将其速度提高200%以上。诀窍是不压缩jar,这样你就可以利用rsyncs分块算法。

我使用以下命令压缩了一个包含大量类文件的目录...

jar cf0 uncompressed.jar .
jar cf  compressed.jar   .

这创建了以下两个罐子...

-rw-r--r--  1 rsync jar    28331212 Apr 13 14:11 ./compressed.jar
-rw-r--r--  1 rsync jar    38746054 Apr 13 14:10 ./uncompressed.jar

请注意,未压缩 Jar 的大小大约为 10MB。

然后,我对这些文件进行同步,并使用以下命令对它们进行计时。(请注意,即使为压缩文件启用压缩也没有什么影响,我稍后会解释)。

压缩罐

time rsync -av -e ssh compressed.jar jar@rsync-server.org:/tmp/

building file list ... done
compressed.jar

sent 28334806 bytes  received 42 bytes  2982615.58 bytes/sec
total size is 28331212  speedup is 1.00

real  0m9.208s
user  0m0.248s
sys 0m0.483s

未压缩罐

time rsync -avz -e ssh uncompressed.jar jar@rsync-server.org:/tmp/

building file list ... done
uncompressed.jar

sent 11751973 bytes  received 42 bytes  2136730.00 bytes/sec
total size is 38746054  speedup is 3.30

real  0m5.145s
user  0m1.444s
sys 0m0.219s

我们加快了近 50%。这至少加快了rsync的速度,我们得到了很好的提升,但是后续的rsyncs已经进行了一些小的更改呢?

我从目录中删除了一个大小为170字节的类文件,重新创建了jars,它们就是这个大小。

-rw-r--r--  1 rsycn jar  28330943 Apr 13 14:30 compressed.jar
-rw-r--r--  1 rsync jar  38745784 Apr 13 14:30 uncompressed.jar

现在的时间安排非常不同。

压缩罐

building file list ... done
compressed.jar

sent 12166657 bytes  received 31998 bytes  2217937.27 bytes/sec
total size is 28330943  speedup is 2.32

real  0m5.435s
user  0m0.378s
sys 0m0.335s

未压缩罐

building file list ... done
uncompressed.jar

sent 220163 bytes  received 43624 bytes  175858.00 bytes/sec
total size is 38745784  speedup is 146.88

real  0m1.533s
user  0m0.363s
sys 0m0.047s

因此,我们可以使用此方法大大加快大型jar文件的rsync速度。其原因与信息论有关。当您压缩数据时,它实际上会从数据中删除所有常见内容,即您留下的内容看起来非常像随机数据,最好的压缩程序会删除更多此类信息。对任何数据和大多数压缩算法的微小更改都会对数据的输出产生巨大影响。

Zip 算法有效地使 rsync 更难找到服务器和客户端之间相同的校验和,这意味着它需要传输更多数据。当你解压缩它时,你让rsync做它擅长的事情,发送更少的数据来同步两个文件。


答案 2

据我所知,rsyncable gzip的工作原理是重置霍夫曼树,并每8192字节的压缩数据填充到字节边界。这避免了对压缩的长期副作用(如果移动的数据块至少是字节对齐的,则rsync会处理它们)

从这个意义上说,包含小文件(小于 8192 字节)的 jar 已经是可同步的,因为每个文件都是单独压缩的。作为测试,您可以使用jar的选项(无压缩)来检查它是否有助于rsync,但我认为它不会。-0

要提高同步性,您需要(至少):

  • 确保文件以相同的顺序存储。
  • 确保与未更改文件关联的元数据也未更改,因为每个文件都有一个本地文件头。例如,上次修改时间对于文件来说是有问题的。
    我不确定jar,但zip允许额外的字段,其中一些可能会阻止rsync匹配,例如unix扩展的最后一次访问时间。.class

编辑:我用以下命令做了一些测试:

FILENAME=SomeJar.jar

rm -rf tempdir
mkdir tempdir

unzip ${FILENAME} -d tempdir/

cd tempdir

# set the timestamp to 2000-01-01 00:00
find . -print0 | xargs --null touch -t 200001010000

# normalize file mode bits, maybe not necessary
chmod -R u=rwX,go=rX .

# sort and zip files, without extra
find . -type f -print | sort | zip ../${FILENAME}_normalized  -X -@

cd ..
rm -rf tempdir

删除jar / zip中包含的第一个文件时的rsync统计信息:

total: matches=1973  hash_hits=13362  false_alarms=0 data=357859
sent 365,918 bytes  received 12,919 bytes  252,558.00 bytes/sec
total size is 4,572,187  speedup is 12.07

当第一个文件被删除并且每个时间戳都被修改时:

total: matches=334  hash_hits=124326  false_alarms=4 data=3858763
sent 3,861,473 bytes  received 12,919 bytes  7,748,784.00 bytes/sec
total size is 4,572,187  speedup is 1.18

因此,存在显着差异,但没有我预期的那么多。

似乎更改文件模式不会影响传输(也许是因为它存储在中央目录中?


推荐