spark之前的知识已经学习过很久了,但是中途没有很好的整理,也没有发博客,导致知识点得不到复习,所以也快忘得干净,特写此篇来复习巩固一下,把自己以为重点的知识用整理下来,以后会不定期的整理和修改
2019-8-22
添补了官方文档的查漏补缺和更改,对一些细节的东西进行了增加,建议在明白了整个hadoop框架之后在进行查看和学习,也可以自己去官网查询。
2019-8-28
头疼的两天,搞了一些皮毛的源码思路,有点头皮发麻,可能以后还会更新,不确定源码的分析一定是对的,不要太纠结,只是一个思路,有的地方也没有深入研究。
2019-9-2
hadoop作用:利用服务器集群,根据用户的自定义业务逻辑,对海量数据进行分布式处理。
hadoop的核心组件:
广义上来讲,Hadoop是一个生态圈,本文所涉及的是Hadoop这个框架,其他的工具会后续更新。
高可用需要Zookeeper,这里给出的是普通配置,(推荐: 普通–>secondaryNameNode–>HA)
这样可以对配置文件中的具体property是做什么有一定的了解。
安装hadoop2.4.1
先上传hadoop的安装包到服务器上去/home/hadoop/
注意:hadoop2.x的配置文件$HADOOP_HOME/etc/hadoop
伪分布式需要修改5个配置文件
配置hadoop
第一个:hadoop-env.sh
vim hadoop-env.sh
export JAVA_HOME=/usr/java/jdk1.7.0_65
第二个:core-site.xml
<!-- 指定HADOOP所使用的文件系统schema(URI),HDFS的老大(NameNode)的地址 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mini01:9000</value>
</property>
<!-- 指定hadoop运行时产生文件的存储目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/hdpdata</value>
</property>
第三个:hdfs-site.xml
<!-- 指定HDFS副本的数量 -->
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
第四个:mapred-site.xml (mv mapred-site.xml.template mapred-site.xml)
mv mapred-site.xml.template mapred-site.xml
vim mapred-site.xml
<!-- 指定mr运行在yarn上 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
第五个:yarn-site.xml
<!-- 指定YARN的老大(ResourceManager)的地址 -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>weekend-1206-01</value>
</property>
<!-- reducer获取数据的方式 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
3.2将hadoop添加到环境变量
vim /etc/proflie
export JAVA_HOME=/usr/java/jdk1.7.0_65
export HADOOP_HOME=/itcast/hadoop-2.4.1
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
source /etc/profile
格式化namenode(是对namenode进行初始化)
hdfs namenode -format
启动hadoop
hadoop-daemon.sh start namenode
3.5验证是否启动成功
使用jps命令验证
27408 NameNode
28218 Jps
配置ssh免登陆
#生成ssh免登陆密钥
#进入到我的home目录
cd ~/.ssh
ssh-keygen -t rsa (四个回车)
执行完这个命令后,会生成两个文件id_rsa(私钥)、id_rsa.pub(公钥)
将公钥拷贝到要免密登陆的目标机器上
ssh-copy-id localhost
---------------------------
ssh免登陆:
生成key:
ssh-keygen
复制从A复制到B上:
ssh-copy-id B
验证:
ssh localhost/exit,ps -e|grep ssh
ssh A #在B中执行
配置免密登录后启动hadoop命令
start-all.sh
停止命令:
stop-dfs.sh
hadoop fs -ls / 查看根目录文件
hadoop fs -put demo.txt / 将demo.txt复制到根目录
hadoop fs -cat /demo.txt 查看根目录下的demo.txt文件
hadoop fs -moveFromLocal /demo.txt 将本地的demo.txt剪切粘贴到hdfs
hadoop fs -copyToLocal /demo.txt 将hdfs的demo.txt复制粘贴到本地
hadoop fs -mkdir -p /wordcount/input 创建文件夹
hadoop jar hadoop-mapreduce-examples-2.6.4.jar wordcount /wordcount/input/ /wordcount/output 统计该文件夹下文件单词
hadoop fs -appendToFile b.txt /a.txt 将b.txt的内容添加到a.txt后
hadoop fs -chown hadoop:supergroup /a.txt 修改所属用户
hadoop fs -chmod 777 /a.txt 修改权限
hadoop fs -cp /aaa/a.txt /bbb/b.txt 在hdfs内部复制
hadoop fs -mv /aaa/a.txt /bbb/b.txt 在hdfs内部移动
hadoop fs -getmerge /*.txt merg.file 合并并下载到本地
hadoop fs -rm /a.txt 删除
df 统计可用空间信息
du 统计文件夹大小信息
hadoop fs -setrep 3 /b.txt 设置成3个副本,若超过机器的数量,则为文件数目,不是真实数目,增加机器后会自动添加到新机器上
HDFS上传文件流程图:
从源码的角度分析:
HDFS下载文件流程图:
在源码的角度获取输出流(输出流中包含的block块的信息)
SecondaryNameNode工作机制:Image镜像文件的管理
seen_txid:
文件中记录的是edits滚动的序号,每次重启Namenode时候,NameNode就知道要对那些edits进行加载。
//第一种方法,要在vm中配置 -DHADOOP_USER_NAME=hadoop。
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://mini01:9000");
//第二种方法,直接指定URI和用户,在创建FS的时候传过去。
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://mini01:9000"),conf,"hadoop");
import java.net.URI;
import java.util.Iterator;
import java.util.Map.Entry;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;
import org.junit.Before;
import org.junit.Test;
/**
*
* 在使用api 编写hdfs时候,不做处理,默认用户是windows用户
* 再默认情况下,hdfs客户端api会从jvm中获取一个参数来作为自己的用户身份:
-DHADOOP_USER_NAME=hadoop
* 也可以在构造客户端fs对象时,通过uri参数传递进去
*/
public class HdfsClientDemo {
FileSystem fs;
Configuration conf;
@Before
public void inin() throws Exception{
conf = new Configuration();
conf.set("fs.replication", "2");
//拿到一个文件系统的客户端实例对象
//可以直接传入uri和用户身份
fs = FileSystem.get(new URI("hdfs://mini02:9000"),conf,"hadoop");
}
@Test
public void testUpload()throws Exception{
fs.copyFromLocalFile(new Path("D:/data.txt"), new Path("/HotelPrice/input/data.txt"));
fs.close();
}
@Test
public void testConf() throws Exception{
Iterator> it = conf.iterator();
while(it.hasNext()){
Entry ent = it.next();
System.out.println(ent.getKey()+" "+ent.getValue());
}
}
@Test
public void testDownload()throws Exception{
fs.copyToLocalFile(new Path("/HotelPrice/output/part-r-00000"), new Path("D:/Demo1.txt"));
fs.close();
}
@Test
public void testMkdir() throws Exception{
boolean flag = fs.mkdirs(new Path("/wordcount/input"));
System.out.println(flag);
}
@Test
public void testDel() throws Exception{
boolean flag = fs.delete(new Path("/wordcount/input"), true);
//true是是否指定使用java本地的IO流系统
System.out.println(flag);
}
@Test
public void testLs() throws Exception{
RemoteIterator listFiles = fs.listFiles(new Path("/"), true);
while(listFiles.hasNext()){
LocatedFileStatus lf = listFiles.next();
System.out.println("filename:"+lf.getPath());
System.out.println("blocksize:"+lf.getBlockSize());
System.out.println("owner:"+lf.getOwner());
System.out.println("replication:"+lf.getReplication());
System.out.println("permission:"+lf.getPermission());
BlockLocation[] blockLocations = lf.getBlockLocations();
for(BlockLocation b:blockLocations){
System.out.println("快起始偏移量:" +b.getOffset());
System.out.println("快长度:"+b.getLength());
String[] hosts = b.getHosts();
for(String host :hosts){
System.out.println("datanode:" + host);
}
}
System.out.println("-------------------------------");
}
}
@Test
public void testLs2() throws Exception{
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for(FileStatus lf:listStatus){
System.out.println("filename:"+lf.getPath());
System.out.println("blocksize:"+lf.getBlockSize());
System.out.println("owner:"+lf.getOwner());
System.out.println("replication:"+lf.getReplication());
System.out.println("permission:"+lf.getPermission());
System.out.println("-------------------------------");
}
}
}
package erchou;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URI;
import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.Before;
import org.junit.Test;
public class HdfsStreamAccess {
Configuration conf;
FileSystem fs;
@Before
public void init() throws Exception{
/*
第一种方法,要在vm中配置 -HADOOP_USER_NAME=hadoop
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://mini01:9000");
*/
conf = new Configuration();
fs = FileSystem.get(new URI("hdfs://mini01:9000"),conf,"hadoop");
}
@Test
public void testUpload() throws Exception{
FSDataOutputStream create = fs.create(new Path("/cxy.txt"),true);
FileInputStream stream = new FileInputStream("C:/cxy.txt");
IOUtils.copy(stream,create);
}
@Test
public void testDownLoad() throws Exception{
FSDataInputStream open = fs.open(new Path("/cxy.txt"));
FileOutputStream outputStream = new FileOutputStream("D:/cxy.txt");
IOUtils.copy(open,outputStream);
}
@Test
public void testRandomAcess() throws Exception{
FSDataInputStream open = fs.open(new Path("/cxy.txt"));
open.seek(5);
FileOutputStream outputStream = new FileOutputStream("D:/cxy.part2.txt");
IOUtils.copy(open,outputStream);
}
@Test
public void testCat() throws Exception{
FSDataInputStream open = fs.open(new Path("/cxy.txt"));
IOUtils.copy(open,System.out);
}
}
HDFS具有主/从架构。HDFS集群由单个NameNode,一个管理文件系统命名空间的主服务器和管理客户端对文件的访问组成。此外,还有许多DataNode,通常是群集中每个节点一个,用于管理连接到它们运行的节点的存储。在内部,文件被分成一个或多个块,这些块存储在一组DataNode中。NameNode执行文件系统命名空间操作,如打开,关闭和重命名文件和目录。它还确定了块到DataNode的映射。DataNode负责提供来自文件系统客户端的读写请求。DataNodes还执行块创建,删除,
复制品的放置对HDFS的可靠性和性能至关重要。
对于常见情况,当复制因子为3时,HDFS的放置策略是 writer位于datanode上时 就将副本放在本地计算机上(就是提交上传任务的机器本身就是一个datanode),否则放在随机datanode上,在另一个(远程)机架上的节点上放置另一个副本,最后一个在同一个远程机架中的另一个节点上。如果复制因子大于3,则随机确定第4个及以下副本的放置,同时保持每个机架的副本数量低于上限(基本上(副本-1)/机架+ 2)。
为了最大限度地减少全局带宽消耗和读取延迟,HDFS尝试满足最接近读取器的副本的读取请求。如果在与读取器节点相同的机架上存在副本,则该副本首选满足读取请求。如果HDFS群集跨越多个数据中心,则驻留在本地数据中心的副本优先于任何远程副本。
HDFS名称空间由NameNode存储。NameNode使用名为EditLog的事务日志来持久记录文件系统元数据发生的每个更改。在HDFS中创建新文件会导致NameNode将记录插入EditLog,更改文件的复制因子会导致将新记录插入EditLog。NameNode使用其本地主机OS文件系统中的文件来存储EditLog。整个文件系统命名空间存储在名为FsImage的文件中。FsImage也作为文件存储在NameNode的本地文件系统中。
HDFS的主要目标是即使在出现故障时也能可靠地存储数据。三种常见的故障类型是NameNode故障,DataNode故障和网络分区。
数据磁盘故障,心跳和重新复制
每个DataNode定期向NameNode发送Heartbeat消息。NameNode通过缺少Heartbeat消息来检测此情况。NameNode将没有最近Heartbeats的DataNodes标记为已死,并且不会将任何新的IO请求转发给它们。NameNode不断跟踪需要复制的块,并在必要时启动复制。
标记DataNodes死机的超时是保守的长(默认情况下超过10分钟),以避免由DataNode状态抖动引起的复制风暴。
集群重新平衡
HDFS架构与数据重新平衡方案兼容。如果DataNode上的可用空间低于某个阈值,则方案可能会自动将数据从一个DataNode移动到另一个DataNode。
数据完整新
当客户端创建HDFS文件时,它会计算文件每个块的校验和,(对应的是上传图中的chunk)并将这些校验的和存储在同一HDFS命名空间中的单独隐藏文件中。当客户端检索文件内容时,它会验证从每个DataNode接收的数据是否与存储在关联的校验和文件中的校验和相匹配。如果没有,则客户端可以选择从具有该块的副本的另一个DataNode中检索该块。
元数据磁盘故障
增加故障恢复能力的另一个选择是使用多个NameNode 在NFS上使用共享存储
或使用分布式编辑日志(称为Journal)来启用高可用性。
MapReduce框架设计思想
MapReduce中MapTask和ReduceTask的产生和执行流程,Shuffle过程在下图中给出。
如果在Map和Reduce之间传递自己定义的对象,需要把对象进行序列化,实现Writable接口,如果这个类要根据自己的定义排序,实现WritableComparable接口,具体练习在统计流量并并排序给出代码实现。
客户端在做切片,即main方法在做切片。
默认大小就是block的大小。
一个切片就是一个mapTask
一般的切片过程,默认大小128M,如果小于,也算是一个切片,启动一个MapTask进程。
InputFormat切片过程。
通过设置不同的分区号,来决定有多少个reduceTask
//设置partitioner子类
job.setPartitionerClass(FlowPartitioner.class);
job.setNumReduceTasks(2);
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
public class FlowPartitioner extends Partitioner{
@Override
public int getPartition(FlowBean bean, Text phone, int numParitions) {
Long allFlow = bean.getAllFlow();
if(allFlow>2000){
return 1;
}
return 0;
}
}
其他代码在实例练习中给出。
默认情况下,TextFileInputFormat对任务的切片机制是按文件规划切片,不管文件多小,都会是一个切片,都会交给一个mapTask,这样,如果有大量小文件,就会产生大量的mapTask,效率极其低下。
优化策略
最好的办法:在数据处理系统最前端(预处理/采集),将小文件合并成大文件,再上传到HDFS做后续分析。
补救措施:如果已经是大量小文件再hdfs中,可以使用另一种InputFormat来做切片(CombineFileInputFormat),他可以将多个小文件从逻辑上规则到一个切片中,多个小文件就可以交给一个mapTask
代码
job.setInputFormatClass(CombineTextInputFormat.class);
CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);
CombineTextInputFormat.setMinInputSplitSize(job, 2097152);
根据自己的比较规则,再shuffle过程中分到同一组。发给一个reduce进行操作。
public class ItemidGroupingComparator extends WritableComparator {
protected ItemidGroupingComparator() {
super(OrderBean.class, true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
OrderBean abean = (OrderBean) a;
OrderBean bbean = (OrderBean) b;
//将item_id相同的bean都视为相同,从而聚合为一组
return abean.getItemid().compareTo(bbean.getItemid());
}
}
job.setGroupingComparatorClass(ItemidGroupingComparator.class);
Mapper的输入是一个键值对,通过map方法进行转换,给定的输入可以转成多个输出,也可以不输出(就是一个map过程可以向reduce写出多条数据),而且输出的格式是自己指定的。
mapreduce为InputFormat生成的切片(InputSplit)生成一个map任务。
Mapper的实现是通过Job.setMapperClass(Class)方法传递给作业,然后对InputSplit中的每个键值对调用map方法。
对Mapper的输出进行排序,然后在根据Reducer进行分区,分区总数与作业的reducer的任务数目相同。用户可以通过自定义分区程序控制那些健转到那个Reducer,可以选择通过Job.setCombinerClass(Class)指定组合器,以执行中间输出的本地聚合,有助于减少从Mapper传输到Reducer的数据量。
map的正确并行度似乎是每个mapTask大约10-100个map
Reducer中所有的key值相同的value共享同一个key值,即在数据传输到reduce中的时候,只传递第一个key,和一个values数组。
Reducer的数目是通过SetNumReduceTasks设置的。
Reducer有3个主要阶段:shuffle,sort和reduce
注意:把shuffle划分到Reducer中实际上shuffle就是reduce通过http协议将mapper的输出下载并划分到reducer中的过程。
shuffle和sort一起并发,也就是一边下载,一边shuffle一边merge。
再通过指定的GroupingComparator进行二次排序。(如果要求对key进行分组的等价规则与在reduce之前对key默认的分组的规则不同,即自定义的类是使用地址。不定义这个又会通过地址打乱)
正确的reduce数目是 所有的map的0.95倍或者1.75倍(使用0.95时,所有reduce都可以立即启动,使用1.75,更快的节点将完成第一轮reduce并启动第二波reduce,从而更好地实现负载平衡。)
增加reduce的数量会增加框架开销,但会增加负载平衡并降低故障成本。
如果不需要reduce,可以不写reduce。
Job是用户向Hadoop框架描述MapReduce作业以执行的主要接口。
Job通常用于指定Mapper,combiner(如果有),Partitioner,Reducer,InputFormat,OutputFormat实现。
Job用于指定作业的其他高级方面,例如要使用的比较器,要放入DistributedCache的文件,是否要压缩中间和/或作业输出,每个任务的最大尝试次数
MRAppMaster执行Mapper/ Reducer任务作为一个单独的JVM子进程
用户/管理员还可以使用mapreduce {map | reduce} .memory.mb指定已启动的子任务的最大虚拟内存,并且该值必须大于或等于传递给JavaVM的-Xmx,否则JVM可能无法启动。
当序列化缓冲区或元数据超过阈值时,缓冲区的内容将在后台排序并写入磁盘,如果在溢出过程中任一缓冲区完全填满,则map线程将阻塞。
Job是用户作业与ResourceManager交互的主要接口。
工作提交流程包括:
MapReduce框架依赖于作业的InputFormat:
InputFormat的默认是TextInputFormat。
OutputFormat描述了MapReduce作业的输出规范。
MapReduce框架依赖于作业的OutputFormat:
TextOutputFormat是默认的OutputFormat。
Yarn提交任务的流程图
ResourceManager中有两个重要的组件,Scheduler和ApplicationsManager,Scheduler负责任务调度,ApplicationsManager负责启动ApplicationMaster
package MR;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
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;
public class WordCount {
public static class WordCountMap extends Mapper<LongWritable,Text, Text,IntWritable>{
Text text = new Text();
IntWritable iw = new IntWritable();
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] datas = line.split(" ");
for(String data:datas){
text.set(data);
iw.set(1);
context.write(text, iw);
}
}
}
public static class WordCountReduce extends Reducer<Text,IntWritable, Text,IntWritable>{
Text text = new Text();
IntWritable iw = new IntWritable();
protected void reduce(Text line, Iterable<IntWritable> iter,
Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
String value = line.toString();
int num = 0;
for(IntWritable iw:iter){
num+=iw.get();
}
text.set(value);
iw.set(num);
context.write(text, iw);
}
}
//指定用户
//写内部类要使用static来进行修饰,否则反射的时候不能进行加载。
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.set("mapreduce.framework.name", "local");
conf.set("fs.defaultFS", "hdfs://mini02:9000");
Job job = Job.getInstance(conf);
job.setJarByClass(WordCount.class);
//指定本业务job要使用的mapper业务类
job.setMapperClass(WordCountMap.class);
//指定本业务job要使用的reducer业务类
job.setReducerClass(WordCountReduce.class);
//指定map输出的类型是什么
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定最终输出数据的kv类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//job输入文件所在目录
FileInputFormat.setInputPaths(job, new Path("/wc"));
//job输出结果所在目录
FileOutputFormat.setOutputPath(job, new Path("/out6"));
//提交到yarn
boolean res = job.waitForCompletion(true);
System.exit(res?0:1);
}
}
package Flow;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
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;
public class FlowDriver {
public static class FlowMapper extends Mapper<LongWritable, Text, FlowBean, Text>{
Text text = new Text();
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, FlowBean, Text>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] datas = line.split("\t");
String phone = datas[1];
long upFlow = Long.parseLong(datas[7]);
long downFlow = Long.parseLong(datas[8]);
FlowBean bean = new FlowBean(upFlow, downFlow);
text.set(phone);
context.write(bean, text);
}
}
public static class FlowReducer extends Reducer <FlowBean,Text,Text,FlowBean>{
@Override
protected void reduce(FlowBean bean, Iterable<Text> phones, Reducer<FlowBean,Text,Text,FlowBean>.Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
context.write(phones.iterator().next(),bean);
}
}
public static void main(String[] args) throws IOException, Exception, InterruptedException {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://mini02:9000");
Job job = Job.getInstance(conf);
//设定运行的jar
job.setJarByClass(FlowDriver.class);
//Mapper
job.setMapperClass(FlowMapper.class);
//Reducer
job.setReducerClass(FlowReducer.class);
//map输出类型
job.setMapOutputKeyClass(FlowBean.class);
job.setMapOutputValueClass(Text.class);
//reducer输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FlowBean.class);
//设置输入路径
FileInputFormat.setInputPaths(job,new Path("/flow.log"));
//设置输出路径
FileOutputFormat.setOutputPath(job, new Path("/out2/out2"));
boolean res = job.waitForCompletion(true);
System.exit(res?0:1);
}
}
class FlowBean implements WritableComparable<FlowBean>{
private long upFlow;
private long downFlow;
private long allFlow;
public FlowBean() {
}
public FlowBean(long upFlow, long downFlow) {
this.upFlow = upFlow;
this.downFlow = downFlow;
this.allFlow = upFlow+downFlow;
}
public long getUpFlow() {
return upFlow;
}
public void setUpFlow(long upFlow) {
this.upFlow = upFlow;
}
public long getDownFlow() {
return downFlow;
}
public void setDownFlow(long downFlow) {
this.downFlow = downFlow;
}
public long getAllFlow() {
return allFlow;
}
public void setAllFlow(int allFlow) {
this.allFlow = allFlow;
}
@Override
public void readFields(DataInput in) throws IOException {
this.upFlow = in.readLong();
this.downFlow = in.readLong();
this.allFlow = in.readLong();
}
@Override
public void write(DataOutput out) throws IOException {
out.writeLong(this.upFlow);
out.writeLong(this.downFlow);
out.writeLong(this.allFlow);
}
@Override
public int compareTo(FlowBean o) {
return this.getAllFlow()>o.getAllFlow()?-1:1;
}
@Override
public String toString() {
return "FlowBean [upFlow=" + upFlow + ", downFlow=" + downFlow + ", allFlow=" + allFlow + "]";
}
}
数据:
A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J
输入结果:
A-B E,C,
A-C D,F,
A-D E,F,
A-E D,B,C,
A-F O,B,C,D,E,
A-G F,E,C,D,
A-H E,C,D,O,
A-I O,
A-J O,B,
A-K D,C,
A-L F,E,D,
A-M E,F,
B-C A,
B-D A,E,
B-E C,
B-F E,A,C,
B-G C,E,A,
B-H A,E,C,
B-I A,
B-K C,A,
B-L E,
B-M E,
B-O A,
C-D A,F,
C-E D,
C-F D,A,
C-G D,F,A,
C-H D,A,
C-I A,
C-K A,D,
C-L D,F,
C-M F,
C-O I,A,
D-E L,
D-F A,E,
D-G E,A,F,
D-H A,E,
D-I A,
D-K A,
D-L E,F,
D-M F,E,
D-O A,
E-F D,M,C,B,
E-G C,D,
E-H C,D,
E-J B,
E-K C,D,
E-L D,
F-G D,C,A,E,
F-H A,D,O,E,C,
F-I O,A,
F-J B,O,
F-K D,C,A,
F-L E,D,
F-M E,
F-O A,
G-H D,C,E,A,
G-I A,
G-K D,A,C,
G-L D,F,E,
G-M E,F,
G-O A,
H-I O,A,
H-J O,
H-K A,C,D,
H-L D,E,
H-M E,
H-O A,
I-J O,
I-K A,
I-O A,
K-L D,
K-O A,
L-M E,F,
代码如下:
package Friend;
import java.io.IOException;
import java.util.Arrays;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
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;
public class FriendDriver {
public static class MapperOne extends Mapper<LongWritable, Text, Text, Text>{
Text text1 = new Text();
Text text2 = new Text();
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] datas = line.split(":");
String person = datas[0];
String[] friends = datas[1].split(",");
for(String friend:friends){
text1.set(person);
text2.set(friend);
context.write(text2, text1);
}
}
}
public static class ReduceOne extends Reducer<Text, Text, Text, Text>{
Text text1 = new Text();
Text text2 = new Text();
protected void reduce(Text friend, Iterable<Text> persons, Reducer<Text, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
String line = "";
for(Text person:persons){
String per = person.toString();
line+=per+",";
}
text1.set(friend);
text2.set(line);
context.write(text1, text2);
}
}
public static class MapperTwo extends Mapper<LongWritable, Text, Text, Text>{
Text text1 = new Text();
Text text2 = new Text();
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
System.out.println(line);
String[] datas = line.split("\t");
String friend = datas[0];
String[] persons = datas[1].split(",");
Arrays.sort(persons);
for(int i = 0;i<persons.length;i++){
for(int j = i+1;j<persons.length;j++){
text1.set(persons[i]+"-"+persons[j]);
text2.set(friend);
context.write(text1, text2);
}
}
}
}
public static class ReduceTwo extends Reducer<Text, Text, Text, Text>{
Text text1 = new Text();
Text text2 = new Text();
protected void reduce(Text perToPer, Iterable<Text> friends, Reducer<Text, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
String line = "";
for(Text friend:friends){
String per = friend.toString();
line+=per+",";
}
text1.set(perToPer);
text2.set(line);
context.write(text1, text2);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://mini02:9000");
Job job = Job.getInstance(conf);
//设定运行的jar
job.setJarByClass(FriendDriver.class);
//此处可以用job串 串联起来
//Mapper
//job.setMapperClass(MapperOne.class);
job.setMapperClass(MapperTwo.class);
//Reducer
// job.setReducerClass(ReduceOne.class);
job.setReducerClass(ReduceTwo.class);
//map输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//reducer输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//设置输入路径
FileInputFormat.setInputPaths(job,new Path("/out/out4/part-r-00000"));
//设置输出路径
FileOutputFormat.setOutputPath(job, new Path("/out/out13"));
boolean res = job.waitForCompletion(true);
System.exit(res?0:1);
}
}