本文介绍hadoop小集群的搭建.
十台装有linux的计算机.
这些计算机都装有jdk, 并且准确配置jdk.
这些计算机都装了ssh, 并且都能实现相互之间无密码访问.
记录各台计算机的ip, 并将其中的一台作为master机, 其他计算机一次标号为slave1, slave2, slave3…slave9.
将ip对应标号的信息添加到/etc/hosts文件中, 格式为:
ip1 master
ip2 slave1
ip3 slave2
......
ip10 slave9
同时分别修改各台计算机对应的hostname, 使其对应hosts文件中的标号.
接着是配置hadoop了, hadoop的配置文件分为四个文件, 分别是core-site.xml, hdfs-site.xml, mapred-site.xml, yarn-site.xml.
这三个文件分别对应核心配置, hdfs配置, mapreduce配置和yarn配置.
接着介绍一下需要配置的属性:
hdfs默认的服务器地址和端口.
位置在core-site.xml.
代码:
<property>
<name>fs.default.name</name>
<value>hdfs://master:9000</value>
</property>
hadoop的临时目录存放位置. 这个属性必须设置, 不然hadoop会在/tmp/目录下创建一个目录作为临时目录, 但是当系统重启之后, /tmp/目录就会被清除, 从而启动hadoop时需要重新格式化文件系统.
同时, 要赋予启动hadoop的用户对设置目录的读写权限.
位置在core-site.xml.
代码:
<property>
<name>hadoop.tmp.dir</name>
<value>/opt/hadoop/tmp</value>
</property>
文件块存储的份数, 冗余存储, 提高系统的稳定性.
位置在hdfs-site.xml
代码:
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
平衡器同步时使用的带宽, 这个值设置的高了会占用带宽影响运行效率.
位置在hdfs-site.xml
代码:
<property>
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>10485760</value>
</property>
设置哪种mapreduce框架, 分为local, classic和yarn.
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
jobtracker的位置, 一般设置master机的位置. ip加端口号. 不需要协议
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.jobtracker.address</name>
<value>master:9001</value>
</property>
制定mapper的数量
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.job.maps</name>
<value>2</value>
</property>
制定reducer的数量
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.job.reduces</name>
<value>1</value>
</property>
制定mapper的最大数量
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.tasktracker.map.tasks.maximum</name>
<value>4</value>
</property>
制定reducer的最大数量
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.tasktracker.reduce.tasks.maximum</name>
<value>2</value>
</property>
设置shuffle阶段内存的大小. shuffle有两种方式, 一种是在磁盘上的, 另一种是在内从中进行.当单个文件的大小大于设置的阖值时, 将在磁盘进行操作.
如果小于, 就在内存中操作. 当单个文件小于这个阖值, 而文件数又很多时, 会造成内存不够用的情况, 从而导致程序崩溃现象. 解决方案, 将这个设置设置的很小,
始终在磁盘上进行.
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.reduce.shuffle.memory.limit.percent</name>
<value>0.001</value>
</property>
设置输出文件是否进行压缩. 文件压缩可以减小磁盘的使用率, 当作为中间产物进行传输时, 也节省了带宽.
hadopp支持集中压缩方式, 主要是Bzip2和Gzip. Bzip2压缩文件可以被分块, 即可以由多个map进行操作, 而且这种算法压缩效率较高, 但是这种算法压缩速度较慢.
对于Gzip, 这种算法在压缩速度和压缩效率上都不错, 但是这种压缩算法压缩后的文件不支持分块, 即只能由一个map进行操作.这种方案严重影响实际效率.
对于reduce个数较少可以将中间产物设置成Gzip, 而将最终结果设置成BZip2. 即提升了传输效率, 又有利于二次利用.
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.output.fileoutputformat.compress</name>
<value>true</value>
</property>
设置map之后的产物是否进行压缩
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.map.output.compress</name>
<value>true</value>
</property>
设置输出文件的压缩算法
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.output.fileoutputformat.compress.codec</name>
<value>org.apache.hadoop.io.compress.BZip2Codec</value>
</property>
设置map阶段结果的压缩算法
位置在mapred-site.xml
代码:
<property>
<name>mapreduce.map.output.compress.codec</name>
<value>org.apache.hadoop.io.compress.GzipCodec</value>
</property>
将配置好的hadoop目录打包, 分别发送到各个奴隶机上, 解压,并防止在同一个目录, 同时设置, HADOOP_HOME.
在master机上执行下面命令用于格式化文件系统:
hadoop namenode -format
格式化, 在master机的/opt/hadoop/目录下,执行下面的命令, 启动hadoop集群:
sbin/start-all.sh
可以使用jps命令检查一下是否正常运行, 在master机下执行jps命令, 显示的结果如下:
XXXXX NameNode
XXXXX Jps
XXXXX SecondaryNameNode
XXXXX ResourceManager
在slave机上执行jps, 显示结果如下:
XXXXX NodeManager
XXXXX Jps
XXXXX DataNode
接着是创建用户目录:
hadoop fs -mkdir /user
hadoop fs -mkdir /user/phantom9999
hadoop fs -mkdir /user/phantom9999/logs
hadoop fs -mkdir /user/phantom9999/logs/input/
hadoop fs -mkdir /user/phantom9999/logs/output/
接着是把日志文件上传到hadoop集群中, 使用下面的命令:
hadoop fs -put ./nginx.log.bz2 /user/phantom9999/logs/input/
接着是运行程序了, 将写好的程序上传到集群上, 使用下面的命令进行运行:
hadoop jar countIP.jar input countIP
下面讲述一下上面运行的代码.
这个代码统计了一下nginx日志文件中, 各个IP出现的次数, 并根据出现的次数进行排序.
其中排序操作是在map阶段, 将ip出现的次数作为键名, 将ip作为键值. 在reduce阶段,
交换两者的位置.
具体代码如下:
package com.company;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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.jobcontrol.JobControl;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
public static class DoMapper extends Mapper<Object, Text, Text, IntWritable> {
public static int ipLength = "255.255.255.255,".length();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] fields = line.split("\t");
context.write(new Text(fields[1]), new IntWritable(1));
}
}
public static class DoReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable item: values) {
sum += item.get();
}
context.write(key, new IntWritable(sum));
}
}
public static class SortMapper extends Mapper<Object, Text, IntWritable, Text> {
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] fields = line.split("\t");
if (fields.length == 2) {
context.write(new IntWritable(Integer.parseInt(fields[1])), new Text(fields[0]));
}
}
}
public static class SortReducer extends Reducer<IntWritable, Text, Text, IntWritable> {
public void reduce(IntWritable key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
for (Text item: values) {
context.write(item, key);
}
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length < 2) {
System.out.println("参数不够");
System.exit(0);
}
SimpleDateFormat df = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String tmpDirPath = df.format(new Date());
//tmpDirPath = "2015_09_20_21_31_46";
Path inputPath = new Path("/user/phantom/logs/" + otherArgs[0]);
Path outputPath = new Path("/user/phantom/logs/output/" + otherArgs[1]);
Path middlePath = new Path("/user/phantom/logs/output/" + tmpDirPath);
Job job = Job.getInstance(conf, "log");
job.setJarByClass(Main.class);
job.setMapperClass(DoMapper.class);
job.setCombinerClass(DoReducer.class);
job.setReducerClass(DoReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, inputPath);
FileOutputFormat.setOutputPath(job, middlePath);
job.waitForCompletion(true);
Configuration conf2 = new Configuration();
Job sortJob = Job.getInstance(conf2, "sort");
sortJob.setJarByClass(Main.class);
sortJob.setMapperClass(SortMapper.class);
sortJob.setReducerClass(SortReducer.class);
sortJob.setMapOutputKeyClass(IntWritable.class);
sortJob.setMapOutputValueClass(Text.class);
sortJob.setOutputKeyClass(Text.class);
sortJob.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(sortJob, middlePath);
FileOutputFormat.setOutputPath(sortJob, outputPath);
boolean result = sortJob.waitForCompletion(true);
FileSystem fs = FileSystem.get(conf2);
fs.deleteOnExit(middlePath);
System.exit(result ? 0 : 1);
}
}