使用Paperclip上传小文件到s3时,CPU使用率达到100%并挂起。

53
我有一个包含小于20MB的PDF文件(每个PDF代表一则广告)的目录,存储在一个AWS EC2大型实例上。我正尝试使用Ruby和DM-Paperclip将每个PDF文件上传到S3。

大多数文件都能成功上传,但有些文件似乎需要数小时,而CPU占用率会保持在100%。我通过在相关部分打印调试语句来定位引起问题的代码行。

 # Takes an array of pdf file paths and uploads each to S3 using dm-paperclip
 def save_pdfs(pdfs_files)
  pdf_files.each do |path|
  pdf = File.open(path)
  ad = Ad.new
  ad.pdf.assign(pdf) # <= Last debug statment is printed before this line
  begin
    ad.save
  rescue => e
    # log error
  ensure
    pdf.close
  end
 end

为了帮助解决问题,当进程卡在100%时,我附加了strace。结果出现了数十万行像这样的代码:
 ...
 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3543, ...}) = 0
 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3543, ...}) = 0
 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3543, ...}) = 0
 ... 500K lines

接着是几千个:

 ...
 brk(0x1224d0000)                        = 0x1224d0000
 brk(0x1224f3000)                        = 0x1224f3000
 brk(0x122514000)                        = 0x122514000
 ...

在不挂起的上传过程中,strace 的输出如下:

 ...
 ppoll([{fd=12, events=POLLOUT}], 1, NULL, NULL, 8) = 1 ([{fd=12, revents=POLLOUT}])
 fstat(12, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
 fcntl(12, F_GETFL)                      = 0x2 (flags O_RDWR)
 write(12, "%PDF-1.3\n%\342\343\317\323\n8 0 obj\n<</Filter"..., 4096) = 4096
 ppoll([{fd=12, events=POLLOUT}], 1, NULL, NULL, 8) = 1 ([{fd=12, revents=POLLOUT}])
 write(12, "S\34\367\23~\277u\272,h\204_\35\215\35\341\347\324\310\307u\370#\364\315\t~^\352\272\26\374"..., 4096) = 4096
 ppoll([{fd=12, events=POLLOUT}], 1, NULL, NULL, 8) = 1 ([{fd=12, revents=POLLOUT}])
 write(12, "\216%\267\2454`\350\177\4\36\315\211\7B\217g\33\217!e\347\207\256\264\245vy\377\304\256\307\375"..., 4096) = 4096
 ...

导致此问题的PDF文件似乎是随机的。它们都是有效的PDF文件,而且它们都相对较小。它们的大小在100KB到50MB左右。

看起来过多的stat系统调用与我的问题有关吗?


2
除非异常是由 ad.save 触发的,否则您的 ensure 块不会在异常发生时被执行。在这种情况下,ad.pdf.assign(pdf) 可能会引发异常,文件将无法关闭。在此之前可能已经发生了几百次,留下了对数百个文件的引用,导致 CPU 使用率达到 100% 的文件。如果您将所有内容都包装在一个块中并将其传递给 File.open,那么您可以确保文件始终正确关闭。根据您处理的文件数量,这可能会显著提高性能。 - Isaac Betesh
对于所有的“下载/上传CPU挂起/内存不足问题”,我强烈建议设置<attachment>_file_size参数(在HTTP中为Content-length头)。 - xerx593
Paperclip是用Rails编写的。Rails是用C语言编写的。Rails调用C语言中的函数来获取时区信息,这些代码你看不到,很可能会导致内存泄漏。Rails有自定义类和方法来处理时区。为了防止这种情况发生,Paperclip应该使用这些方法而不是内置的方法。 - Entree
https://edgeguides.rubyonrails.org/active_support_core_extensions.html#extensions-to-time - Entree
或者以简单的方式检查其中一种方法是否适用于您的操作系统。https://dev59.com/B2445IYBdhLWcg3wydBk - Entree
显示剩余3条评论
2个回答

1

开始使用dm-paperclip已经不再维护,因此您应该考虑使用其他替代方案,如carrierwaveactive_storage(如果您正在使用Rails)来处理文件上传。

根据您提供的信息,过多的stat系统调用可能与您的问题有关。高CPU使用率可能是由于您的Ruby进程反复查询/etc/localtime文件系统。

您可以尝试使用像ruby-prof这样的性能分析工具对Ruby代码进行分析,以确定性能瓶颈出现在哪里。这可能会帮助您更精确地定位问题。


-1

这似乎是一个问题,原始文件权限不正确,导致从源计算机发送的失败文件。所有要保存到服务器的pdf文件都应该在脚本中分配为0644,或者如果脚本在客户端接收时使用原始权限进行发送,则需要分配原始权限。 基本上,由于写入磁盘时文件权限不是0644,因此服务器操作系统和配置会拒绝它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接