一、关于设置hdfs
①获取hdfs文件系统
1
Configuration configuration = new Configuration();
2
FileSystem fSystem = fSystem = FileSystem.get(new URI("hdfs://server:9000"), configuration, "hadoop");
备注:抛出异常Exception;直接操作hdfs上的文件,需要输入用户名,而提交Job等不需要输入用户名。
或者Run Configuration 的Environment variables HADOOP_USER_NAME hadoop
②单元测试
@before 标识的函数 A---->对某函数B进行单元测试前必须先执行的函数A
@Test 被测试的函数 B
二、运行模式
/***********设定本地测试模式 输入输出文件,,在本地或者集群*************/
1
Configuration conf = new Configuration();
2
Job job = Job.getInstance(conf);
3
4
job.setJarByClass(WordCountDriver.class);
5
job.setMapperClass(WordCountMapper.class);
6
job.setReducerClass(WordCountReducer.class);
7
8
job.setMapOutputKeyClass(Text.class);
9
job.setMapOutputValueClass(IntWritable.class);
10
11
job.setOutputKeyClass(Text.class);
12
job.setOutputValueClass(IntWritable.class);
13
14
//输入输出的文件都在本地,适合来测试代码可靠性
15
FileInputFormat.setInputPaths(job, new Path("d:/a.txt")); //执行本地模式
16
FileOutputFormat.setOutputPath(job, new Path("d:/a_out")); //路径必须是新的
17
//将上两句设置为以下两句即可 实现:输入输出的文件都在集群,在本地运行,实际中大文件传输耗损!不能发挥云计算的优点
18
FileInputFormat.setInputPaths(job, new Path("hdfs://server:9000/a.txt"));
19
FileOutputFormat.setOutputPath(job, new Path("hdfs://server:9000/aaaa_out"));
20
21
boolean res = job.waitForCompletion(true);
22
System.exit(res ? 0 : 1);
备注:本地运行时,可以不 设置Run Configuration 的Environment variables HADOOP_USER_NAME hadoop 也能正常运行。
(即本地运行,用户可设置也可不设置)
只是文件的user name 是本机电脑的 user_name 不是hadoop
/***********设定集群运行模式 输入输出文件都在集群*************/
方式A
将程序打成 JAR 包,然后在集群的任意一个节点上用 hadoop 命令启动 推荐
$ hadoop jar wordcount.jar cn.itcast.bigdata.mrsimple.WordCountDriver inputpath outputpath
方式B
修改YarnRunner,并且将四个配置文件放在src下。在执行时也需要打包放到linux上。
只是把控制台的消息打印在了windows的IDEA上,实际意义不大,放弃!
故总结运行模式:①本地来测试代码逻辑;②打包提交到集群模式运行
MapReduce框架用到的Java API
①TestMapper类继承 Mapper <KEYIN, VALUEIN, KEYOUT, VALUEOUT>{} 类
1
KEYIN : LongWritable //KEYIN: 默认情况下是mr框架所读到的一行文本的起始偏移量。但是在hadoop中有自己的更精简的序列化接口LongWritable(extends WritableComparable)
2
VALUEIN : Text //读取的一行数据String 原因同上故用Text
3
KEYOUT : Text //用户自定义输出的数据格式key 一般用Text满足序列化
4
VALUEOUT : IntWritable //用户自定义输出的数据value 一般输出是个对象,算法中可能会用IntWritable(1);
实现方法A:protected void map (LongWritable key, Text value, Context context) throw Exception{}
//map阶段的业务逻辑就写在自定义的map()方法中, maptask会对每一行输入数据调用一次自定义的map()方法。
new Text(key) ,new IntWritable(value)) 无参数时用nullWritable。
实现方法B:protected void setup(Context context ,)throws Exception{} //task启动的时候调用。
实现方法C:protected void cleanup(Context context ,)throws Exception{} //task结束的时候调用。
② TextReducer类继承 Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {}类
1
KEYIN, VALUEIN : //对应 mapper输出的KEYOUT,VALUEOUT类型对应
2
KEYOUT, VALUEOUT : //是自定义reduce逻辑处理结果的输出数据类型一般是<Text,Bean>
实现方法A:protected void reduce(Text key , Iterable<IntWritable> values , Context context ) throws Exception {}类
//按照key分组,确定reduceTask的个数。每个reduceTask都会执行此自定义的reduce()方法,对values进行迭代
//如果key来传过来的是对象, 每个对象都是不一样的, 所以每个对象都调用一次reduce方法。
实现方法B:protected void setup(Context context ,)throws Exception{} //task启动的时候调用。
实现方法C:protected void cleanup(Context context ,)throws Exception{} //task结束的时候调用。
③TextDriver类 执行main方法<完成九行设置,两行IO,两行等待结束>
……………………………………………………以上是标准MR程序的设计规范,以下补充一些常用的API…………………………………………………………………………
扩展API
①String[] values=value.toString().split("\t");//将读取的第一行数据,按照字符串里面内容分片.
数据表格:可将多重数据存在字符串中,然后再次利用split(",")方法取出各数据。
判断读入的字符串是否非空 StringUtils.isNotEmpty(line)
检测字符串相同与否用
将字符串中的数字转变成int型 Integer.parseInt(fields[0])
②StringBuffer:起到String没有的功能 stringBuffer.append(variable).append(",");
③Text, IntWritable,LongWritable. new Text("A"); new IntWritable(1); new LongWritable();
④Iterable<T>接口,Iterator<T> iterator();用在reduce()方法直接写出结果的情况。context.write(values.iterator().next(), bean);
⑤在处理切片时,通过文件名区别读入的切片
FileSplit inputSplit = (FileSplit) context.getInputSplit();//获得输入切片
String fileName = inputSplit.getPath().getName();//获得切片的源文件名
⑥遍历values: for(String value : values);
⑦partitioner<key ,value>抽象类。继承后重写i nt getPartition(KEY key, VALUE value, int numPartitions);实现该方法,自定义某(k,v)对对应的reduceTask。
1
// 指定我们自定义的数据分区器
2
job.setPartitionerClass(ProvincePartitioner.class);
3
// 同时指定相应“分区”数量的reducetask
4
job.setNumReduceTasks(5);
构建ProvincePartitioner.class 继承Partitioner 实现字段匹配分类
1
public static HashMap<String, Integer> proviceDict = new HashMap<String, Integer>();
2
3
static {
4
proviceDict.put("136", 0);
5
proviceDict.put("137", 1);
6
proviceDict.put("138", 2);
7
proviceDict.put("139", 3);
8
}
9
10
String prefix = key.toString().substring(0, 3); //根据字段,区分ID
11
Integer provinceId = proviceDict.get(prefix); //获取ID
12
13
return (provinceId == null) ? 4 : provinceId; //返回ID
四、关于Map等集合:大数据处理中的(k,v)对,Java一定会用到Map。
元素的Key索引 从Map里取出该元素
Map里: 所有Key放在一起来看==>一个Set集合。Value放在一起==>一个List 。即 Map<Set , List>.
Map接口下有HashMap、LinkedHashMap、SortedMap、TreeMap、EnumMap。
1
Map常用方法:
2
void clear(); //删除该Map对象的所有key-value对。
3
boolean containsKey(Object key); //查询Map中是否包含指定的key,如果包含则返回true。
4
boolean containsValue(Obejct,value); //查询Map中是否包含一个或多个Value,如果包含则返回true。
5
Set entrySet();返回Map中包含的key-value对所组成的Set集合,每个集合元素都是Map.Entry对象。
6
Object get(Object key);返回指定key所对应的value,如果此Map中不包含该key,则返回null。
7
boolean isEmpty();查询该Map是否为空。若空为true。
8
Set keySet(); //返回该Map中所有key组成的Set集合。
9
Object put(Object key,Object value); //添加一个key-value对。如果有则覆盖。
10
void putAll(Map m); //将m中的key-value对复制到本Map中。
11
Object remove(Object key); //删除指定key所对应的key-value对,返回被删除key所关联的value,如果没有则返回null。
12
boolean remove(Object key,Object value); //删除指定k-v对。成功删除则返回true,否则为false。
13
int size(); //返回该Map里的k-v对的个数。
14
Collection values(); //返回该Map里所有value组成的Collection。
15
16
Map内部类Entry,该类封装了k-v对,Entry包含三个方法:
17
Object getKey();返回该Entry里包含的key值。
18
Object getValue();返回该Entry里包含的value值。
19
Object setValue(V value);设置Entry里包含的value值,并返回新设置的value。
五、关于MapReduce编程的整理与总结
mapreduce在编程的时候,基本上一个固化的模式,没有太多可灵活改变的地方,除了以下几处:
1、输入数据接口:InputFormat ---> FileInputFormat(文件类型数据读取的通用抽象类) DBInputFormat (数据库数据读取的通用抽象类)
默认使用的实现类是: TextInputFormat job.setInputFormatClass(TextInputFormat.class)
TextInputFormat的功能逻辑是:一次读一行文本,然后将该行的起始偏移量作为key,行内容作为value返回
2、逻辑处理接口: Mapper
完全需要用户自己去实现其中 map() setup() clean()
3、map输出的结果在shuffle阶段会被partition以及sort,此处有两个接口可自定义:
Partitioner 有默认实现 HashPartitioner,逻辑是 根据key和numReduces来返回一个分区号; key.hashCode()&Integer.MAXVALUE % numReduces。 通常情况下,用默认的这个HashPartitioner就可以,如果业务上有特别的需求,可以自定义
Comparable 当我们用自定义的对象作为key来输出时,就必须要实现WritableComparable接口,override其中的compareTo()方法
4、reduce端的数据分组比较接口 : Groupingcomparator reduceTask拿到输入数据(一个partition的所有数据)后,首先需要对数据进行分组,其分组的默认原则是key相同,然后对每一组kv数据调用一次reduce()方法,并且将这一组kv中的第一个kv的key作为参数传给reduce的key,将这一组数据的value的迭代器传给reduce()的values参数。
利用上述这个机制,我们可以实现一个高效的分组取最大值的逻辑: 自定义一个bean对象用来封装我们的数据,然后改写其compareTo方法产生倒序排序的效果, 然后自定义一个Groupingcomparator,将bean对象的分组逻辑改成按照我们的业务分组id来分组(比如订单号) 这样,我们要取的最大值就是reduce()方法中传进来key
5、逻辑处理接口:Reducer 完全需要用户自己去实现其中 reduce() setup() clean()
6、输出数据接口: OutputFormat ---> 有一系列子类 FileOutputformat DBoutputFormat ..... 默认实现类是TextOutputFormat,功能逻辑是: 将每一个KV对向目标文本文件中输出为一行
详见eclipse的Test模板包
手机号的流量数据分析:
①从hdfs上获取文件的手机号及其流量统计的数据
②输出结果按照流量大小排序
③输出结果按照省份分割数据到不同的文件
一、项目分析:
①对hdfs上的数据格式分析,确定(k,v)手机号来做key;上下行数据量构成对象做value。
②构造对象Bean,成员变量及其方法。
③编写Mapper和Reducer类及方法
1、完成Bean的对象构造
①根据对象特点,完善其成员变量以及其get(),set()方法;
②因为要使用context.write(k,v)所以对象要实现Writable的接口;实现后即可以被写
③实现String toString();方法。
备注:如果作为对象的模板学会 修改成员变量;
2、编写Mapper类(在TestMR类中写静态mapper类)
①:继承Mapper确定输入输出的数据格式;
②:读取一行内容,用空格"\t"或者某些标识切割一行数据,获取内容存储在字符串数组中;
③:数据解析后构造对象,写入context。
3、编写Reducer类(在TestMR类中写静态reducer类)
①确定输入输出kv对与Mapper输出的kv对格式相同;
②按照key完成Values的遍历,迭代等操作;
③输出结果文件,注意输出的对象的定义,最后封装对象输出。
4、编写Driver类(一般在TestMR类中写main方法实现)
①设定Job;
②完成一系列的设置,类,输入输出格式,文件路径;
③提交Job,检测。
5、提交Job,进行云计算
①打包jar,sftp提交到host上;
②执行 hadoop jar Test.jar test.TestMR
③执行 hadoop fs -cat /output/part-r-00000 读取结果文件