Apache Nutch以job文件集群运行时无法读取自定义配置文件问题
问题背景
我已完成Apache Nutch 1.x的网页爬取相关配置,根据业务需求,需要为每个被索引的域名向Solr文档添加额外信息,该部分配置存储在JSON文件中。我基于index-basic插件完成了对应功能开发,在本地模式下测试运行成功,插件初始化代码片段如下:
this.enable_extra_domain = conf.getBoolean("domain.extraInfo.enable", false); if (this.enable_extra_domain) { String domainExtraInfo = conf.get("domain.extraInfo.file","conf/domain-extra.json"); readDomainFile(domainExtraInfo); LOG.info("domain.extraInfo.enable is enabled. Using " + domainExtraInfo + " for input."); } else { LOG.info("domain.extraInfo.enable is disabled."); }
负责读取配置文件的函数实现如下:
private void readDomainFile(String domainExtraInfo) { // Instance of our Domain map with extra info website_records = new HashMap<String, List<Object>>(); JSONParser jsonParser = new JSONParser(); try (FileReader reader = new FileReader(domainExtraInfo)) { Object obj = jsonParser.parse(reader); JSONArray DomainList = (JSONArray) obj; DomainList.forEach( domain -> parseDomainObject( (JSONObject) domain ) ); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } }
上述代码在本地模式下运行完全正常,但当我将Nutch打包为.job文件提交至EMR(或其他Hadoop集群)运行时,抛出java.io.FileNotFoundException异常。本地运行时新增的配置文件存放在conf目录下,集群部署时该配置文件已被打包进.job文件中。
问题根因
核心问题是FileReader只能读取本地文件系统上的物理磁盘文件,无法读取JAR包(Nutch生成的.job文件本质是可执行Hadoop作业JAR)内部的classpath资源。
本地运行时,Nutch直接从项目本地磁盘的conf目录读取实体文件,路径匹配所以可以正常加载;提交到集群运行时,.job文件会被分发到各个计算节点的工作目录,代码中写的相对路径conf/domain-extra.json会被解析为节点本地磁盘上的路径,而非JAR包内部的资源路径——哪怕文件确实被打进了JAR包,FileReader也无法穿透JAR包读取内部文件,因此抛出文件不存在异常。
解决方案
你可以根据自身业务场景选择以下任意一种方案解决:
- 方案1:改用Classpath读取资源(最推荐,无需改动现有部署流程)
替换readDomainFile方法中的文件读取逻辑,通过类加载器获取JAR包内的资源流,不再依赖本地文件路径读取,修改后的代码示例:
注意:使用该方式时请确认打包后JSON文件在JAR包内的相对路径和private void readDomainFile(String domainExtraInfo) { website_records = new HashMap<String, List<Object>>(); JSONParser jsonParser = new JSONParser(); // 从classpath读取资源流,兼容本地运行和集群JAR包运行场景 try (InputStream is = getClass().getClassLoader().getResourceAsStream(domainExtraInfo)) { if (is == null) { throw new RuntimeException("Domain extra config file not found in classpath: " + domainExtraInfo); } try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { Object obj = jsonParser.parse(reader); JSONArray DomainList = (JSONArray) obj; DomainList.forEach( domain -> parseDomainObject( (JSONObject) domain ) ); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Failed to load domain extra config", e); } }domain.extraInfo.file配置值一致:如果文件打在JAR包的conf/目录下,保留原有conf/domain-extra.json配置即可;如果文件被打在JAR包根目录,将配置值改为domain-extra.json即可。 - 方案2:通过Hadoop分布式缓存分发配置文件
如果后续需要频繁修改JSON配置、不想每次调整配置都重新打包Nutch作业文件,可以在提交作业时将配置文件加入分布式缓存:- 将
domain-extra.json上传到HDFS固定路径 - 提交Nutch作业时添加分布式缓存参数,将HDFS上的配置文件分发到所有计算节点的本地工作目录
- 代码中直接读取缓存到本地的文件即可,无需修改原有文件读取逻辑,但需要额外配置作业提交参数
- 将
- 方案3:将配置直接写入Nutch配置文件
如果JSON配置内容较小且基本不会变动,可以直接将配置内容转义后写入nutch-site.xml,代码中直接从Nutch的Configuration对象读取配置值解析,完全不需要依赖独立文件,稳定性最高但灵活性较差。
内容的提问来源于stack exchange,提问作者Hafiz Muhammad Shafiq
相关产品推荐
相关产品推荐

