我正在构建一个由弹簧启动的服务,该服务使用文件系统API将数据写入Hadoop。一些数据被写入镶木地板文件,大块被缓存在内存中,因此当服务关闭时,可能必须将数百Mb的数据写入Hadoop。
FileSystem
默认情况下自动关闭,因此,当服务关闭时,有时FileSystem
会在所有编写器都关闭之前关闭,从而导致镶木地板文件损坏。
文件系统fs.automatic.close
中有Configuration
标志,但是FileSystem
实例是从多个线程中使用的,我不知道有什么干净的方法可以在关闭{ {1}}手动。我尝试使用专用的filesysem关闭bean,它使用最大FileSystem
来实现Spring SmartLifeCycle
,因此它最后被销毁了,但是实际上它没有被销毁,但是最后被通知了shutdown,而其他bean仍在关闭过程中下来。
理想情况下,每个需要phase
的对象都将得到一个并负责关闭它。问题是FileSystem
返回了一个缓存的实例。有FileSystem.get(conf)
,但尚不清楚在性能上使用多个FileSystem.newInstance(conf)
实例会带来什么后果。另一个问题是-无法将FileSystem
实例传递给FileSystem
-使用ParquetWriter
将gets one传递给would be returned。有人会认为该行将仅返回分配给该文件的path.getFileSystem(conf)
实例,但是会出错-最有可能是同一缓存的实例{{3}},因此关闭它是错误的。
是否有推荐的方法来管理FileSystem
的生命周期?如果将FileSystem
设置为FileSystem
且从未手动关闭过fs.automatic.close
,该怎么办?也许spring-boot支持一种干净的方法来在所有其他bean实际上都被销毁(未销毁)之后关闭true
?
谢谢!
答案 0 :(得分:1)
您可以使用FileSystem
配置(发现here,一些讨论here)来禁用fs.<scheme>.impl.disable.cache
缓存,其中<scheme>
将是{ {1}}(假设您正在使用HDFS)。这将迫使hdfs
在调用ParquetWriter
时创建一个新的FileSystem
实例。该配置没有充分的理由证明-虽然在Hadoop本身内部的单元测试中广泛使用了此配置,但在生产系统中使用它可能非常危险。为了回答有关性能的问题,假设您正在使用HDFS,则每个path.getFileSystem(conf)
实例都会创建一个与HDFS NameNode的单独TCP连接。应用程序和库代码通常是在假设FileSystem
和path.getFileSystem(conf)
之类的调用既便宜又轻巧的前提下编写的,因此经常使用它们。在生产系统中,我已经看到客户端系统DDoS一个NameNode服务器,因为它禁用了缓存。您不仅需要仔细管理{em>您的代码创建的FileSystem.get(conf)
实例的生命周期,而且还需要仔细管理由您使用的库创建的实例的生命周期。我通常会建议不要这样做。
听起来问题似乎真的来自于Spring使用的JVM shutdown hooks与Hadoop使用的错误交互,而Hadoop是用于自动关闭FileSystem
实例的机制。 Hadoop包含自己的ShutdownHookManager,用于在关机期间对事件进行排序; FileSystem
关机是有目的地进行的,以便可以首先完成其他关机钩子(例如,在MapReduce任务之后进行清理)。但是,Hadoop的FileSystem
仅知道已向其注册的关闭任务,因此它不会知道Spring的生命周期管理。听起来确实像利用Spring的关闭顺序和利用ShutdownHookManager
可能最适合您的应用程序;我没有Spring的经验,所以在这方面我无法为您提供帮助。您还可以使用非常高的优先级向Hadoop的fs.automatic.close=false
注册Spring的整个关闭序列,以确保Spring的关闭序列在关闭队列中排在首位。
要专门回答此部分:
是否存在建议的管理文件系统生命周期的方法?
推荐的方法通常是不对其进行管理,而由系统为您完成。每当您尝试自己进行管理时,都会碰到龙,因此请谨慎操作。