【发布时间】:2020-11-07 00:45:18
【问题描述】:
我们的处理服务器主要与 TempFiles 一起使用,因为它使我们的事情变得更容易:无需担心删除它们,因为它们会被垃圾收集或处理名称冲突等。
最近,我们遇到了 TempFiles 在这个过程中过早地被 GCed 的问题。尤其是我们的一项服务,它将 Foo 文件从 url 转换为一些 Bar 文件并将其上传到我们的服务器。
为了清楚起见,我在下面添加了一个案例场景,以便于讨论并提供一个示例。
此工作流程执行以下操作:
- 获取一个url作为参数
- 将 Foo 文件下载为 TempFile
- 将其复制到新的 TempFile
- 将相关资产下载到 TempFiles
- 将相关资产链接到本地 dup TempFile
- 将 Foo 转换为 Bar 格式
- 上传到我们的服务器
有时转换失败,一切都表明我们的本地 Foo 文件指向在转换之前已创建和 GC 的相关资产。
我的两个问题:
-
我的 TempFiles 是否有可能过早地被 GCed?我阅读了有关 Ruby GCed 系统的信息,避免这些情况非常保守。
-
如何避免这种情况发生?我可以尝试从
download_and_replace_uri(node)保存所有相关资产,并将它们作为回报传递,以在ConvertService的实例仍然存在时使其保持活力。但我不确定这是否会解决它。
myfile.foo
{
"buffers": [
{ "uri": "http://example.com/any_file.jpg" },
{ "uri": "http://example.com/any_file.png" },
{ "uri": "http://example.com/any_file.jpmp3" }
]
}
main.rb
ConvertService.new('http://example.com/myfile.foo')
转换服务
class ConvertService
def initialize(url)
@url = url
@bar_file = Tempfile.new
end
def call
import_foo
convert_foo
upload_bar
end
private
def import_foo
@foo_file = ImportService.new(@url).call.edited_file
end
def convert_foo
`create-bar "#{@foo_file.path}" "#{@bar_file.path}"`
end
def upload_bar
UploadBarService.new(@bar_file).call
end
end
导入服务
class ImportService
def initialize(url)
@url = url
@edited_file ||= Tempfile.new
end
def call
download
duplicate
replace
end
private
def download
@original = DownloadFileService.new(@url).call.file
end
def duplicate
FileUtils.cp(@original.path, @edited_file.path)
end
def replace
file = File.read(@edited_file.path)
json = JSON.parse(file, symbolize_names: true)
json[:buffers]&.each do |node|
node[:uri] = DownloadFileService.new(node[:uri]).call.file.path
end
write_to_disk(@edited_file.path, json.to_json)
end
end
下载文件服务
module Helper
class DownloadFileService < ApplicationHelperService
def initialize(url)
@url = url
@file = Tempfile.new
end
def call
uri = URI.parse(@url)
Net::HTTP.start(
uri.host,
uri.port,
use_ssl: uri.scheme == 'https'
) do |http|
response = http.request(Net::HTTP::Get.new(uri.path))
@file.binmode
@file.write(response.body)
@file.flush
end
end
end
end
UploadBarService
module Helper
class UploadBarService < ApplicationHelperService
def initialize(file)
@file = file
end
def call
HTTParty.post('http://example.com/upload', body: { file: @file })
# NOTE: End points returns the url for the uploaded file
end
end
end
【问题讨论】:
-
请花点时间阅读minimal reproducible example。您实际上已将整个应用程序上传到此处,并希望有人对其进行解析并为您解决问题。通过自己做一些工作来大幅减少这种情况,您更有可能获得帮助。您提供的代码甚至无法由任何人运行,因为它缺少像
ApplicationHelperService这样的部分,即使存在,它仍然无法使用,因为它取决于我们没有的其他数据。跨度> -
也许this discussion 会有所帮助?
-
@anothermh 代码的目标是让人们对结构有所了解。这实际上不可能提供开箱即用的东西,因为(如果你会查看消息)将需要设置上传服务器和其他超出 ruby 范围的机制以及 TempFile 的工作方式......
-
@lacostenycoder 很棒的链接!它指向的链接说得更多(简而言之)。如果没有正在运行的代码指向 TempFiles,则它会被 GCed。
ImportService#replace中的代码通过DownloadFileService获取 TempFiles 并使用它的路径,而不在内存中保存对它的引用。所以当这个实例被 GCed 时,对 TempFile 的引用(即使仍在其他 TempFiles 上使用)被破坏。为了避免这种情况,在ImportService中添加一个类数组并将 TempFile 实例附加到它的全局应该就足够了,因为我的ImportService将持续到ConvertService结束
标签: ruby garbage-collection temporary-files