Hadoop实战(Chuck Lam)

Comments: No Comments
Published on: 2012 年 05 月 08 日


第一章hadoop简介

hadoop是一个联合多个服务器建立起一个服务器集群公共作业的一套框架程序。包括了分布式存储和分布式计算。

第二章初识hadoop

运行hadoop意味着在不同机器上运行一些daemons,这些daemons包括:

NameNode
DataNode
SecondaryNameNode
JobTracker
TaskTracker

NameNode是名字节点,是hadoop采取的分布式存储文件系统HDFS主从(master/slave)结构的主端。,负责存储HDFS内的文件信息,比如记录如何从HDFS中找到被分割存储在各个机器中的文件等。这样可以看出NameNode很重要,因为这个节点失效,意味着整个HDFS内的数据全部丢失,因为你不知道该如何组织这些数据还原成正常的数据。所以SecondaryNameNode用来防止这一情况的发生,是一个备份的NameNode,平时SNN不工作,只与NameNode通信获取相关HDFS数据信息,当NameNode失效时便发挥作用。

DataNode就是分布式存储数据的节点,里边存储有数据的某些分割后的数据块,根据需要保证失效几个DataNode节点仍能正常工作保持数据的完整性而进行不同程度的冗余存储。比如4个节点,存储一个数据,这个数据被分割成5份,如果要保证任意俩节点失效的情况下数据仍然完整,则需要每个分割都存储3个备份,可以像这样存储:

node1:2,3,4,5
node2:1,3,5
node3:1,2,3,4,5
node4:1,2,4

有没有很类似之前一篇文章里说的多个特工传递信息的方法?

JobTracker是Hadoop里的作业分配者,它会确定执行计划,包括决定处理哪些文件、为不同节点分配不同任务、追踪作业,分发作业代码等工作。

TaskTracker就是被JobTracker管理的作业节点,执行被分配的任务。

书中这里开始讲hadoop的搭建,请看这篇文章

第三章hadoop组件

hadoop的一个重要组件就是HDFS,相关的基本命令和部分编程API请看之前的hadoop权威指南一,一个编程样例请看这篇

这里说一下另一个重要MapReduce组件的程序流程。
这一流程先读取数据到map程序,map程序内部经过自己的逻辑处理之后输出到reduce程序进行合并,最后输出结果。这一一个MapReduce就算完成了。

再稍微详细一点点就是原始数据source(k1,v1)读入到map,经过map逻辑,把处理后的数据格式化为list(k2,v2)后传给reduce,经过reduce合并结果数据result(k2,list(v2))到最后得到最终合并出的结果list(k3,v3)。
这一过程要确保map传递出的数据键值k2和reduce得到的k2数据类型保持一致。确保reduce合并结果就是最终结果的一个操作就是根据k2键值进行一种规则下的散列,使得相同键值的中间数据会被散列到同一reduce程序中。这个中间可以对map的格式化中间数据list(k2,v2)进行本地合并优化,减少散列过程中的数据规模,这一过程就是Combiner。
各个过程中的数据类型有hadoop预定义的,也可以自定义,但是要继承hadoop给的Writable接口。需要了解详细Hadoop自带的org.apache.hadoop.io包中有广泛的Writable类,请看hadoop权威指南二这篇里的中间的介绍相关内容的部分。

再详细一点就是,程序继承MapReduceBase基类,并实现Mapper和Reducer接口,使用void configure(JobConf job)函数提取配置文件信息或者在主程序中修改设置,在走MapReduce流程前确定运行参数。然后就是完成继承后的map和reduce程序自己的逻辑部分:

void map(k1 key, v1 value, OutputCollect(k2,v2) output, Reporter reporter) throws IOException{...}

void reduce(k2 key, Iterator<v2> values, OutputCollect(k3,v3) output,
Reporter reporter) throws IOException{...}

当然如何数据类型,hadoop也提供了一些实用的map和reduce的实现:

IdentityMapper<k,v> 实现Mapper<k,v,k,v>,将输入直接映射到输出
InverseMapper<k,v> 实现Mapper<k,v,v,k>,反转键值对
RegexMapper<k> 实现Mapper<k,Text,Text,LongWritable>为每个常规表达式的匹配生成一个(match,1)对
TokenCountMapper 实现Mapper<k,Text,Text,LongWritable>当输入分词时,生成一个(token,1)对
IdentityReducer 实现Reducer<k,v,k,v>直接输出
LongSumReducer 实现<k,LongWritable,k,LongWritbale>计算给定键相对应的所有值的和

使用的时候就在JobConf里设置即可,如:

conf.setMapperClass(TokenCountMapper.class);

书里继续说了IO操作,主要是InputFormat和OutputFormat。InputFormat类中hadoop自带的实现有:

TextInputFormat 文本文件一行一个记录,键值为一行的字节偏移量,值为一行内容

KeyValueTextInputFormat 文本文件一行为一个记录,以每行第一个分隔符为界,之前的为键值,之后的是值,分隔器在属性key.value.separator.io.input.line中设定,默认为\t

SequenceFileInputFormat<k,v> 用于读取顺序序列文件的InputFormat,键和值由用户自定义,序列文件为hadoop专用的压缩二进制文件格式,它专用于一个MapReduce作业和其他作业之间传递数据。

NLineInputFormat 与TextInputFormat相同,但每个分片一定有N行,N在属性mapred.line.input.format.linespermap中设定,默认为1。

可以在JobConf对象中设置使用,比如:

conf.setInputFormat(KeyValueTextInputFormat.class);

也可以自定义InputFormat,这时必须编写自定义的InputFormat类,详细情况另行查看InputFormat接口。

OutputFormat的自带实现有:

TextOutputFormat 讲键和值以字符串形式写入,并以\t分隔,这个分隔符在mapred.textoutputformat.separator中修改
SequenceFileOutputFormat 以Hadoop专有序列文件格式写入键值对
NullOutputFormat 无输出

第四章MapReduce基础程序

下边给出一个统计单词出现次数的程序,也是模板程序,以后的MapReduce程序只需要进行相对应的改造即可。这里没有使用书中给的例子,而是结合我自己的经验给出一个模板,更适合新人学习,书中给的程序也是老的API,另外也为了我之后的帮助后来者做样例:

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
import java.io.IOException;
import java.util.StringTokenizer;
 
import org.apache.hadoop.conf.Configuration;
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.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
 
public class WordCount {
 
  public static class TokenizerMapper
       extends Mapper{
 
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();
 
    public void map(Object key, Text value, Context context
                    ) throws IOException, InterruptedException {
      StringTokenizer itr = new StringTokenizer(value.toString());
      while (itr.hasMoreTokens()) {
        word.set(itr.nextToken());
        context.write(word, one);
      }   
    }   
  }
 
  public static class IntSumReducer 
       extends Reducer {
    private IntWritable result = new IntWritable();
 
    public void reduce(Text key, Iterable values, 
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }   
      result.set(sum);
      context.write(key, result);
    }
  }
 
  public static void main(String[] args) throws Exception {
    Configuration conf = new Configuration();
    String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
    if (otherArgs.length != 2) {
      System.err.println("Usage: wordcount  ");
      System.exit(2);
    }
    Job job = new Job(conf, "word count");
    job.setJarByClass(WordCount.class);
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class);
    job.setReducerClass(IntSumReducer.class);
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);
    FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}

现在解释一下这个模板程序,从主函数看起,先是处理了一下程序入口传入的参数,然后声明了一个作业job。接下来都是给这个作业job进行一些属性设定,大部分都前边有提到,设置了mapper类和reducer类的名字,设置了本地合并的combiner的默认一个实现,设置了输出的数据类型,最后设置了程序的读入路径和输出路径。

再来看map函数,在所在类里声明了一个hadoop的IntWritable类型,设置值为1,然后声明一个hadoop的Text类型。函数内使用StringTokenizer函数按空格分解value值,循环读取迭代器内的所有值并复制给word变量,同时输出(key,value)对,每个单词都是输出相应的单词和数量1。

reduce函数接受到的键值key和一起的一个迭代器,表示所有这个键值的值的集合,使用循环操作来读取迭代器内所有单词出现的次数并进行统计,最后输出结果。前后要确保输入输出类型的一致。

这个模板至此结束。大家可以把MapReduce程序想象成linux里的awk命令或其他命令,linux很多命令都是基于每次处理一行这一原则设计的。MapReduce也是每次处理一行数据,这一行数据如何处理全看map函数里的逻辑。然后会根据key的输出值进行散列,这样确保了相同的key是一定散列到同一reduce里的,这样reduce最终输出的结果无需再进行多个作业机器结果合并,出来的结果就是最终结果。

MapReduce的使用,至此已经可以实战了,非常简单方便好用。在使用的过程中遇到的问题都是提高的过程。书中给的程序都是老版本的,如果你看到这里,请放弃老版本程序,因为在0.20之后的版本将不再支持,专用上边给的新的模板程序。

下边说一下一个好用的强力的工具Streaming,这个程序主要用作编写简单、短小的MapReduce程序。它同样也可以使用其他语言来编程。这个组件非常好用。下边先给出简单的使用linux命令编写MapReduce程序的样例:

1
2
$ hadoop jar contrib/streaming/hadoop-0.20.2-streaming.jar -input cite75_99.txt \
&gt;            -output output -mapper 'cut -f 2 -d , ' -reducer 'uniq'

这个样例是书中的例子,数据类似如下:

"CITING","CITED"
3858241,956203
3858241,1324234
3858241,3398406
3858241,3557384
3858241,3634889
...

这个程序,使用cut命令截取第二列,然后map输出会排序,最后给reducer进行uniq操作,非常简单明了。不仅可以执行unix命令,其他的python、php等等,都可以执行,不再赘述。详细情况直接运行streaming的jar包查看。

streaming提供了一个非常好用的统计工具Aggregate,只需要提供一个预处理的map程序即可使用,map输出规则为:

function:key\tvalue

其中支持的function的聚合器函数有:

DoubleValueSum 一个double值序列的求和
LongValueMax 求一个long值序列的最大值
LongValueMin
LongValueSum
StringValueMax 求一个string值序列的字母序最大值
StringValueMin
UniqValueCount 为每个键求单一值的个数
ValueHistogram 求每个值的个数、最小值、中值、最大值、平均值和标准方差

其中ValueHistogram的格式有点特别:

ValueHistogram:key\tvalue\tcount

还是拿书中样例示范:

View Code PYTHON
1
2
3
4
5
6
7
8
#!/usr/bin/env python
# Name: AttributeCount.py
import sys
 
index=int(sys.argv[1])
for line in sys.stdin:
    fields=line.split(",")
    print "LongValueSum:"+fields[index]+"\t"+"1"

使用命令:

$ hadoop jar contrib/streaming/hadoop-*-streaming.jar -input apat63_99.txt -output output
-file AttributeCount.py -mapper 'AttributeCount.py 1 ' -reducer aggregate

样例数据apat63_99.txt如下:

"CITING","CITED"
3858241,956203
3858241,1324234
3858241,3398406
3858241,3557384
3858241,3634889
...

第五章高阶MapReduce

这一章介绍了MapReduce高级应用,有时候无法用一个MapReduce完成任务时,可以使用链接作业,包括多个MapReduce作业顺序链接执行,具有依赖关系的链接执行等。另外又介绍了contrib/datajoin jar包的联接数据文件的应用。

详细说了一种Bloom filter数据结构,是用于用较少内存来判断某一元素是否存在数据集中的方法,有一定概率误判,误判概率和数据集中元素个数、分配内存大小、散列算法有关。算法是把一个元素按一定规则散列到一个长bit数据结构中的某几位,这几位赋值为1,检测时按同一散列规则检测,如果所有位为1,则表示数据集中出现过该元素,否则没出现过。书中给的散列算法如下:

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
	private int numHashFunc = 6;
	private int bitArraySize = 100000000;
 
	protected int[] getHashIndexes(E obj) {
		int[] indexes = new int[numHashFunc];
		long seed = 0;
		byte[] digest;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(obj.toString().getBytes());
			digest = md.digest();
 
			for (int i = 0; i &lt; 6; i++) {
				seed = seed ^ (((long) digest[i] &amp; 0xff)) &lt;&lt; (8 * i);
			}
		} catch (NoSuchAlgorithmException e) {}
 
		Random gen = new Random(seed);
 
		for (int i = 0; i &lt; numHashFunc; i++) {
			indexes[i] = gen.nextInt(bitArraySize);
		}
 
		return indexes;
	}

返回值是obj应该散列到的相应的numHashFunc个数的位的索引。

第六章编程实践

主要介绍了生产过程,从编码、本地测试、伪分布测试、到最后的集群测试。
同时说了测试过程中的监测,包括日志监测、web页面监测等。
最后提了一些性能优化方面的,通过conbiner减少网络流量、减少输入数据量、使用压缩、重用JVM、代码重构算法改进等。

第七章细则手册

可以向作业传递自定义的参数。
探查作业任务的特定信息。

job.set...
job.get...

划分多个输出文件、目录等。
继承MultipleOutputFormat的某个子类,并实现这个方法:

protected String generateFileNameForKeyValue(k key, v value,String name)

有时候我们不仅仅需要按行来分文件,如果我们需要按列分,这时候应该采用MultipleOutputs。它可以创建多个OutputCollector,每个都可以有自己的OutputFormat和键值对类型。应用类似如下(目测书中给的都是老版本的代码,0.20之后新版本代码在官方API里找到一个样例,这里给出):

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
//Usage pattern for job submission:
 
 Job job = new Job();
 
 FileInputFormat.setInputPath(job, inDir);
 FileOutputFormat.setOutputPath(job, outDir);
 
 job.setMapperClass(MOMap.class);
 job.setReducerClass(MOReduce.class);
 ...
 
 // Defines additional single text based output 'text' for the job
 MultipleOutputs.addNamedOutput(job, "text", TextOutputFormat.class,
 LongWritable.class, Text.class);
 
 // Defines additional sequence-file based output 'sequence' for the job
 MultipleOutputs.addNamedOutput(job, "seq",
   SequenceFileOutputFormat.class,
   LongWritable.class, Text.class);
 ...
 
 job.waitForCompletion(true);
 ...
 
//Usage in Reducer:
 
  String generateFileName(K k, V v) {
   return k.toString() + "_" + v.toString();
  }
 
 public class MOReduce extends
   Reducer {
 private MultipleOutputs mos;
 public void setup(Context context) {
 ...
 mos = new MultipleOutputs(context);
 }
 
 public void reduce(WritableComparable key, Iterator&lt;Writable&gt; values,
 Context context)
 throws IOException {
 ...
 mos.write("text", , key, new Text("Hello"));
 mos.write("seq", LongWritable(1), new Text("Bye"), "seq_a");
 mos.write("seq", LongWritable(2), key, new Text("Chau"), "seq_b");
 mos.write(key, new Text("value"), generateFileName(key, new Text("value")));
 ...
 }
 
 public void cleanup(Context) throws IOException {
 mos.close();
 ...
 }
 
 }

具体的包位置org.apache.hadoop.mapred.lib.MultipleOutputs;
需要注意的是,如果用于map过程,则reducer只能处理原始的Context中的数据。

虽然hadoop不擅长交互数据库,但是很多时候我们还需要以数据库作为输入输出的目标。许多时候需要将数据集使用dump工具获取flat文件然后put复制到HDFS,但是有时候更合理的做法是让MapReduce程序直接写入数据库。DBOutputFormat和DBInputFormat是用于访问数据库的关键类。详细查看org.apache.hadoop.mapred.lib.db;数据库操作仅适用于数据集较小的,否则数据库将成为性能瓶颈。

我们知道reducer的输入都是经过按键排序的,它是按键分组的有效途径,但是有时候我们并不需要按键分组,这样可以消除整个reduce阶段来提高性能。可以将reducer的个数设置为0,从而让应用程序成为一个map-only作业。

但有时候我们又需要排序很好的结果,比如part-00000的记录都小于part-00001,而part-00001又都小于part-00002这样,要做到这点关键还在于框架中的partitioner操作。Partitioner的任务是确保为每个键分配一个reducer。相同的键的所有记录都结合成组,被分在同一个reducer中处理。Partitioner的一个重要设计需求是在reducer之间找到负载平衡,默认是使用散列函数来均匀的将键分配给reducer,这是随意的,不存在顺序的。但是如果实现知道键是大致均匀分布的,我们就可以使用一个partitioner给没一个reducer分配一个键的范围,这样既可以保证reducer负载均衡,又得到了最终结果的排序。TotalOrderPartitioner是一个可以保证在输入分区之间,而不仅仅是分区内部排序的partitioner。

第八章管理hadoop

除了之间教的搭建的时候需要填写的一些参数,以下是一些集群调优的参数:

参数 取值 备注
fs.default.name NameNode的URI。 hdfs://主机名/
mapred.job.tracker JobTracker的主机(或者IP)和端口 主机:端口
dfs.name.dir NameNode持久存储名字空间及事务日志的本地文件系统路径 当这个值是一个逗号分割的目录列表时,nametable数据将会被复制到所有目录中做冗余备份
dfs.data.dir DataNode存放块数据的本地文件系统路径,逗号分割的列表 当这个值是逗号分割的目录列表时,数据将被存储在所有目录下,通常分布在不同设备上
mapred.system.dir Map/Reduce框架存储系统文件的HDFS路径(比如/hadoop/mapred/system/) 这个路径是默认文件系统(HDFS)下的路径, 须从服务器和客户端上均可访问
mapred.local.dir 本地文件系统下逗号分割的路径列表,Map/Reduce临时数据存放的地方 多路径有助于利用磁盘i/o
mapred.tasktracker.{map|reduce}.tasks.maximum 某一TaskTracker上可运行的最大Map/Reduce任务数,这些任务将同时各自运行 默认为2(2个map和2个reduce),可依据硬件情况更改
dfs.hosts/dfs.hosts.exclude 许可/拒绝DataNode列表 如有必要,用这个文件控制许可的datanode列表
mapred.hosts/mapred.hosts.exclude 许可/拒绝TaskTracker列表 如有必要,用这个文件控制许可的TaskTracker列表
hadoop.tmp.dir hadoop临时目录 /home/hadoop/tmp
dfs.datanode.du.reserved datanode应该具备的最小空间 1073741824
mapred.child.java.opts 分配给每个子任务的堆栈大小 -Xmx512m
mapred.reduce.tasks 一个作业的reduce任务个数
fs.trash.interval 启用hdfs回收站机制,默认为0分钟不启动 1440

更为详细的请参考官方网站的说明,core-defaulthdfs-default、还有mapred-default

hadoop提供文件系统检查工具叫fsck:

$ hadoop fsck /

HDFS是自我修复的,所以过度复制、复制不足等都不是问题,但是损坏的意味着数据永远丢失了。默认情况下fsck什么也不做,一般可以使用-delete删掉,也可以-move到/lost+found目录中备用。
还可以加上-files、-blocks、-locations、-racks等选项,已打印更多信息,每个后续选项需要前边的选项存在。

权限设置的命令主要在fs里。

配额管理命令在dfsadmin里,配额管理主要防止用户生成过多小文件,令namenode负担过重。

删减datanode和添加datanode。
使用hadoop提供的退役(decommissioning)功能。该功能确保所有块在剩余的活动节点上仍达到所需的副本数。要使用此功能,必须在namenode的本地文件系统上生成一个排除文件(最初为空),并让参数dfs.hosts.exclude参数在namenode的启动过程中指向该文件,当你想删减datanode时,把他们列在排除文件中,每行列一个节点。还必须用完整的主机名、ip或ip:port的格式来指定节点,执行命令:

$ hadoop dfsadmin -refreshNodes

来强制namenode重新读取排除文件,并开始退役过程。成功标志是namenode日志文件出现“Decommission complete for node ip:port”这样的消息。如果启动时没让dfs.hosts.exclude参数指向排空文件,那么就停止namenode,然后设置指向后重启。如果排空文件在重启namenode非空,那么namenode就会混淆,而且日志里引发“ProcessReport from unregistered node: ip:port”这样的消息,namenode会认为它接触的是系统之外的datanode,而不是即将去除的节点。如果离线的节点需要回来,则删除排空文件里的相应行,然后执行上边的刷新节点命令即可。

如果需要添加一个新节点,那么手动启动相应的节点的datanode和tasktracker进程,更新主服务器的slaves文件即可。此时新节点是空的,为了hdfs平衡,启用平衡工具:

$ start-balancer.sh

即可,管理员也可以提前终止该命令:

$ stop-balancer.sh

可以在启动平衡命令行后加上-threshold num 来确定阈值半分比。一般均衡操作放在集群空闲的时候做。或者也可以设置dfs.balance.bandwidthPerSec参数,来限制用于做均衡占用的带宽。

Namenode重要性不再赘述,一般放在性能最好内存很大的服务器上。默认SNN(Secondary NameNode)运行在同一服务器。SNN的作用是定期清理NameNode上文件系统的状态信息,使之紧凑,进而帮助NameNode变得更有效率。NameNode使用FsImage和EditLog两个文件来管理文件系统的状态信息。文件FsImage是文件系统在一些检查点上的快照,EditLog记录在此检查点之后文件系统的每个增量修改(delta)。通过这两个文件可以完全确定文件系统的当前状态。当初始化NameNode时,它将合并这两个文件来创建新的快照。在NameNode初始化结束时,FsImage将包含新的快照,而EditLog将为空。之后任何导致HDFS状态改变的操作都被追加到EditLog,而FsImage保持不变。NameNode关闭重启时,将再次合并。需要注意这两个文件仅在NameNode不运行时文件系统保留的状态信息。NameNode将文件系统的状态信息常驻在内存中,以快速响应对文件系统的相关查询。

在繁忙的集群中,EditLog会非常大,并且NameNode在下次重启将花很久才能合并两个文件。而且一般hadoop也很久才重启一次。此时SNN就发挥作用了。它合并FsImage和EditLog到一个新的快照中,并让NameNode专注于活动事务。SNN合并两个文件也很耗内存,一般单独放在同NameNode相当的服务器上。需要单独列SNN的,需要修改master文件,添加SNN的ip,另外还需要SNN上修改hdfs-site.xml文件,让属性dfs.http.address指向NameNode主机地址的端口50070。必须设置为50070,因为SNN是通过网址的HTTP的Get请求获取两个文件信息的。合并后通过相同的地址和端口来更新NameNode的。

当然SNN也被用来设计成NameNode失效的备用机。

关于网络布局和机架设计感知,你必须告诉hadoop。为了帮助Hadoop知道每个节点的位置,还必须提供一个可执行的脚本,放在主节点上,它的位置需要topology.script.file.name属性来指出。hadoop调用脚本时会使用一组IP地址作为彼此独立的参数。该脚本会以相同的顺序打印出每个IP地址所对应的机架名称,中间以空格分隔。属性topology.script.number.args控制在任何一时刻Hadoop所请求的IP地址的最大数目。简便办法是将这个值设为1,以简化这个脚本程序。一个样例,拓扑图示例如下:
topology
示例程序如下:

1
2
3
4
5
6
7
8
#! /bin/bash
ipaddr=$1
segments=`echo $ipaddr | cut --delimiter=. --fields=4`
if [ "$segments" -lt 128 ] ; then
    echo /rack-1
else
    echo /rack-2
fi

这个程序取IPv4地址的最后一个字段,如果小于128被认为在第一个机架,否则在第二个机架。如果没有网络拓扑脚本则被默认是平坦拓扑。
机架被识别的名称类似路径名,上图中H1\H2\H3的机架名是/D1/R1,类似这样。

hadoop的多用户调度。默认采用的是FIFO调度器,还有另外的Facebook开发的公平调度器(Fair Scheduler)和雅虎开发的容量调度器(Capacity Scheduler)。详细的使用另行查资料。

第九章在云上运行hadoop

主要教了AWS(Amazon Web Services)的应用。

第十章用Pig编程

我猜你可能也喜欢:

No Comments - Leave a comment

Leave a comment

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


Welcome , today is 星期五, 2017 年 09 月 22 日