HadoopMapReduce代码分析GrepExample(二)

run方法先进行命令行参数校验,没什么好看的。 接下来创建临时文件,用的是Path类,目前也没什么好看的。 接下来获取Configuration,用的实现的接口的getConf(),在之前ToolRunner中已经set了一个了,这个Configuration很简单,直接new出来的,loadDefaults为true。 (Context和conf是不一样的两个东西) conf目前看起来是简单的map存放各种配置,里面会有些对过期的key的检查与设置。 接下来创建了一个Job,job里面用了深拷贝将conf里的配置拷贝给自己,这样job中随便怎么修改conf也不会影响外部,深拷贝将conf变成了JobConf。 然后在Job中set需要执行的类,这次就是Grep.class,我觉得这一步非常关键,好好学一下。 进去之后就是先找jar包,好像确实就是找jar,最后返回的是file:这种通用文件定位格式的字符串。 后面的设置mapper,reducer,combiner class没什么复杂的,就是把对应的class保存起来。 重头戏应该是waitForCompletion,这个函数就是将job提交到cluster。 Job一共就只有两种状态,DEFINE,RUNNING,如果是DEFINE那就提交。粗看了一下,这里的wait是提交上去之后立即返回,然后不断轮询状态,要是轮询失败了就抛异常。看看submit()函数里具体做了什么。 好吧,submit这里就有点看不懂了,好像做了一些mock,得debug进去看一看

February 23, 2023 · 1 min

HadoopMapReduce代码分析GrepExample(一)

Grep程序在谷歌的MapReduce论文中也作为示例程序提到过,在大规模数据集中并行找出符合指定模式的文件。 https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html 按照官方指南,只需要在hadoop安装目录下用hadoop程序就可以作为单机来跑hadoop 按照官方的指南,跑一个简单的grep程序,具体的jar包得看使用的自己使用的版本 $ mkdir input $ cp etc/hadoop/*.xml input $ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.0-SNAPSHOT.jar grep input output 'dfs[a-z.]+' $ cat output/* 启动jvm的调试功能,只需要在hadoop-env.sh中添加 export HADOOP_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" (这部分是jvm调试相关) 在idea中打上断点,通过查看haoop-mapreduce-examples模块中的pom文件,可以看到在打包插件里指定了org.apache.hadoop.examples.ExampleDriver类为启动类。 那么就从这个类开始分析吧。 ExampleDriver这个类是用来将各种测试类注册进去并加上一些文字性描述,能看到自带了很多实例程序,我这次分析的是grep程序,对应注册的是Grep类。 ExampleDrive类中使用ProgramDriver类,这个ProgramDriver中使用一个map保存测试类和他们对应的名字关系,而测试类在ProgramDriver中被解析成ProgramDescription,ProgramDescription中保存了测试类的main方法和描述,然后测试时,比如我们的grep程序,先用grep这个名字找到对应的ProgramDescription然后直接调用main方法把命令行中的剩余参数传入。 接下来看一下Grep中的main方法 main方法中用了一个ToolRunner去run,run方法中提供了Configuration类,Grep类,和参数,能猜到应该是使用了默认的配置项去调用Grep,能看到这里的核心是一个Tool接口,Grep继承了Tool接口,ToolRunner会运行Tool。 根据javadoc看看Tool是什么 看样子hadoop为了方便测试写的一个接口,把一些环境的初始化配置隐藏起来了。 那来看看Configuration是什么,这个应该是核心类 这个Configuration就负责加载core-site那些配置文件已经用户程序中设定的配置,具体的配置目前应该没必要深究。可以看到使用默认配置是将loadDefault这个bool值设为false,然后往一个WeakHashMap中put一个key为自己,value为null的键值对。 第一次看到WeakHashMap,学一下。 https://web.archive.org/web/20061130103858/http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html 这里使用的原因,应该是为了没有地方继续使用Configuration时可以让垃圾回收机自动回收这个entry,Configuration应该是一个比较大的对象。有几个问题,第一,什么时候Configuration会不再有对象引用呢?第二,这里的map中会存放哪些Configuration呢?第三,是通过把各种不同路径的C configuration都存在这里然后只用其中的部分,剩下的部分如果一直不使用就让垃圾回收器回收吗? 现在可以进入ToolRunner方法中去看了 一上来就是CallerContext.getCurrent(),经常看到一些这种getCurrent的操作,今天仔细盘一下。 进去之后调用的是CurrentCallerContextHolder.CALLER_CONTEXT.get() 类描述里贴心的给了链接https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom 学一下,原来这就是线程安全的单例的使用案例,利用jvm初始化类的特性。 不过这个单例有点变种,返回的是ThreadLocal变量,这部分内容我还是有点生疏。 学习一下ThreadLocal 我现在理解的是ThreadLocal给每个线程属于自己的对象, ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的;ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递;https://www.liaoxuefeng.com/wiki/1252599548343744/1306581251653666 而inheritableThreadLocal会让每个子进程继承父进程的threadlocal CallerContext是一个单例,使用线程安全的单例模式,CALLER_CONTEXT这个线程变量就是实例,那就是说CallerContext在每个线程中只存放一份。 “单例”模式-ThreadLocal线程单例 再回到ToolRunner的run方法,通过getCurrent()获取当前线程的CallerContext,第一次应该没有(因为没有写initValue方法),所以手动构造一个CallerContext。CallerContext中还有个建造者,使用建造者模式build出CallerContext。 这个builder构造函数目前看起来就是简单的把context存起来,当然前面做了一些简单的合法判断。 最终build时设置了context和signature两个变量,目前不是很清楚是干什么的。 好,现在已经有了线程的CallderContext了,接下来是一个CommonAuditContext.noteRntryPoint(tool), 这个函数将类和一个PATAM_COMMAND(字符串)放到一个全局的map中去了,我猜这个应该是开启审计功能的开关?后面审计部分可能会看那个全局表。 然后是再判断conf是不是null,是就再创建一个新的。感觉有点冗余。 接下来创建了一个GenericOptionsParser,这个类是用来解析命令行参数的,其实感觉像开源项目中这种解析命令行参数的类还是值得看得,不然每次命令行参数都用的云里雾里的。里面用了另一个开源的参数解析包CLI,就不细看了。 Grep类继承了Configured,而Configured又实现了COnfigurable接口,所以有setConf()以及Configuration,run方法中把生成的conf给set进了tool自己 set the configuration back, so that Tool can configure itself...

February 21, 2023 · 1 min

HadoopMapReduce学习实况(1)

MapReduce是一个编程模型,基本思想是: 用户编写一个Map程序,这个Map程序的输入是一个key/value pair,输出也是一个key/value pair。(当然也能是一组,这就是具体实现的区别,下文会看谷歌的具体实现) 用户再编写一个Reduce程序,这个Reduce程序的输入是先前Map程序的输出,这个Reduce程序将先前Map程序输出的所有key/value pair按照key进行merge。 这个思想很简单,但是在实践中想要用这种编程方式并行处理大数据需要处理一些问题。 输入的数据进如何进行分区 每一个程序该在哪台机器上执行 集群中有机器坏了如何处理 机器之间如何传递数据 这些问题都需要解决,并且要高效,要让程序尽可能的快 谷歌把这些问题解决了,然后发表了这篇论文,厉害之处在于他们把上述复杂的问题解决了(还保证程序能跑的很快),给开发者暴露出简单的接口,这样开发者只需要专注于程序的逻辑,不需要考虑分布式运行时出现的各种问题。 谷歌的MapReduce编程模型 用户写的Map程序接受一个key/value pair,输出一组key/value pairs。 MapReduce Library将这一组key/value pairs按照key进行merge,然后将结果传给用户写的Reduce程序(为了防止OOM,Reduce通过迭代器模式获取这些结果)。 用户写的Reduce程序接受一个key和这个key对应的一堆values,然后把这一堆values变成一小堆values,这一小堆values的数目通常是1或者0。 再明确一下MapReduce只是一种编程模型,在不同的场合下可以有不同的实现

February 17, 2023 · 1 min

Hadoop源码编译阅读

mvn test -Dtest=TestLongLong 在跑测试的时候,出现了如下问题 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.7:run (common-test-bats-driver) on project hadoop-common: An Ant BuildException has occured: exec returned: 1 [ERROR] around Ant part …… @ 4:69 in /home/chuhuilong/hadoop/hadoop-common-project/hadoop-common/target/antrun/build-main.xml 根据错误提示,是hadoop-common这个模块中的antrun插件有问题,去该模块的pom中查看, 是执行这个execution的时候出错了。 拿着common test bats driver关键词去hadoop官方的社区里搜索 https://issues.apache.org/jira/browse/HADOOP-14618 这个问题和我的出错一样,hadoop版本是3.0.0也很接近,只有一条评论说是running the build as a non-root user,以非root用户权限执行之前在网上看别人调试hadoop2.0版本的时候强调过,但是我是用的官方docker直接调试的,这些用户权限应该都是调好了的吧。并且这个issue中有执行的脚本出错的具体信息,我这边是没有的。 不过和我这个信息一样的就只有这个issue了,那我看看我执行mvn命令的用户权限是什么 还是没看出什么 再沉思分析一下,这个插件是在做脚本执行的测试,说明是有脚本执行失败了,那我找出来是哪个脚本之心失败了应该就可以进一步分析 看到插件里面,是执行了src/test/scripts下的run-bats.sh 可以看到这个脚本就是把scripts下的脚本都跑一遍 再看看我的日志,说明这个脚本跑起来了了,有脚本没跑起来,那就按照顺序看看是到哪个没跑起来 官方issue里是hadoop_mkdir.bats这个脚本没跑通,我这里这个脚本跑通了 我最后一个start-build-env.bats的脚本已经执行了也没报错,为什么还是没通过呢? 是不是全部跑完然后中间有不ok的就算报错,于是我回头再看了一遍日志 原来hadoop_java_setup.bats这个脚本中有not ok的 看看这个脚本 原来这个脚本里有四个测试,这时我也才搞懂日志中1..4是代表测试的数目 第三个测试出了问题 这一下问题更精确了,带着hadoop_java_setup这个关键字再去hadoop社区看一下 但是这个关键字太宽泛了,一大堆有关的问题 这个测试猜测是要没有执行权限的时候不执行,但是不知道为什么这里的chmod a-x好像没有起作用,我把-eq 1 改成-eq 0,也就是和第四个对比测试一样就能暂时通过了,不过这应该是个问题。 想要执行单元测试 遇到了这个问题 Unable to load Atom ’execute_script’ from file ‘:/ghostdriver/....

January 26, 2023 · 1 min

Windows下安装运行hadoop的几个要点

hadoop2.7.1要使用的jdk必须是64位的,使用javac命令,如果是64位的jdk会有64的字眼出现,没有就不是64位的,这一点很关键,32位的sdk是运行不了hadoop2.7.1的。 hadoop的压缩包解压需要管理员权限 环境变量里面要添加HADOOP_HOME,path路径里面要增加hadoop安装目录下的bin 要修改Hadoop-2.7.1/etc/hadoop/hadoop-env.cmd里面java的路径,JAVA_HOME=%JAVA_HOME%,理论上如果环境变量里面设置了JAVA_HOME也就不需要改了 伪集群运行,配置文件哪些是必须填的,哪些是选择,有什么作用? <!--core-site.xml--> <configuration> <property> <!--这是选择使用默认的文件系统--> <name>fs.defaultFS</name> <!--使用localhost,这样hdfs的客户端连接端口会默认启动在localhost:8020上,才能在本机通过hadoop fs命令以及直接用java api 访问--> <value>hdfs://localhost/</value> </property> </configuration> <!--mapred-site.xml--> <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration> <!--hdfs-site.xml--> <configuration> <property> <!--配置副本数--> <name>dfs.replication</name> <value>1</value> </property> <!--下面的namenode和datanode路径是可选的,主要是为了指定namenode和datanode的路径,如果不指定的话,hadoop会在默认路径下生成这些文件,好像是直接在根目录下生成(反正不在安装目录下,要看一下日志文件),指定路径就要在相对于的位置下(这里是安装目录下)创建相对应的目录--> <property> <name>dfs.namenode.name.dir</name> <value>/hadoop-2.7.1/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/hadoop-2.7.1/data/datanode</value> </property> </configuration> <!--yarn-site.xml--> <configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration> 执行hdfs namenode –format,在bin目录下,bin目录已经设在path里了,应该在哪里都可以直接使用 执行start-all.cmd,在sbin目录下 会启动四个服务,使用jps可以查看 四个服务分别是namenode的服务,datanode的服务,yarn里面的两个服务,一个负责资源管理,一个负责节点管理

September 29, 2022 · 1 min