4

我应该使用什么压缩-解压缩 Python 模块来构建 Google App Engine (Python 2.7) 与 Linux 机器上的应用程序交换压缩数据的系统?

还有两个额外的约束:

  • Linux机器和GAE都会做压缩/解压,需要线程安全运行;
  • 我想在不使用类文件对象的情况下做所有事情,因为 App Engine 无法为动态文件提供常规的 Python 文件名。

我之所以问,是因为从文档中不清楚某些 [de] 压缩模块是否是线程安全的。

谁能帮忙填写压缩模块表?

谢谢!

编辑(回应 abarnert 的问题):

  • RAM 与类文件对象 = App Engine 不提供打开类文件对象的方法(除非文件是作为应用程序的一部分上传的)。因此,如果 GAE 从 Linux 机器获取压缩数据,如果压缩模块坚持让我通过类似文件的对象,我不知道如何解压缩它。例如,gzip 模块坚持有一个文件名: http: //docs.python.org/2/library/gzip.html

  • linux 上的“线程安全” = 应用程序将位于网络服务器后面,因此可能会调用单独的线程同时压缩和/或解压缩。Linux 应用程序首先从磁盘读取几千个(数百万个)半随机压缩数据块,然后解压缩每个块,然后更改每个未压缩块,然后压缩更改的块,然后发送到 GAE。目前,该应用程序使用 zlib 并在 cherrypy 中的轻负载下完美运行,但一旦请求开始并行发生,就会引发 zlib 错误。一旦解决了这个压缩问题,我就会切换到 nginx。

4

1 回答 1

3

你的问题基本上没有意义,因为你误解了一些基本的东西,创造了不存在的问题。我尝试在评论中回答,但你可以这样做是有限度的,所以……</p>


我想在不使用类文件对象的情况下做所有事情,因为 App Engine 无法为动态文件提供常规的 Python 文件名。

对于类似文件的对象,您不需要文件名或文件。这就是类文件对象背后的全部想法。


App Engine 不提供打开类文件对象的方法(除非文件是作为应用程序的一部分上传的)。

不,您仍然在混淆文件对象和类文件对象。文件对象表示磁盘上的实际文件。GAE 限制了这些。类文件对象是具有相同 API 的任何对象——即,行为类似于文件的对象,但(不一定)实际上是一个对象。GAE 不会做任何事情来阻止您创建类似文件的对象。

--

类文件对象的范例示例 a 的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参数吗?文档的第一行说:

... fileobjfilename中的至少一个必须被赋予一个不平凡的值...</p>

所以,它不坚持有一个filename除非fileobjNone。为了解决这个问题,只是……不要错过Nonefileobj

fileobj必须是真实的文件对象,还是可以是另一个类似文件的对象?好吧,下一段说:

… 新的类实例基于fileobj,它可以是常规文件、StringIO对象或任何其他模拟文件的对象。

所以,你去吧。


不幸的是,Python 2.x 对于什么是类文件对象并不是 100% 一致的,而且文档并不总是清楚地说明这一点。(这在 3.x 中被清理了很多,但如果您使用 GAE,这对您没有任何好处。)

如果某些 API 不喜欢您的类文件对象,因为它没有模拟足够的 API,您将通过获取AttributeError. 例如,您可能会收到一条错误消息,指出您从中返回的对象urllib2.urlopen没有seek属性。

解决方法很简单:将其读入内存并创建一个StringIO. 换句话说,只需更改fileobj=my_file_objfileobj=StringIO(my_file_obj.read()).


另请注意, aGzipFile本身就是一个类似文件的对象。这很重要,因为这意味着您可以将事物链接在一起——您可以从 a 中制作GzipFilea StringIO,然后TarFile从 a 中制作 a GzipFile,等等。


linux 上的“线程安全” = 应用程序将位于网络服务器后面,因此可能会调用单独的线程同时压缩和/或解压缩。

这不是问题。再次阅读您链接到的文档:

如果您需要使用来自多个线程的单个 LZMAFile 实例,则需要使用锁来保护它。

压缩和/或解压缩多个独立LZMAFile实例不是问题。仅当您想跨线程共享相同的实例时。而且几乎没有充分的理由这样做。


Linux 应用程序首先从磁盘读取几千个(数百万个)半随机压缩数据块,然后解压缩每个块,然后更改每个未压缩块,然后压缩更改的块,然后发送到 GAE。

您所说的所有压缩器都是流压缩器。如果不将文件压缩到该点,则无法从文件中间解压缩任意块。

这对我来说意味着你实际上拥有的是一堆独立压缩的块(无论是在单独的文件中,还是连接在一起成为一个文件尚不清楚,但并不重要)。

这意味着您无需在任何地方共享解压缩器或压缩器。例如:

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 错误。

在不了解您的代码的情况下,很难对其进行调试,但我有一个很好的猜测:您正在通过写入临时文件进行压缩,而不是使用安全的 API tempfile,您已经重新发明了轮子,带有独特的错误,这意味着您最终会遇到线程覆盖彼此的临时文件。


关于个人锁定的评论对于 bz2 意味着什么

诚然,这有点令人困惑。它说的是:

  • 线程安全使用单独的锁定机制。

这显然意味着它是线程安全的,但你为什么关心他们使用什么锁定机制呢?什么是“单独的锁定机制”?

您只能通过查看来源来判断)。

它们的意思是每个BZ2Compressor(和BZ2Decompressor)对象都有自己独立的锁,因此其中一个可以锁定而不会影响其他对象。

如果你还没有处理过 Python C 扩展中的线程,你可能不明白这是怎么一回事。通常,在 Python 中,每个线程都需要持有GIL才能执行任何工作,这意味着一次只能运行一个线程。但是一个 C 扩展模块可以在处理非 Python 对象的 CPU 繁重的工作时释放 GIL(例如,压缩一个大缓冲区)。如果 N 个线程释放 GIL,最多可以并行运行 N+1 个线程,这意味着您可以从您的 8 核 CPU 中获得很大的优势,而无需运行多个进程。然而,当 GIL 被释放时,你不能接触任何 Python 对象,除非你用锁保护它们。

许多释放 GIL 以加快速度的模块会创建单个模块锁(有时因为很难弄清楚代码可能接触到哪些对象)。这意味着您可以运行一个线程来执行该模块的工作,同时运行一个线程来执行其他操作,但不能超过一个线程来执行该模块的工作。

但是如果每个线程只需要接触一个对象,您可以为每个对象使用不同的锁,这意味着您可以并行运行任意数量的线程,只要它们都在不同的对象上工作。

如果您确实尝试同时在两个线程中使用同一个对象,它不会破坏任何东西;你最终只会有一个线程等待获取锁,直到另一个线程完成(这并不比在 GIL 上等待更好或更糟)。

于 2013-06-19T02:45:31.690 回答