我应该使用什么压缩 - 解压缩Python模块来构建一个系统,其中Google App Engine(Python 2.7)与Linux机器上的应用程序交换压缩数据?
还有两个额外的限制因素:
我问,因为从文档中不清楚某些[de]压缩模块是否是线程安全的。
有人可以帮忙填写压缩模块表吗?
bz2:IS SAFE,per:http://docs.python.org/2/library/bz2.html但是有关于个别锁定的评论让我想知道是否需要明确管理锁。
zlib :? - 发布GIL,每个:http://docs.python.org/2/c-api/init.html 但基础C代码被认为是安全的:http://www.gzip.org/zlib/zlib_faq.html#faq21
gzip:?
lzma:不安全,依据:http://docs.python.org/3.4/library/lzma
谢谢!
编辑(回答abarnert的问题):
RAM与类似文件的对象= App Engine不提供打开类文件对象的方法(除非文件是作为应用程序的一部分上传的)。因此,如果GAE从Linux盒子中获取压缩数据,如果压缩模块坚持要通过类似文件的对象,我不知道如何解压缩它。例如,gzip模块坚持使用文件名:http://docs.python.org/2/library/gzip.html
Linux上的“线程安全”=应用程序将位于Web服务器后面,因此可能会同时调用单独的线程进行压缩和/或解压缩。 Linux应用程序首先从磁盘中读取几千个(数百万个)半随机块压缩数据,然后解压缩每个,然后更改每个未压缩的块,然后压缩更改的块,然后发送到GAE。现在,该应用程序使用zlib并在cherrypy中轻负载下运行完美,但是一旦请求开始并行发生,就会引发zlib错误。一旦这个压缩事情被整理出来,我就会切换到nginx。
答案 0 :(得分:3)
你的问题基本上没有意义,因为你误解了一些基本的东西并产生了不存在的问题。我试着在评论中回答,但是你可以用这种方式限制,所以...
我想在不使用类似文件的对象的情况下完成所有操作,因为App Engine无法为动态文件提供传统的Python文件名。
您不需要文件类对象的文件名或文件。这就是文件类对象背后的整个想法。
App Engine不提供打开类文件对象的方法(除非该文件是作为应用程序的一部分上传的。)
不,您仍然会混淆文件对象和类文件对象。 file object表示磁盘上的实际文件。 GAE限制了那些。 类似文件的对象是具有相同API的任何对象,即一个 文件的对象,没有(必然)实际为一个。 GAE没有做任何事情来阻止你创建类似文件的对象。
-
类文件对象的范例示例StringIO.StringIO
就像一个文件对象,但它不是实际读写文件,而是读取和写入内存中的字符串缓冲区。
因此,您可以像这样编写类似文件的对象:
my_file_like_obj = StringIO()
或者,如果你在内存中有一个缓冲区,并且你希望能够像文件那样从中读取它:
my_file_like_obj = StringIO(buffer)
但是,在许多情况下,Python / GAE已经为您提供了一个类似文件的对象,您可以按原样使用它,而无需将其读入缓冲区并将其包装在另一个类似文件的对象中。许多网络API为您提供类似文件的对象,但不是全部。
例如,如果你调用urllib2.urlopen
,结果就是一个类似文件的对象;如果您拨打urlfetch.fetch
,则不会,如果您需要,则必须使用StringIO(response.content)
。
因此,如果GAE从Linux机箱获取压缩数据,如果压缩模块坚持要通过类似文件的对象,我就不知道如何解压缩它。
如果它坚持使用类似文件的对象,请为其提供类似文件的对象。创建实际文件是一种方法,但不是唯一的方法。如果您收到urllib2.urlopen
回复,请通过该回复。如果您在内存中有缓冲区,只需将其包装在StringIO
中即可。等等。
例如,gzip模块坚持使用文件名:http://docs.python.org/2/library/gzip.html
不,它没有。阅读您链接到的文档:
class gzip.GzipFile([filename [,mode [,compress level [,fileobj [,mtime]]]]])
请注意,fileobj
参数以及filename
参数?文档的第一行说:
... fileobj 和 filename 中的至少一个必须被赋予非平凡的价值......
因此,除非filename
为fileobj
,否则它不会坚持None
,。为了解决这个问题,只是......不要None
通过fileobj
。
fileobj
必须是真正的文件对象,还是可以是另一个类似文件的对象?那么,下一段说:
...新的类实例基于 fileobj ,它可以是常规文件,
StringIO
对象或任何其他模拟文件的对象。
所以,你去吧。
不幸的是,Python 2.x对于什么算作类似文件的对象并不是100%一致,并且文档并不总是清楚。 (这在3.x中被清理了很多,但如果您使用GAE,这对您没有任何好处。)
如果某些API不喜欢您的文件类对象,因为它没有模拟足够的API,您会发现AttributeError
。例如,您可能会收到错误消息,指出您从urllib2.urlopen
返回的对象没有seek
属性。
解决方法很简单:将其读入内存并创建StringIO
。换句话说,只需将fileobj=my_file_obj
更改为fileobj=StringIO(my_file_obj.read())
。
另请注意,GzipFile
本身就是一个类似文件的对象。这很重要,因为这意味着您可以将事物链接在一起 - 您可以从GzipFile
中创建StringIO
,然后从TarFile
中创建GzipFile
,等等。
"线程安全"在Linux上=应用程序将在Web服务器后面,因此可能会同时调用单独的线程进行压缩和/或解压缩。
这不是问题。再次,阅读您链接到的文档:
如果您需要从多个线程使用单个LZMAFile实例,则需要使用锁来保护它。
压缩和/或解压缩多个独立的LZMAFile
实例不是问题。只有当您想跨线程共享同一个实例时才可以。并且几乎没有充分的理由这样做。
Linux应用程序首先从磁盘中读取几千个(数百万个)半随机块压缩数据,然后解压缩每个,然后更改每个未压缩的块,然后压缩更改的块,然后发送到GAE。 / p>
您所谈论的所有压缩机都是流式压缩机。在不压缩文件的情况下,您无法从文件中间解压缩任意块。
这对我来说意味着你实际拥有的是一堆独立压缩的块(无论是在单独的文件中,还是连接成一个单独的文件都不清楚,但并不重要)。
这意味着您无需在任何地方共享解压缩程序或压缩程序。例如:
with lzma.LZMAFile(chunk_path) as f:
decompressed_chunk = f.read()
new_chunk = alter(decompressed_chunk)
sio = StringIO.StringIO()
with lzma.LZMAFile(fileobj=sio) as f:
f.write(new_chunk)
compressed_chunk = sio.getvalue()
send_to_gae(compressed_chunk)
线程之间没有什么可分享的。即使200个线程同时执行此操作,即使其中100个正在尝试处理相同的块文件,仍然不会出现问题。唯一需要排序的是最后send_to_gae
。
现在,该应用程序使用zlib并在cherrypy中轻负载下运行完美,但在请求开始并行发生时立即引发zlib错误。
在不了解您的代码的情况下,调试起来非常困难,但我有一个很好的猜测:您通过写入临时文件来进行压缩,而不是使用tempfile
中的安全API,你已经彻底改造了轮子,带有独特的错误,这意味着你最终会用线程覆盖彼此。临时文件。
关于个别锁定的评论对于bz2
是什么意思
不可否认,这有点令人困惑。它只是说:
这显然意味着它的线程安全,但为什么你关心它们使用什么锁定机制?什么是"个人锁定机制"呢?
你只能看the source)。
它们的含义是每个BZ2Compressor
(和BZ2Decompressor
)对象都有自己独立的锁,因此其中一个可以锁定而不会影响其他对象。
如果您还没有处理Python C扩展中的线程,您可能无法理解这是什么。通常,在Python中,每个线程都需要保持GIL来执行任何工作,这意味着一次只能运行一个线程。但是C扩展模块可以释放GIL,同时使用非Python对象(例如,压缩大缓冲区)进行CPU繁重的工作。如果N个线程释放GIL,则最多可以并行运行N + 1个线程,这意味着您可以从您的8核CPU中获得很大的优势,而无需运行多个进程。但是,除非您使用锁保护它们,否则在GIL发布时您无法触摸任何Python对象。
许多发布GIL以加速的模块会创建一个模块锁(有时因为它不容易弄清楚代码可能触及的对象)。这意味着你可以运行一个线程,与执行其他操作的线程并行处理该模块的内容,但是执行该模块的东西不会超过一个线程。
但是如果每个线程只需要触摸一个对象,你就可以为每个对象使用不同的锁,这意味着你可以并行运行任意数量的线程,只要它们全部正常工作即可。在不同的对象上。
如果你做尝试同时在两个线程中使用同一个对象,它就不会破坏任何东西;你最终会得到一个等待获取锁定的线程,直到其他线程完成(这比等待GIL更好或更差)。