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

手机人生

来北京工作之后办了一张北京的中国移动卡,办完之后才发现是一张刚注销的卡。这张卡还绑定着原机主的银行卡,所以原机主的转账记录收入支出的信息都发给我了。从短信内容来看,原机主应该是个房东,因为这张卡绑定的银行卡的每月收入都来自自如的转账。而且中间有几次中介打电话问我要不要考虑卖掉哪儿哪儿哪儿的一套房。 每个月我收到工资后先划出一部分交房租的时候,我的手机倒是会弹出收租的信息,倒是同时体验了一把房东的快乐。 今天大概是他的子女来京?早上七点多给我打电话,听那边的声音说刚下飞机,说这个手机号是她的不知道为什么串号了。这个时间工作日坐飞机来北京,大概率是大学生?返校季。后来又有个男人打电话给我,可能是女子不清楚发生什么了,让胆大的来。不过好笑的是,每次对方都问我我是哪位?那我怎么自我介绍?难道还得把自己生世阐述一番?我也只好以问代答,“您是哪位”。中国的见面招呼已经从“吃饭了”变成了“你哪位?”,感觉有“how do you do”的无厘头之势。 我看场面太尴尬,就直接说明了这个手机号应该是原主人注销了,请麻烦通知原主人还有他的亲朋好友。男人用一口很地道的北京话对我道谢。 以后可能不能再体会一下房东的快乐了吧。

February 22, 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

土地爷

家乡过年有一个风俗,拜土地爷和土地娘娘。我是没听说过土地娘娘的,也许乡里人认为土地爷总得讨个老婆,所以应该是有土地娘娘的。也可能烧制土地爷佛像的泥瓦匠多烧了一个女像,索性就当土地娘娘打包卖出去了。 在除夕夜的零点,跨年之际,土地庙旁的天空中一声爆竹炸响,黑夜中一点亮光闪过,拜土地的活动就该开始。不知道是怎样一股无形的力量驱使着,人们心照不宣地认为第一个磕头的是个好彩头,于是老一辈假装心不在焉地站好位置,一边互相拜年说欢喜话,一边提防着四周,不时看看手表,零点一到,一半身子去拦着后来者,一半身子将自己的孙子孙女推到土地爷爷面前去磕头。 磕头的都是小孩,大人们负责烧香,放鞭炮,男人们可能会在周围谈论几句。土地庙很小,但是烟火气这时候很足,没有人知道这座小土地庙是什么时候建的,里面两尊土地像看上去就年代久远,很有佛性。 县升市,上头派来了一个新的女书记,具体职位不清楚,大家叫大官都叫书记。坊间传闻她信基督教,所以要拆了全县(现在是市了)的土地庙。凡举报土地庙者奖励300元,这招很有效。推土机突突突突地碾过一尊又一尊土地爷的像,当然他老婆也未能幸免。我们的土地庙也要被推倒,土地庙前的一户人家,听到此消息连夜去土地庙中抢救土地爷爷和土地娘娘,乡里乡间也前来抗议。抗议颇有成效,推土机在一片抗议声中把土地庙碾的更碎了一点。 人民群众不满意,所以只能诅咒,诅咒女书记迟早完蛋。诅咒颇有成效,坊间传闻,推土机在一次碾土地庙时翻到沟里去了。于是女书记一声令下停止了对土地庙的清除活动。抢救出土地爷爷和他老婆的那个人,呼吁大家一起重建土地庙。乡里大家一起捐款出力,重建了土地庙。 所以今年我回来的时候,拜的已经是新土地庙了。新的土地庙与之前的相差不大,砖头新了,砖缝的石灰粉还是白色的。倒是旧的土地爷爷和他老婆那两尊历史悠久的像没有换回来,两个现代化的崭新的新时代土地爷爷和土地奶奶的像摆在桌上。而他们身后本来应该空空如也的墙上,张贴了一张巨大的毛主席海报。

January 26, 2023 · 1 min

我对单元测试的理解

我对单元测试的理解 保证测试的代码符合编写时的预期 目前为止,我还没有系统的学习过测试领域的知识,但是测试对于研发岗来说是重要的。测试在我的日常工作中主要是起到程序逻辑校验的作用,作为研发来说,自己做的单元测试的目的主要在于程序逻辑,而不是业务逻辑。也就是说,单元测试能保证通过单元测试的代码一定是符合我的预期的,而我的预期本身是不是有问题没法通过单元测试校验,需要集成测试以及专业的测试团队进行测试。 单元测试帮助我快速复现bug 在实际生产过程中,我的单元测试足够多,覆盖面足够广,那么出了问题的时候就可以快速定位到出错代码的单元测试。这样修改代码,就可以针对单元测试来进行修改。当然,这里也会出现,bug出现了却没有制定的单元测试,这时就可以添加单元测试,在修复bug的同时丰富单元测试用例。我称之为基于单元测试的敏捷开发。 单元测试帮助代码重构 这有两方面,第一方面是有了单元测试,在代码重构时就能可以保证代码的输入和输出不会出问题,那就可以放心大胆的一步一步重构。第二方面,单元测试也能保证代码质量,因为越小的代码,耦合越低的代码越容易进行单元测试。有的一些代码在写单元测试的时候会发现根本无从下手,这就是很明显的代码坏味道。 单元测试帮助阅读和理解旧代码 我发现这一点很少有人提及,因为单元测试一般都是测试一些比较关键的代码,而且输入和输出都会明确列举出来。如果源代码里有单元测试,那么读单元测试就能很清晰的了解到这一块代码的作用是什么。而如果没有单元测试,又有一块代码的逻辑不是很清晰,那么也可以通过补充单元测试的方式来搞懂代码。说白了,单元测试就是可以将一块代码简化成输入->输出这种形式,更易于人脑解析。 研发的单元测试比重 很多人不想写单元测试,是觉得单元测试时间太长。其实单元测试的好处大家都知道,如果给予更多的开发周期,相信更多人会花时间在单元测试上。其实这里关键的问题不止单元测试值不值得做,还有单元测试耗费的时间到底长不长。单元测试一定值得做,上面提到的好处在整体上一定会减少开发时间。而单元测试耗费的时间到底长不长呢?这就要提到一个误区了,有不少人是把单元测试也当作代码来写,这并没有太多问题,但是单元测试是一种工具,那么使用这种工具就一定是越简单越快越好,直白点就是有轮子就用轮子,代码越简单越好。比如写java的单元测试,如果不引用单元测试框架,但确实折磨,引入了框架再加上一些mock工具就很更加简单,而如果再使用一些辅助语言比如groovy或者scala,单元测试就会变得十分简单,就是在程序编写时顺手的一个工作。 Spock单元测试推荐 我再尝试了一些单元测试框架和mock工具之后,目前在写java程序时稳定使用的是spock测试框架,这时一个基于groovy的单元测试框架,groovy和java代码可以无缝接入,所以我的代码一般都是主程序用java写,测试程序用groovy写。 简单的spock测试程序大概长这样: class PropertyCopierTest extends Specification { def "CopyBeanProperties"() { given: def sourceBean = new TestProperty("beanOne", 100) def destinationBean = new TestProperty() when: PropertyCopier.copyBeanProperties(TestProperty.class, sourceBean, destinationBean) then: destinationBean.with { name == "beanOne" id == 100 } } } spock的教程网上有很多,我就不累述了: Spock单元测试框架介绍以及在美团优选的实践 用Groovy更迅速地对Java代码进行单元测试 Introduction to Testing with Spock and Groovy 单元测试和Mock工具中的一些通用概念 mock mock就是直接通过类创建一个对象,这个对象的返回值是模拟出来的也就是程序调用到指定方法就返回你事先定义好的值。 比如用Mockito框架mock: //模拟PersonDao对象 mockDao = mock(PersonDao.class); when(mockDao.getPerson(1)).thenReturn(new Person(1, "Person1")); when(mockDao....

November 25, 2022 · 1 min

技术文章归总(1)

单元测试 使用Junit+Mockito实践单元测试 Spock单元测试框架介绍以及在美团优选的实践 腾讯新闻的单元测试总结 用Groovy更迅速地对Java代码进行单元测试 Linux 通过inode删除乱码文件 Spark 美团技术团队Spark优化基础篇 美团技术团队Spark优化高级篇 SparkSql与hive on spark与spark on hive的关系 不要滥用spark repartition hiveql的distribute by Spark Thrift Server与Hive Server2的区别详解 如何管理spark的分区 图数据库平台建设 美团图数据库平台建设 OPPO图数据库平台建设 算法 滑动窗口算法

October 26, 2022 · 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

使用vscode调试c

在vscode下创建tasks.json文件,该文件会被vscode用来执行生成可执行文件的操作。 { "version": "2.0.0", "tasks": [ { "label": "build c", "type": "shell", "command": "cc", "args": [ "-std=c99", "-Wall", "-ledit", "-lm", "-g", "${workspaceFolder}/**.c", "-o", "${workspaceFolder}/${fileBasenameNoExtension}.out" ], "group": "build" }, ] } Command 和 args 是主要的参数,如何生成可执行文件就如何编写。 ${workspaceFolder}表示工作目录 ${fileBasenameNoExtension}如其名表示去掉后缀的文件名 创建launch.json文件,该文件会被vscode用来执行debug操作。 { "version": "0.2.0", "configurations": [ { "preLaunchTask": "build c", "name": "Launch C", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/${fileBasenameNoExtension}.out", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": true, "MIMode": "lldb", } ] } preLaunchTask填写生成调试文件的task的label。...

September 28, 2022 · 1 min