我正在构建一个使用文件系统API向Hadoop写入数据的spring-boot供电服务。一些数据被写入parquet文件,大块数据被缓存在内存中,所以当服务关闭时,可能有几百Mb的数据要被写入Hadoop。
FileSystem
默认情况下会自动关闭,所以当服务被关闭时,有时候 FileSystem
在所有的写入器关闭之前就被关闭,导致parquet文件损坏。
有一个 fs.automatic.close
标志 Configuration
但 FileSystem
实例是由多个线程使用的,我不知道有什么干净的方法可以在关闭之前等待它们全部完成。FileSystem
手动关闭。我试着使用一个实现Spring的专用filesysem关闭Bean SmartLifeCycle
以最大 phase
所以它是最后被销毁的,但实际上它并不是最后被销毁的,而是最后被通知关闭的,而其他bean还在关闭的过程中。
理想情况下,每一个对象都需要一个 FileSystem
将得到一个,并负责关闭它。问题是 FileSystem.get(conf)
返回一个缓存的实例。有一个 FileSystem.newInstance(conf)
但目前尚不清楚使用多份文件的后果是什么。FileSystem
实例的性能。这还有一个问题--没有办法通过 FileSystem
例子 ParquetWriter
- 它 得一 使用 path.getFileSystem(conf)
. 人们会认为这一行会返回一个 FileSystem
实例只分配给该文件,但有一个是错误的--很可能是同一个缓存实例。将被退回 所以关闭它是错误的。
有没有一种推荐的方式来管理一个生命周期内的 FileSystem
? 如果一个... FileSystem
创立于 fs.automatic.close
设为 true
而从不手动关闭?也许spring-boot支持一种干净的方式来关闭 FileSystem
在所有其他豆子实际被销毁(不是被销毁)之后?
谢谢!我正在构建一个使用文件系统API向Hadoop写入数据的spring-boot驱动服务。
你可以禁用 FileSystem
缓存中使用 fs.<scheme>.impl.disable.cache
配置 此处,一些讨论 此处),其中 <scheme>
在你的情况下,将是 hdfs
假设你使用的是HDFS)。这将迫使 ParquetWriter
创建一个新的 FileSystem
实例时,它调用 path.getFileSystem(conf)
. 这种配置没有被记录下来是有原因的--虽然在 Hadoop 本身的单元测试中被广泛使用,但在生产系统中使用是非常危险的。为了回答您关于性能的问题,假设您使用的是 HDFS,那么每一个 FileSystem
实例将创建一个单独的TCP连接到HDFS NameNode。应用程序和库代码在编写时,通常会假设调用像 path.getFileSystem(conf)
和 FileSystem.get(conf)
是便宜且轻量级的,所以被频繁使用。在生产系统中,我见过一个客户端系统DDoS了一个NameNode服务器,因为它禁用了缓存。你需要仔细管理生命周期,不仅要管理好 FileSystem
实例 您的代码 创建的,但也包括你使用的库所创建的。我一般建议不要这样做。
听起来,这个问题确实是来自于 JVM关机钩子 和Hadoop所采用的机制,即用于自动关闭 FileSystem
实例。Hadoop包括自己的 关机管理器 用于在关机期间对事件进行排序。FileSystem
shutdown被特意放在最后,这样其他的shutdown钩子(比如MapReduce任务后的清理)可以先完成。但是,Hadoop的 ShutdownHookManager
只知道已注册的关机任务,所以它不会知道Spring的生命周期管理。听起来确实像利用Spring的关机序列和利用 fs.automatic.close=false
可能适合您的应用;我没有Spring的经验,所以在这方面我无法帮助您。您也可以将Spring的整个关闭序列注册到Hadoop的 ShutdownHookManager
,使用很高的优先级来确保Spring的关机序列在关机队列中是第一位的。
具体回答一下这部分问题。
有没有一个推荐的方法来管理一个文件系统的生命周期?
推荐的方式一般是不管理,让系统来帮你管理。只要你想自己管理,就会有龙,所以要谨慎进行。