基于MapReduce的Hadoop KNN在Cloudera环境运行失败求助
这个问题我之前帮不少开发者排查过,本质是MapReduce分布式缓存的配置和文件访问逻辑没匹配上——咱们一步步来拆解解决:
1. 先搞懂报错的核心原因
你的报错java.io.FileNotFoundException: File does not exist: /KnnParams.txt#knnParamFile,结合代码里的FileUtils.readFileToString(new File("./knnParamFile")),其实暴露了两个关键问题:
- 你尝试通过分布式缓存绑定HDFS文件
/KnnParams.txt到别名knnParamFile,但这个绑定要么没配置对,要么文件根本没上传到HDFS的对应路径; - 任务节点上直接用本地文件路径去读缓存文件,这不符合MapReduce的分布式文件访问逻辑。
2. 确认分布式缓存的配置是否正确
MapReduce任务的节点没法直接访问主节点的本地文件,必须通过分布式缓存把文件上传到HDFS并分发给每个任务节点。你得检查代码里的缓存配置是否符合API要求:
- 如果是Hadoop 1.x或旧版API:
// 必须在提交Job前执行! DistributedCache.addCacheFile(new URI("/KnnParams.txt#knnParamFile"), job.getConfiguration()); - 如果是Hadoop 2.x+的新版API:
先确认HDFS上确实存在// 同样要在Job提交前配置 job.addCacheFile(new URI("/KnnParams.txt#knnParamFile"));/KnnParams.txt,用命令hdfs dfs -ls /就能验证。如果文件不存在,先上传:hdfs dfs -put ./local/KnnParams.txt /
3. 修正任务节点上的文件读取逻辑
代码里的new File("./knnParamFile")是读本地磁盘的相对路径,但分布式缓存的文件是放在任务的工作目录下的,而且更可靠的方式是通过HDFS API读取:
- 替换掉你的FileUtils读取代码,改成用Map/Reduce任务的Context获取配置后读取:
如果你非要用FileUtils,先打印当前工作目录确认文件位置(调试用):Configuration conf = context.getConfiguration(); FileSystem fs = FileSystem.get(conf); // 直接用缓存文件的别名 Path cachePath = new Path("knnParamFile"); BufferedReader reader = new BufferedReader(new InputStreamReader(fs.open(cachePath))); // 读取文件内容逻辑...
但这种方式不如HDFS API稳定,因为不同集群的工作目录可能有差异。System.out.println("Current task working dir: " + new File(".").getAbsolutePath());
4. 检查缓存配置的时机
如果你的缓存配置是在job.submit()或者job.waitForCompletion(true)之后才加的,那肯定不会生效——必须确保缓存配置是在Job提交之前完成的,顺序不能搞反。
5. 验证缓存是否被正确分发
你可以查看Map/Reduce任务的日志,搜索DistributedCache关键词,看是否有“Adding file to distributed cache”之类的成功记录。另外,也可以登录任务节点,到临时目录(通常是/tmp/hadoop-<用户名>/mapred/local/taskTracker/<任务ID>/work)下查看是否有knnParamFile的符号链接。
内容的提问来源于stack exchange,提问作者Asad Khan

