eclipse/intellij idea 远程调试hadoop 2.6.0
很多hadoop初学者估计都我一样,由于没有足够的机器资源,只能在虚拟机里弄一个linux安装hadoop的伪分布,然后在host机上win7里使用eclipse或intellj idea来写代码测试,那么问题来了,win7下的eclipse或intellij idea如何远程提交map/reduce任务到远程hadoop,并断点调试?
一、准备工作
1.1 在win7中,找一个目录,解压hadoop-2.6.0,本文中是d:\yangjm\code\study\hadoop\hadoop-2.6.0 (以下用$hadoop_home表示)
1.2 在win7中添加几个环境变量
hadoop_home=d:\yangjm\code\study\hadoop\hadoop-2.6.0
hadoop_bin_path=%hadoop_home%\bin
hadoop_prefix=d:\yangjm\code\study\hadoop\hadoop-2.6.0
另外,path变量在最后追加;%hadoop_home%\bin
二、eclipse远程调试
1.1 下载hadoop-eclipse-plugin插件
hadoop-eclipse-plugin是一个专门用于eclipse的hadoop插件,可以直接在ide环境中查看hdfs的目录和文件内容。其源代码托管于github上,官网地址是
有兴趣的可以自己下载源码编译,百度一下n多文章,但如果只是使用 这里已经提供了各种编译好的版本,直接用就行,将下载后的hadoop-eclipse-plugin-2.6.0.jar复制到eclipse/plugins目录下,然后重启eclipse就完事了
1.2 下载windows64位平台的hadoop2.6插件包(hadoop.dll,winutils.exe)
在hadoop2.6.0源码的hadoop-common-project\hadoop-common\src\main\winutils下,有一个vs.net工程,编译这个工程可以得到这一堆文件,输出的文件中,
hadoop.dll、winutils.exe 这二个最有用,将winutils.exe复制到$hadoop_home\bin目录,将hadoop.dll复制到%windir%\system32目录 (主要是防止插件报各种莫名错误,比如空对象引用啥的)
注:如果不想编译,可直接下载编译好的文件 hadoop2.6(x64)v0.2.rar
1.3 配置hadoop-eclipse-plugin插件
启动eclipse,windows->show view->other
window->preferences->hadoop map/reduce 指定win7上的hadoop根目录(即:$hadoop_home)
然后在map/reduce locations 面板中,点击小象图标
添加一个location
这个界面灰常重要,解释一下几个参数:
location name 这里就是起个名字,随便起
map/reduce(v2) master host 这里就是虚拟机里hadoop master对应的ip地址,下面的端口对应 hdfs-site.xml里dfs.datanode.ipc.address属性所指定的端口
dfs master port: 这里的端口,对应core-site.xml里fs.defaultfs所指定的端口
最后的user name要跟虚拟机里运行hadoop的用户名一致,我是用hadoop身份安装运行hadoop 2.6.0的,所以这里填写hadoop,如果你是用root安装的,相应的改成root
这些参数指定好以后,点击finish,eclipse就知道如何去连接hadoop了,一切顺利的话,在project explorer面板中,就能看到hdfs里的目录和文件了
可以在文件上右击,选择删除试下,通常第一次是不成功的,会提示一堆东西,大意是权限不足之类,原因是当前的win7登录用户不是虚拟机里hadoop的运行用户,解决办法有很多,比如你可以在win7上新建一个hadoop的管理员用户,然后切换成hadoop登录win7,再使用eclipse开发,但是这样太烦,最简单的办法:
hdfs-site.xml里添加
<property> <name>dfs.permissions</name> <value>false</value> </property>
然后在虚拟机里,运行hadoop dfsadmin -safemode leave
保险起见,再来一个 hadoop fs -chmod 777 /
总而言之,就是彻底把hadoop的安全检测关掉(学习阶段不需要这些,正式生产上时,不要这么干),最后重启hadoop,再到eclipse里,重复刚才的删除文件操作试下,应该可以了。
1.4 创建woldcount示例项目
新建一个项目,选择map/reduce project
后面的next就行了,然后放一上wodcount.java,代码如下:
package yjmyzz; import java.io.ioexception; import java.util.stringtokenizer; import org.apache.hadoop.conf.configuration; import org.apache.hadoop.fs.path; import org.apache.hadoop.io.intwritable; import org.apache.hadoop.io.text; import org.apache.hadoop.mapreduce.job; import org.apache.hadoop.mapreduce.mapper; import org.apache.hadoop.mapreduce.reducer; import org.apache.hadoop.mapreduce.lib.input.fileinputformat; import org.apache.hadoop.mapreduce.lib.output.fileoutputformat; import org.apache.hadoop.util.genericoptionsparser; public class wordcount { public static class tokenizermapper extends mapper<object, text, text, intwritable> { private final static intwritable one = new intwritable(1); private text word = new text(); public void map(object key, text value, context context) throws ioexception, interruptedexception { stringtokenizer itr = new stringtokenizer(value.tostring()); while (itr.hasmoretokens()) { word.set(itr.nexttoken()); context.write(word, one); } } } public static class intsumreducer extends reducer<text, intwritable, text, intwritable> { private intwritable result = new intwritable(); public void reduce(text key, iterable<intwritable> values, context context) throws ioexception, interruptedexception { int sum = 0; for (intwritable val : values) { sum += val.get(); } result.set(sum); context.write(key, result); } } public static void main(string[] args) throws exception { configuration conf = new configuration(); string[] otherargs = new genericoptionsparser(conf, args).getremainingargs(); if (otherargs.length < 2) { system.err.println("usage: wordcount <in> [<in>...] <out>"); system.exit(2); } job job = job.getinstance(conf, "word count"); job.setjarbyclass(wordcount.class); job.setmapperclass(tokenizermapper.class); job.setcombinerclass(intsumreducer.class); job.setreducerclass(intsumreducer.class); job.setoutputkeyclass(text.class); job.setoutputvalueclass(intwritable.class); for (int i = 0; i < otherargs.length - 1; ++i) { fileinputformat.addinputpath(job, new path(otherargs[i])); } fileoutputformat.setoutputpath(job, new path(otherargs[otherargs.length - 1])); system.exit(job.waitforcompletion(true) ? 0 : 1); } }
然后再放一个log4j.properties,内容如下:(为了方便运行起来后,查看各种输出)
log4j.rootlogger=info, stdout #log4j.logger.org.springframework=info #log4j.logger.org.apache.activemq=info #log4j.logger.org.apache.activemq.spring=warn #log4j.logger.org.apache.activemq.store.journal=info #log4j.logger.org.activeio.journal=info log4j.appender.stdout=org.apache.log4j.consoleappender log4j.appender.stdout.layout=org.apache.log4j.patternlayout log4j.appender.stdout.layout.conversionpattern=%d{absolute} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32c %4l | %m%n
最终的目录结构如下:
然后可以run了,当然是不会成功的,因为没给wordcount输入参数,参考下图:
1.5 设置运行参数
因为wordcount是输入一个文件用于统计单词字,然后输出到另一个文件夹下,所以给二个参数,参考上图,在program arguments里,输入
hdfs://172.28.20.xxx:9000/jimmy/input/readme.txt
hdfs://172.28.20.xxx:9000/jimmy/output/
大家参考这个改一下(主要是把ip换成自己虚拟机里的ip),注意的是,如果input/readm.txt文件没有,请先手动上传,然后/output/ 必须是不存在的,否则程序运行到最后,发现目标目录存在,也会报错,这个弄完后,可以在适当的位置打个断点,终于可以调试了:
三、intellij idea 远程调试hadoop
3.1 创建一个maven的wordcount项目
pom文件如下:
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <groupid>yjmyzz</groupid> <artifactid>mapreduce-helloworld</artifactid> <version>1.0-snapshot</version> <dependencies> <dependency> <groupid>org.apache.hadoop</groupid> <artifactid>hadoop-common</artifactid> <version>2.6.0</version> </dependency> <dependency> <groupid>org.apache.hadoop</groupid> <artifactid>hadoop-mapreduce-client-jobclient</artifactid> <version>2.6.0</version> </dependency> <dependency> <groupid>commons-cli</groupid> <artifactid>commons-cli</artifactid> <version>1.2</version> </dependency> </dependencies> <build> <finalname>${project.artifactid}</finalname> </build> </project>
项目结构如下:
项目上右击-》open module settings 或按f12,打开模块属性
添加依赖的libary引用
然后把$hadoop_home下的对应包全导进来
导入的libary可以起个名称,比如hadoop2.6
3.2 设置运行参数
注意二个地方:
1是program aguments,这里跟eclipes类似的做法,指定输入文件和输出文件夹
2是working directory,即工作目录,指定为$hadoop_home所在目录
然后就可以调试了
intellij下唯一不爽的,由于没有类似eclipse的hadoop插件,每次运行完wordcount,下次再要运行时,只能手动命令行删除output目录,再行调试。为了解决这个问题,可以将wordcount代码改进一下,在运行前先删除output目录,见下面的代码:
package yjmyzz; import java.io.ioexception; import java.util.stringtokenizer; import org.apache.hadoop.conf.configuration; import org.apache.hadoop.fs.filesystem; import org.apache.hadoop.fs.path; import org.apache.hadoop.io.intwritable; import org.apache.hadoop.io.text; import org.apache.hadoop.mapreduce.job; import org.apache.hadoop.mapreduce.mapper; import org.apache.hadoop.mapreduce.reducer; import org.apache.hadoop.mapreduce.lib.input.fileinputformat; import org.apache.hadoop.mapreduce.lib.output.fileoutputformat; import org.apache.hadoop.util.genericoptionsparser; public class wordcount { public static class tokenizermapper extends mapper<object, text, text, intwritable> { private final static intwritable one = new intwritable(1); private text word = new text(); public void map(object key, text value, context context) throws ioexception, interruptedexception { stringtokenizer itr = new stringtokenizer(value.tostring()); while (itr.hasmoretokens()) { word.set(itr.nexttoken()); context.write(word, one); } } } public static class intsumreducer extends reducer<text, intwritable, text, intwritable> { private intwritable result = new intwritable(); public void reduce(text key, iterable<intwritable> values, context context) throws ioexception, interruptedexception { int sum = 0; for (intwritable val : values) { sum += val.get(); } result.set(sum); context.write(key, result); } } /** * 删除指定目录 * * @param conf * @param dirpath * @throws ioexception */ private static void deletedir(configuration conf, string dirpath) throws ioexception { filesystem fs = filesystem.get(conf); path targetpath = new path(dirpath); if (fs.exists(targetpath)) { boolean delresult = fs.delete(targetpath, true); if (delresult) { system.out.println(targetpath + " has been deleted sucessfullly."); } else { system.out.println(targetpath + " deletion failed."); } } } public static void main(string[] args) throws exception { configuration conf = new configuration(); string[] otherargs = new genericoptionsparser(conf, args).getremainingargs(); if (otherargs.length < 2) { system.err.println("usage: wordcount <in> [<in>...] <out>"); system.exit(2); } //先删除output目录 deletedir(conf, otherargs[otherargs.length - 1]); job job = job.getinstance(conf, "word count"); job.setjarbyclass(wordcount.class); job.setmapperclass(tokenizermapper.class); job.setcombinerclass(intsumreducer.class); job.setreducerclass(intsumreducer.class); job.setoutputkeyclass(text.class); job.setoutputvalueclass(intwritable.class); for (int i = 0; i < otherargs.length - 1; ++i) { fileinputformat.addinputpath(job, new path(otherargs[i])); } fileoutputformat.setoutputpath(job, new path(otherargs[otherargs.length - 1])); system.exit(job.waitforcompletion(true) ? 0 : 1); } }
但是光这样还不够,在ide环境中运行时,ide需要知道去连哪一个hdfs实例(就好象在db开发中,需要在配置xml中指定datasource一样的道理),将$hadoop_home\etc\hadoop下的core-site.xml,复制到resouces目录下,类似下面这样:
里面的内容如下:
<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>fs.defaultfs</name> <value>hdfs://172.28.20.***:9000</value> </property> </configuration>
上面的ip换成虚拟机里的ip即可。
推荐阅读
-
eclipse/intellij idea 远程调试hadoop 2.6.0
-
详述IntelliJ IDEA远程调试Tomcat的方法(图文)
-
Intellij IDEA基于Springboot的远程调试
-
Intellij idea远程debug连接tomcat实现单步调试
-
Intellij idea远程debug连接tomcat实现单步调试
-
详述IntelliJ IDEA远程调试Tomcat的方法(图文)
-
IntelliJ IDEA远程调试运行中的JAVA程序/项目
-
使用windows下的Eclipse或者IDEA远程连接Linux的Hadoop并运行wordcount
-
Hadoop 使用IntelliJ IDEA 进行远程调试代码的配置方法
-
Windows下IntelliJ IDEA远程连接服务器中Hadoop运行WordCount(详细版)