hadoop编程初步介绍

近来工作当中用到hadoop之mapreduce、hive、hbase等做大数据的离线数据分析与挖掘颇多,在进一步巩固了自身对于hadoop相关研发的理论与实战水平,在空闲之余会写一系列相关的编程博文。鉴于前边已有一些博文介绍名词概念、基本理论等,本篇作为开篇博文,重点从开发流程中去梳理hadoop编程做大数据分析挖掘的标准或常用流程。 

  1、hadoop研发核心流程

 (1)数据源  

      * 来源:数据来源主要分三种情况,一是,自有平台产生,像腾讯、淘宝。二是,靠网络采集系统,像百度、谷歌。三是,像第三方数据供应商提供,如运营商数据、开源大数据共享集合等。

      * 存储: 有了数据来源,要想进行挖掘必须要存储于hadoop的分布式文件系统中,即hadoop distributed file system,即hdfs。

 (2)数据的处理流程逻辑

  严格来讲,(1)、(2)两点是分离的,只是存在一个前后处理的顺序关系。数据是千差万别的,本篇重点讲解处理流程与逻辑,即mapreduce开发环节。

   mapreduce开发主要是四个环节:map-->combine-->shuffle-->reduce。

   (2.1)其中的map和combine是属于map阶段,

     map:即将原始的输入数据split分块打散的过程,也是大数据“分而治之”、“分流”的思想的体现。将海量数据通过设定的拆分规则,拆分成小的数据块block。然后分配给各map进程并行处理,并将本地计算的数据暂存到本地的临时数据目录,待该输出被reduce所使用完毕,hadoop系统会主动将map产生的这些临时文件删除。

    combine: 即合并的过程。map的输出会作为reduce的输入,而map、reduce不一定在同一机器节点中,这样势必会经过网络传输。相对于计算而言,网络传输是比较费时的,故为了减少map->reduce的零散的大量小数据块的传输耗时,hadoop提供combiner来合并map输出的零散小数据,从而减小网络传输,提高整体的效率。

   (2.2)shuffle

      shuffle是中间阶段,负责map的各个节点的输出经过hash处理后映射到相应的reduce的过程。map和reduce并不是一一对应的,而是按照一定的规则将map上的输出的key/value通过一定的hash计算映身到固定的reduce进程中,这样也就保证了相同的key必然会输出到相同的reduce进程中,从而完成了以key为中心的(key,value list)的汇聚。

  (2.3)redcue

       即对map打散计算完成后,在此对结果合进行汇聚计算与输出。

   例如,在经典的wordcount的mapreduce使用实例中,是对以上四步的准确反应。其代码详解在历史博文中有介绍,有疑问题可以详查看看。


 2、hadoop开发的标准编程

    2.1 关键模块

    任务驱动类Driver:即指定要做的hadoop job的描述信息,包括输入与输出数据的路径、类型、处理的map/reduce类、reduce个数的设置等等。具体代码如下示例   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Job job =  new   Job(ConfigurationUtil.conf,  "CrowdCalculator_v2" );
         job.setJarByClass(CrowdCalcDriver. class );
         job.setMapperClass(CrowdCalcMapper. class );
         job.setReducerClass(CrowdCalcReducer. class );
 
         // 同时设置了map/reduce两部分的key,value对
         job.setOutputKeyClass(Text. class );
         job.setOutputValueClass(Text. class );
 
         // 将otherArgs[0]的输入串进行拆分,以","号隔开
         if   (StringOperatorUtil.isNotBlank(otherArgs[ 0 ])
                 && StringOperatorUtil.isNotBlank(otherArgs[ 1 ])) {
             String[] inputPathArray = otherArgs[ 0 ].split( "," );
             for   (String inputPath : inputPathArray) {
                 FileInputFormat.addInputPath(job,  new   Path(inputPath));
             }
         }  else   {
             System.out.println( "输入或输出路径有问题,请检查!" );
             System.exit( 0 );
         }
 
         FileOutputFormat.setOutputPath(job,  new   Path(otherArgs[ 1 ]));
         // 手动设置reduce个数
         job.setNumReduceTasks(SystemParas.mr_reduce_number);
         int   finish_status = job.waitForCompletion( true ) ?  0   :  1 ;
 
         return   finish_status;

    map过程处理类:

       也就是数据打散后的业务逻辑处理类,即结合天亮分词对已有的数据做关键词的匹配计算与输出。map中除核心的map函数外,还包括map之前要执行的setup函数,以及map之后要执行的cleanup函数,二者一个负责预置计算,一个负责清理扫尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public   class   PyCookieToQQMapper4StepOne  extends   Mapper<Object, Text, Text, Text> {
     @Override
     protected   void   cleanup(Context context)  throws   IOException,
             InterruptedException {
         // TODO Auto-generated method stub
     }
 
     public   static   MyLogger logger =  new   MyLogger(PyCookieToQQMapper4StepOne. class );
     public   static   Set<String> keyword_wanted_set =  new   HashSet<String>();
 
     @Override
     protected   void   setup(Context context)  throws   IOException,
             InterruptedException {
         // 上传到hdfs之后,读取hdfs上的所有缓存文件,初始化分词器
         SkyLightAnalyzerManager.init();
         /**
          * 关于keywordSet的对应关系的加载,此为keyword的白名单
          */
         String keyword_path =  "keyword_wanted.txt" ;
         ReadConfigUtil readConfigUtil =  new   ReadConfigUtil(keyword_path,
                 false );
         String host_type_string = readConfigUtil.getLineConfigTxt();
         try   {
             StringReader sr =  new   StringReader(host_type_string);
             BufferedReader br =  new   BufferedReader(sr);
 
             String temp =  null ;
             while   ((temp = br.readLine()) !=  null ) {
                 keyword_wanted_set.add(temp.trim());
             }
             br.close();
         }  catch   (Exception e) {
             e.printStackTrace();
         }
     }
 
     private   ExtKeywordManager extKeywordManager =  new   ExtKeywordManager();
 
     private   Text outKey =  new   Text();
     private   Text outValue =  new   Text();
     // private int count = 0;
     String[] columnArray =  null ;
 
     String ts =  null ;
     String url =  null ;
     String host =  null ;
 
     // 从map方法提出来
     ExtKeyResult extKeyResult =  null ;
     String query =  null ;
     List<TermUnit> termUnitList =  null ;
     String splitWord =  null ;
     private   String py_cookie_id =  null ;
     private   DateUtil dateUtil= new   DateUtil();
     
     public   void   map(Object key, Text value, Context context)
             throws   IOException, InterruptedException {
         // 以tab键将数据分成数组
         columnArray = value.toString().split(StaticValue.separator_tab);
         // 取日志中的内容列,为url
         if   (columnArray.length !=  16
                 || (SystemParas.is_ad_source_open &&  "0"
                         .equals(columnArray[ 13 ]))) {
             return ;
         }
 
         py_cookie_id = columnArray[ 15 ];
         // 如果py_cookie_id不存在的话,直接跳过
         if   (StringOperatorUtil.isBlank(py_cookie_id)) {
             return ;
         }
 
         ts = columnArray[ 2 ];
         url = columnArray[ 3 ];
         host = columnArray[ 4 ];
 
         // 其第0列为ad为作为key,第3列作为url,第4列为host
         extKeyResult = extKeywordManager.getExtKeyResutlt(url);
 
         // 获取各搜索引擎中
         if   (extKeyResult !=  null   && extKeyResult.isValidUrl()
                 && extKeyResult.isFromSearchKey4Match()) {
             if   (extKeyResult !=  null ) {
                 if   (SystemParas.is_open_keyword_crowd) {
                     query = extKeyResult.getKeyword();
                     if   (StringOperatorUtil.isNotBlank(query)) {
                         query = query.replace( "\t" ,  "" ).trim();
                         termUnitList = SkyLightAnalyzerManager
                                 .filterPosAndNonsense(query);
                         /**
                          * 判断搜索分词或分词器分词后的词条集合是否为空
                          */
                         if   (termUnitList !=  null   && (!termUnitList.isEmpty())) {
                             for   (TermUnit splitTerm : termUnitList) {
                                 splitWord = splitTerm.getValue();
                                 // 不为空,且长度大于1的才会被查询
                                 if   (StringOperatorUtil.isNotBlank(splitWord)
                                         && splitWord.length() >  1 ) {
                                     if   (keyword_wanted_set.contains(splitWord)) {
                                         // 设置ad_id
                                         outKey.set(py_cookie_id);
                                         outValue.set(host
                                                 + StaticValue.separator_tab
                                                 + query
                                                 + StaticValue.separator_tab
                                                 + dateUtil.formatLongToMMHHssString(ts));
                                         context.write(outKey, outValue);
 
                                         break ;
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
}

reduce过程处理类:

   也就是数据的汇聚过程,将map中的输出key/value经过hash处理映身到各reduce节点中进行计算输出。 

1
2
3
4
5
6
7
8
9
10
11
12
public   class   CrowdCalcReducer  extends
             Reducer<Text, Text, Text, Text> {
         private   Text result =  new   Text();
 
         public   void   reduce(Text key, Iterable<Text> values, Context context)
                 throws   IOException, InterruptedException {
             for   (Text val : values) {
                 result.set(val);
                 context.write(key, result);
             }
         }
     }


   小结:上述的标准流程是较常见的,为减少学习的初始复杂度,并没有包括combiner和shuffle。会在后续再做介绍。


3、hadoop开发中的注意事向

  3.1 并不是所有的数据计算都可以用hadoop来搞定。

      只有可以将数据独立分离计算与合并的任务才可以用hadoop来搞定,即可分割。但大量的实战证明,绝大多数的计算任务都可以通过合理的设计化分成可以划分成可以hadoop处理的任务。但像gzip压缩包解压缩这样的操作,一般认为是无法用hadoop来并行计算的,因为gzip是不可分割的,但像lzo,bz2等可分割的压缩格式,均可以被hadoop处理。

  3.2 hadoop不适宜于实时性计算较强的计算

      它是专为离线大数据而设计的,讲究的是吞吐量而非响应速度。像实时查询、实时计算对于hadoop是不适合的。为此hadoop也开发如hive、hbase等为解决这一问题作出努力,但在实时性方面还是很不适用的,像最近兴趣的spark、storm等,可以作为大数据实时处理的利器,与hadoop互补使用。

  3.3 reduce的数量设置要合理

      hadoop的map/reduce都是以进程为单位进行计算的,reduce设置的过多会导致reduce的初始化与销毁浪费时间,从而影响整个任务的效率。要根据输出数据量来计算reduce的数量。

 3.4 map/reduce的槽位最大的数量设置

      该数量的最大数量设置均有固定的参数,该值的计算与cpu个数、单cpu的核数直接相关,可以参考专门的博文设置之。


时间关系,暂定于此,欢迎各位同行交流指正,欢迎加入网络爬虫、nlp群320349384,交流促进发展,共享成就未来。

你可能感兴趣的:(java,hadoop,大数据,分布式文件系统)