diff --git a/src/docs/changes/ChangesFancyStyle.css b/src/docs/changes/ChangesFancyStyle.css new file mode 100644 index 00000000000..5eef2412b6a --- /dev/null +++ b/src/docs/changes/ChangesFancyStyle.css @@ -0,0 +1,170 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * General + */ + +img { border: 0; } + +#content table { + border: 0; + width: 100%; +} +/*Hack to get IE to render the table at 100%*/ +* html #content table { margin-left: -3px; } + +#content th, +#content td { + margin: 0; + padding: 0; + vertical-align: top; +} + +.clearboth { + clear: both; +} + +.note, .warning, .fixme { + border: solid black 1px; + margin: 1em 3em; +} + +.note .label { + background: #369; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.note .content { + background: #F0F0FF; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.warning .label { + background: #C00; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.warning .content { + background: #FFF0F0; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.fixme .label { + background: #C6C600; + color: black; + font-weight: bold; + padding: 5px 10px; +} +.fixme .content { + padding: 5px 10px; +} + +/** + * Typography + */ + +body { + font-family: verdana, "Trebuchet MS", arial, helvetica, sans-serif; + font-size: 100%; +} + +#content { + font-family: Georgia, Palatino, Times, serif; + font-size: 95%; +} +#tabs { + font-size: 70%; +} +#menu { + font-size: 80%; +} +#footer { + font-size: 70%; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", verdana, arial, helvetica, sans-serif; + font-weight: bold; + margin-top: 1em; + margin-bottom: .5em; +} + +h1 { + margin-top: 0; + margin-bottom: 1em; + font-size: 1.4em; + background-color: 73CAFF +} +#content h1 { + font-size: 160%; + margin-bottom: .5em; +} +#menu h1 { + margin: 0; + padding: 10px; + background: #336699; + color: white; +} +h2 { + font-size: 120%; + background-color: 73CAFF +} +h3 { font-size: 100%; } +h4 { font-size: 90%; } +h5 { font-size: 80%; } +h6 { font-size: 75%; } + +p { + line-height: 120%; + text-align: left; + margin-top: .5em; + margin-bottom: 1em; +} + +#content li, +#content th, +#content td, +#content li ul, +#content li ol{ + margin-top: .5em; + margin-bottom: .5em; +} + + +#content li li, +#minitoc-area li{ + margin-top: 0em; + margin-bottom: 0em; +} + +#content .attribution { + text-align: right; + font-style: italic; + font-size: 85%; + margin-top: 1em; +} + +.codefrag { + font-family: "Courier New", Courier, monospace; + font-size: 110%; +} diff --git a/src/docs/changes/ChangesSimpleStyle.css b/src/docs/changes/ChangesSimpleStyle.css new file mode 100644 index 00000000000..407d0f1cf6d --- /dev/null +++ b/src/docs/changes/ChangesSimpleStyle.css @@ -0,0 +1,49 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +body { + font-family: Courier New, monospace; + font-size: 10pt; +} + +h1 { + font-family: Courier New, monospace; + font-size: 10pt; +} + +h2 { + font-family: Courier New, monospace; + font-size: 10pt; +} + +h3 { + font-family: Courier New, monospace; + font-size: 10pt; +} + +a:link { + color: blue; +} + +a:visited { + color: purple; +} + +li { + margin-top: 1em; + margin-bottom: 1em; +} diff --git a/src/docs/changes/changes2html.pl b/src/docs/changes/changes2html.pl new file mode 100755 index 00000000000..67e1826e116 --- /dev/null +++ b/src/docs/changes/changes2html.pl @@ -0,0 +1,282 @@ +#!/usr/bin/perl +# +# Transforms Lucene Java's CHANGES.txt into Changes.html +# +# Input is on STDIN, output is to STDOUT +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +use strict; +use warnings; + +my $jira_url_prefix = 'http://issues.apache.org/jira/browse/'; +my $title = undef; +my $release = undef; +my $sections = undef; +my $items = undef; +my $first_relid = undef; +my $second_relid = undef; +my @releases = (); + +my @lines = <>; # Get all input at once + +# +# Parse input and build hierarchical release structure in @releases +# +for (my $line_num = 0 ; $line_num <= $#lines ; ++$line_num) { + $_ = $lines[$line_num]; + next unless (/\S/); # Skip blank lines + + unless ($title) { + if (/\S/) { + s/^\s+//; # Trim leading whitespace + s/\s+$//; # Trim trailing whitespace + } + $title = $_; + next; + } + + if (/^(Release)|(Trunk)/) { # Release headings + $release = $_; + $sections = []; + push @releases, [ $release, $sections ]; + ($first_relid = lc($release)) =~ s/\s+/_/g if ($#releases == 0); + ($second_relid = lc($release)) =~ s/\s+/_/g if ($#releases == 1); + $items = undef; + next; + } + + # Section heading: 2 leading spaces, words all capitalized + if (/^ ([A-Z]+)\s*/) { + my $heading = $_; + $items = []; + push @$sections, [ $heading, $items ]; + next; + } + + # Handle earlier releases without sections - create a headless section + unless ($items) { + $items = []; + push @$sections, [ undef, $items ]; + } + + my $type; + if (@$items) { # A list item has been encountered in this section before + $type = $items->[0]; # 0th position of items array is list type + } else { + $type = get_list_type($_); + push @$items, $type; + } + + if ($type eq 'numbered') { # The modern items list style + # List item boundary is another numbered item or an unindented line + my $line; + my $item = $_; + $item =~ s/^(\s{0,2}\d+\.\s*)//; # Trim the leading item number + my $leading_ws_width = length($1); + $item =~ s/\s+$//; # Trim trailing whitespace + $item .= "\n"; + + while ($line_num < $#lines + and ($line = $lines[++$line_num]) !~ /^(?:\s{0,2}\d+\.\s*\S|\S)/) { + $line =~ s/^\s{$leading_ws_width}//; # Trim leading whitespace + $line =~ s/\s+$//; # Trim trailing whitespace + $item .= "$line\n"; + } + $item =~ s/\n+\Z/\n/; # Trim trailing blank lines + push @$items, $item; + --$line_num unless ($line_num == $#lines); + } elsif ($type eq 'paragraph') { # List item boundary is a blank line + my $line; + my $item = $_; + $item =~ s/^(\s+)//; + my $leading_ws_width = defined($1) ? length($1) : 0; + $item =~ s/\s+$//; # Trim trailing whitespace + $item .= "\n"; + + while ($line_num < $#lines and ($line = $lines[++$line_num]) =~ /\S/) { + $line =~ s/^\s{$leading_ws_width}//; # Trim leading whitespace + $line =~ s/\s+$//; # Trim trailing whitespace + $item .= "$line\n"; + } + push @$items, $item; + --$line_num unless ($line_num == $#lines); + } else { # $type is one of the bulleted types + # List item boundary is another bullet or a blank line + my $line; + my $item = $_; + $item =~ s/^(\s*$type\s*)//; # Trim the leading bullet + my $leading_ws_width = length($1); + $item =~ s/\s+$//; # Trim trailing whitespace + $item .= "\n"; + + while ($line_num < $#lines + and ($line = $lines[++$line_num]) !~ /^\s*(?:$type|\Z)/) { + $line =~ s/^\s{$leading_ws_width}//; # Trim leading whitespace + $line =~ s/\s+$//; # Trim trailing whitespace + $item .= "$line\n"; + } + push @$items, $item; + --$line_num unless ($line_num == $#lines); + } +} + +# +# Print HTML-ified version to STDOUT +# +print<<"__HTML_HEADER__"; + + + + $title + + + + + + + +Hadoop +

$title

+ +__HTML_HEADER__ + +my $heading; +my $relcnt = 0; +my $header = 'h2'; +for my $rel (@releases) { + if (++$relcnt == 3) { + $header = 'h3'; + print "

"; + print "Older Releases"; + print "

\n"; + print "\n" if ($relcnt > 3); +print "\n\n"; + + +# +# Subroutine: get_list_type +# +# Takes one parameter: +# +# - The first line of a sub-section/point +# +# Returns one scalar: +# +# - The list type: 'numbered'; or one of the bulleted types '-', or '.' or +# 'paragraph'. +# +sub get_list_type { + my $first_list_item_line = shift; + my $type = 'paragraph'; # Default to paragraph type + + if ($first_list_item_line =~ /^\s{0,2}\d+\.\s+\S+/) { + $type = 'numbered'; + } elsif ($first_list_item_line =~ /^\s*([-.])\s+\S+/) { + $type = $1; + } + return $type; +} + +1; diff --git a/src/docs/cn/changes/ChangesFancyStyle.css b/src/docs/cn/changes/ChangesFancyStyle.css new file mode 100644 index 00000000000..5eef2412b6a --- /dev/null +++ b/src/docs/cn/changes/ChangesFancyStyle.css @@ -0,0 +1,170 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * General + */ + +img { border: 0; } + +#content table { + border: 0; + width: 100%; +} +/*Hack to get IE to render the table at 100%*/ +* html #content table { margin-left: -3px; } + +#content th, +#content td { + margin: 0; + padding: 0; + vertical-align: top; +} + +.clearboth { + clear: both; +} + +.note, .warning, .fixme { + border: solid black 1px; + margin: 1em 3em; +} + +.note .label { + background: #369; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.note .content { + background: #F0F0FF; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.warning .label { + background: #C00; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.warning .content { + background: #FFF0F0; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.fixme .label { + background: #C6C600; + color: black; + font-weight: bold; + padding: 5px 10px; +} +.fixme .content { + padding: 5px 10px; +} + +/** + * Typography + */ + +body { + font-family: verdana, "Trebuchet MS", arial, helvetica, sans-serif; + font-size: 100%; +} + +#content { + font-family: Georgia, Palatino, Times, serif; + font-size: 95%; +} +#tabs { + font-size: 70%; +} +#menu { + font-size: 80%; +} +#footer { + font-size: 70%; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", verdana, arial, helvetica, sans-serif; + font-weight: bold; + margin-top: 1em; + margin-bottom: .5em; +} + +h1 { + margin-top: 0; + margin-bottom: 1em; + font-size: 1.4em; + background-color: 73CAFF +} +#content h1 { + font-size: 160%; + margin-bottom: .5em; +} +#menu h1 { + margin: 0; + padding: 10px; + background: #336699; + color: white; +} +h2 { + font-size: 120%; + background-color: 73CAFF +} +h3 { font-size: 100%; } +h4 { font-size: 90%; } +h5 { font-size: 80%; } +h6 { font-size: 75%; } + +p { + line-height: 120%; + text-align: left; + margin-top: .5em; + margin-bottom: 1em; +} + +#content li, +#content th, +#content td, +#content li ul, +#content li ol{ + margin-top: .5em; + margin-bottom: .5em; +} + + +#content li li, +#minitoc-area li{ + margin-top: 0em; + margin-bottom: 0em; +} + +#content .attribution { + text-align: right; + font-style: italic; + font-size: 85%; + margin-top: 1em; +} + +.codefrag { + font-family: "Courier New", Courier, monospace; + font-size: 110%; +} diff --git a/src/docs/cn/changes/ChangesSimpleStyle.css b/src/docs/cn/changes/ChangesSimpleStyle.css new file mode 100644 index 00000000000..407d0f1cf6d --- /dev/null +++ b/src/docs/cn/changes/ChangesSimpleStyle.css @@ -0,0 +1,49 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +body { + font-family: Courier New, monospace; + font-size: 10pt; +} + +h1 { + font-family: Courier New, monospace; + font-size: 10pt; +} + +h2 { + font-family: Courier New, monospace; + font-size: 10pt; +} + +h3 { + font-family: Courier New, monospace; + font-size: 10pt; +} + +a:link { + color: blue; +} + +a:visited { + color: purple; +} + +li { + margin-top: 1em; + margin-bottom: 1em; +} diff --git a/src/docs/cn/changes/changes2html.pl b/src/docs/cn/changes/changes2html.pl new file mode 100644 index 00000000000..67e1826e116 --- /dev/null +++ b/src/docs/cn/changes/changes2html.pl @@ -0,0 +1,282 @@ +#!/usr/bin/perl +# +# Transforms Lucene Java's CHANGES.txt into Changes.html +# +# Input is on STDIN, output is to STDOUT +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +use strict; +use warnings; + +my $jira_url_prefix = 'http://issues.apache.org/jira/browse/'; +my $title = undef; +my $release = undef; +my $sections = undef; +my $items = undef; +my $first_relid = undef; +my $second_relid = undef; +my @releases = (); + +my @lines = <>; # Get all input at once + +# +# Parse input and build hierarchical release structure in @releases +# +for (my $line_num = 0 ; $line_num <= $#lines ; ++$line_num) { + $_ = $lines[$line_num]; + next unless (/\S/); # Skip blank lines + + unless ($title) { + if (/\S/) { + s/^\s+//; # Trim leading whitespace + s/\s+$//; # Trim trailing whitespace + } + $title = $_; + next; + } + + if (/^(Release)|(Trunk)/) { # Release headings + $release = $_; + $sections = []; + push @releases, [ $release, $sections ]; + ($first_relid = lc($release)) =~ s/\s+/_/g if ($#releases == 0); + ($second_relid = lc($release)) =~ s/\s+/_/g if ($#releases == 1); + $items = undef; + next; + } + + # Section heading: 2 leading spaces, words all capitalized + if (/^ ([A-Z]+)\s*/) { + my $heading = $_; + $items = []; + push @$sections, [ $heading, $items ]; + next; + } + + # Handle earlier releases without sections - create a headless section + unless ($items) { + $items = []; + push @$sections, [ undef, $items ]; + } + + my $type; + if (@$items) { # A list item has been encountered in this section before + $type = $items->[0]; # 0th position of items array is list type + } else { + $type = get_list_type($_); + push @$items, $type; + } + + if ($type eq 'numbered') { # The modern items list style + # List item boundary is another numbered item or an unindented line + my $line; + my $item = $_; + $item =~ s/^(\s{0,2}\d+\.\s*)//; # Trim the leading item number + my $leading_ws_width = length($1); + $item =~ s/\s+$//; # Trim trailing whitespace + $item .= "\n"; + + while ($line_num < $#lines + and ($line = $lines[++$line_num]) !~ /^(?:\s{0,2}\d+\.\s*\S|\S)/) { + $line =~ s/^\s{$leading_ws_width}//; # Trim leading whitespace + $line =~ s/\s+$//; # Trim trailing whitespace + $item .= "$line\n"; + } + $item =~ s/\n+\Z/\n/; # Trim trailing blank lines + push @$items, $item; + --$line_num unless ($line_num == $#lines); + } elsif ($type eq 'paragraph') { # List item boundary is a blank line + my $line; + my $item = $_; + $item =~ s/^(\s+)//; + my $leading_ws_width = defined($1) ? length($1) : 0; + $item =~ s/\s+$//; # Trim trailing whitespace + $item .= "\n"; + + while ($line_num < $#lines and ($line = $lines[++$line_num]) =~ /\S/) { + $line =~ s/^\s{$leading_ws_width}//; # Trim leading whitespace + $line =~ s/\s+$//; # Trim trailing whitespace + $item .= "$line\n"; + } + push @$items, $item; + --$line_num unless ($line_num == $#lines); + } else { # $type is one of the bulleted types + # List item boundary is another bullet or a blank line + my $line; + my $item = $_; + $item =~ s/^(\s*$type\s*)//; # Trim the leading bullet + my $leading_ws_width = length($1); + $item =~ s/\s+$//; # Trim trailing whitespace + $item .= "\n"; + + while ($line_num < $#lines + and ($line = $lines[++$line_num]) !~ /^\s*(?:$type|\Z)/) { + $line =~ s/^\s{$leading_ws_width}//; # Trim leading whitespace + $line =~ s/\s+$//; # Trim trailing whitespace + $item .= "$line\n"; + } + push @$items, $item; + --$line_num unless ($line_num == $#lines); + } +} + +# +# Print HTML-ified version to STDOUT +# +print<<"__HTML_HEADER__"; + + + + $title + + + + + + + +Hadoop +

$title

+ +__HTML_HEADER__ + +my $heading; +my $relcnt = 0; +my $header = 'h2'; +for my $rel (@releases) { + if (++$relcnt == 3) { + $header = 'h3'; + print "

"; + print "Older Releases"; + print "

\n"; + print "\n" if ($relcnt > 3); +print "\n\n"; + + +# +# Subroutine: get_list_type +# +# Takes one parameter: +# +# - The first line of a sub-section/point +# +# Returns one scalar: +# +# - The list type: 'numbered'; or one of the bulleted types '-', or '.' or +# 'paragraph'. +# +sub get_list_type { + my $first_list_item_line = shift; + my $type = 'paragraph'; # Default to paragraph type + + if ($first_list_item_line =~ /^\s{0,2}\d+\.\s+\S+/) { + $type = 'numbered'; + } elsif ($first_list_item_line =~ /^\s*([-.])\s+\S+/) { + $type = $1; + } + return $type; +} + +1; diff --git a/src/docs/cn/forrest.properties b/src/docs/cn/forrest.properties new file mode 100644 index 00000000000..0bfc7f8b4d8 --- /dev/null +++ b/src/docs/cn/forrest.properties @@ -0,0 +1,109 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############## +# Properties used by forrest.build.xml for building the website +# These are the defaults, un-comment them if you need to change them. +############## + +# Prints out a summary of Forrest settings for this project +#forrest.echo=true + +# Project name (used to name .war file) +#project.name=my-project + +# Specifies name of Forrest skin to use +#project.skin=tigris +#project.skin=pelt + +# comma separated list, file:// is supported +#forrest.skins.descriptors=http://forrest.apache.org/skins/skins.xml,file:///c:/myskins/skins.xml + +############## +# behavioural properties +#project.menu-scheme=tab_attributes +#project.menu-scheme=directories + +############## +# layout properties + +# Properties that can be set to override the default locations +# +# Parent properties must be set. This usually means uncommenting +# project.content-dir if any other property using it is uncommented + +#project.status=status.xml +#project.content-dir=src/documentation +#project.raw-content-dir=${project.content-dir}/content +#project.conf-dir=${project.content-dir}/conf +#project.sitemap-dir=${project.content-dir} +#project.xdocs-dir=${project.content-dir}/content/xdocs +#project.resources-dir=${project.content-dir}/resources +#project.stylesheets-dir=${project.resources-dir}/stylesheets +#project.images-dir=${project.resources-dir}/images +#project.schema-dir=${project.resources-dir}/schema +#project.skins-dir=${project.content-dir}/skins +#project.skinconf=${project.content-dir}/skinconf.xml +#project.lib-dir=${project.content-dir}/lib +#project.classes-dir=${project.content-dir}/classes +#project.translations-dir=${project.content-dir}/translations + +############## +# validation properties + +# This set of properties determine if validation is performed +# Values are inherited unless overridden. +# e.g. if forrest.validate=false then all others are false unless set to true. +#forrest.validate=true +#forrest.validate.xdocs=${forrest.validate} +#forrest.validate.skinconf=${forrest.validate} +#forrest.validate.sitemap=${forrest.validate} +#forrest.validate.stylesheets=${forrest.validate} +#forrest.validate.skins=${forrest.validate} +#forrest.validate.skins.stylesheets=${forrest.validate.skins} + +# *.failonerror=(true|false) - stop when an XML file is invalid +#forrest.validate.failonerror=true + +# *.excludes=(pattern) - comma-separated list of path patterns to not validate +# e.g. +#forrest.validate.xdocs.excludes=samples/subdir/**, samples/faq.xml +#forrest.validate.xdocs.excludes= + + +############## +# General Forrest properties + +# The URL to start crawling from +#project.start-uri=linkmap.html +# Set logging level for messages printed to the console +# (DEBUG, INFO, WARN, ERROR, FATAL_ERROR) +#project.debuglevel=ERROR +# Max memory to allocate to Java +forrest.maxmemory=512m +# Any other arguments to pass to the JVM. For example, to run on an X-less +# server, set to -Djava.awt.headless=true +#forrest.jvmargs= +# The bugtracking URL - the issue number will be appended +#project.bugtracking-url=http://issues.apache.org/bugzilla/show_bug.cgi?id= +#project.bugtracking-url=http://issues.apache.org/jira/browse/ +# The issues list as rss +#project.issues-rss-url= +#I18n Property only works for the "forrest run" target. +#project.i18n=true +project.configfile=${project.home}/src/documentation/conf/cli.xconf + diff --git a/src/docs/cn/src/documentation/README.txt b/src/docs/cn/src/documentation/README.txt new file mode 100644 index 00000000000..9bc261b2f15 --- /dev/null +++ b/src/docs/cn/src/documentation/README.txt @@ -0,0 +1,7 @@ +This is the base documentation directory. + +skinconf.xml # This file customizes Forrest for your project. In it, you + # tell forrest the project name, logo, copyright info, etc + +sitemap.xmap # Optional. This sitemap is consulted before all core sitemaps. + # See http://forrest.apache.org/docs/project-sitemap.html diff --git a/src/docs/cn/src/documentation/classes/CatalogManager.properties b/src/docs/cn/src/documentation/classes/CatalogManager.properties new file mode 100644 index 00000000000..b9cb5848fbf --- /dev/null +++ b/src/docs/cn/src/documentation/classes/CatalogManager.properties @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#======================================================================= +# CatalogManager.properties +# +# This is the default properties file for Apache Forrest. +# This facilitates local configuration of application-specific catalogs. +# +# See the Apache Forrest documentation: +# http://forrest.apache.org/docs/your-project.html +# http://forrest.apache.org/docs/validation.html + +# verbosity ... level of messages for status/debug +# See forrest/src/core/context/WEB-INF/cocoon.xconf + +# catalogs ... list of additional catalogs to load +# (Note that Apache Forrest will automatically load its own default catalog +# from src/core/context/resources/schema/catalog.xcat) +# use full pathnames +# pathname separator is always semi-colon (;) regardless of operating system +# directory separator is always slash (/) regardless of operating system +# +#catalogs=/home/me/forrest/my-site/src/documentation/resources/schema/catalog.xcat +catalogs= + diff --git a/src/docs/cn/src/documentation/conf/cli.xconf b/src/docs/cn/src/documentation/conf/cli.xconf new file mode 100644 index 00000000000..5c6e245688c --- /dev/null +++ b/src/docs/cn/src/documentation/conf/cli.xconf @@ -0,0 +1,327 @@ + + + + + + + + . + WEB-INF/cocoon.xconf + ../tmp/cocoon-work + ../site + + + + + + + + + + + + + + + index.html + + + + + + + */* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/content/xdocs/cluster_setup.xml b/src/docs/cn/src/documentation/content/xdocs/cluster_setup.xml new file mode 100644 index 00000000000..0bf6c0e15ee --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/cluster_setup.xml @@ -0,0 +1,386 @@ + + + + + + + +
+ Hadoop集群搭建 +
+ + + +
+ 目的 + +

本文描述了如何安装、配置和管理有实际意义的Hadoop集群,其规模可从几个节点的小集群到几千个节点的超大集群。

+ +

如果你希望在单机上安装Hadoop玩玩,从这里能找到相关细节。

+
+ +
+ 先决条件 + +
    +
  1. + 确保在你集群中的每个节点上都安装了所有必需软件。 +
  2. +
  3. + 获取Hadoop软件包。 +
  4. +
+
+ +
+ 安装 + +

安装Hadoop集群通常要将安装软件解压到集群内的所有机器上。

+ +

通常,集群里的一台机器被指定为 + NameNode,另一台不同的机器被指定为JobTracker。这些机器是masters。余下的机器即作为DataNode作为TaskTracker。这些机器是slaves

+ +

我们用HADOOP_HOME指代安装的根路径。通常,集群里的所有机器的HADOOP_HOME路径相同。

+
+ +
+ 配置 + +

接下来的几节描述了如何配置Hadoop集群。

+ +
+ 配置文件 + +

对Hadoop的配置通过conf/目录下的两个重要配置文件完成:

+
    +
  1. + hadoop-default.xml - 只读的默认配置。 +
  2. +
  3. + hadoop-site.xml - 集群特有的配置。 +
  4. +
+ +

要了解更多关于这些配置文件如何影响Hadoop框架的细节,请看这里

+ +

此外,通过设置conf/hadoop-env.sh中的变量为集群特有的值,你可以对bin/目录下的Hadoop脚本进行控制。

+
+ +
+ 集群配置 + +

要配置Hadoop集群,你需要设置Hadoop守护进程的运行环境和Hadoop守护进程的运行参数

+ +

Hadoop守护进程指NameNode/DataNode + 和JobTracker/TaskTracker

+ +
+ 配置Hadoop守护进程的运行环境 + +

管理员可在conf/hadoop-env.sh脚本内对Hadoop守护进程的运行环境做特别指定。

+

至少,你得设定JAVA_HOME使之在每一远端节点上都被正确设置。

+

管理员可以通过配置选项HADOOP_*_OPTS来分别配置各个守护进程。 + 下表是可以配置的选项。 +

+ + + + + + + + +
守护进程配置选项
NameNodeHADOOP_NAMENODE_OPTS
DataNodeHADOOP_DATANODE_OPTS
SecondaryNamenodeHADOOP_SECONDARYNAMENODE_OPTS
JobTrackerHADOOP_JOBTRACKER_OPTS
TaskTrackerHADOOP_TASKTRACKER_OPTS
+ +

例如,配置Namenode时,为了使其能够并行回收垃圾(parallelGC), + 要把下面的代码加入到hadoop-env.sh : +
+ export HADOOP_NAMENODE_OPTS="-XX:+UseParallelGC ${HADOOP_NAMENODE_OPTS}" +

+

其它可定制的常用参数还包括:

+
    +
  • + HADOOP_LOG_DIR - 守护进程日志文件的存放目录。如果不存在会被自动创建。 +
  • +
  • + HADOOP_HEAPSIZE - 最大可用的堆大小,单位为MB。比如,1000MB。 + 这个参数用于设置hadoop守护进程的堆大小。缺省大小是1000MB。 +
  • +
+
+ +
+ 配置Hadoop守护进程的运行参数 + +

这部分涉及Hadoop集群的重要参数,这些参数在conf/hadoop-site.xml中指定。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数取值备注
fs.default.nameNameNode的URI。hdfs://主机名/
mapred.job.trackerJobTracker的主机(或者IP)和端口。主机:端口
dfs.name.dir + NameNode持久存储名字空间及事务日志的本地文件系统路径。当这个值是一个逗号分割的目录列表时,nametable数据将会被复制到所有目录中做冗余备份。 +
dfs.data.dir + DataNode存放块数据的本地文件系统路径,逗号分割的列表。 + + 当这个值是逗号分割的目录列表时,数据将被存储在所有目录下,通常分布在不同设备上。 +
mapred.system.dirMap/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列表。 +
+ +

通常,上述参数被标记为 + + final 以确保它们不被用户应用更改。 +

+ +
+ 现实世界的集群配置 + +

这节罗列在大规模集群上运行sort基准测试(benchmark)时使用到的一些非缺省配置。

+ +
    +
  • +

    运行sort900的一些非缺省配置值,sort900即在900个节点的集群上对9TB的数据进行排序:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数取值备注
    dfs.block.size134217728针对大文件系统,HDFS的块大小取128MB。
    dfs.namenode.handler.count40 + 启动更多的NameNode服务线程去处理来自大量DataNode的RPC请求。 +
    mapred.reduce.parallel.copies20 + reduce启动更多的并行拷贝器以获取大量map的输出。 +
    mapred.child.java.opts-Xmx512M + 为map/reduce子虚拟机使用更大的堆。 +
    fs.inmemory.size.mb200 + 为reduce阶段合并map输出所需的内存文件系统分配更多的内存。 +
    io.sort.factor100文件排序时更多的流将同时被归并。
    io.sort.mb200提高排序时的内存上限。
    io.file.buffer.size131072SequenceFile中用到的读/写缓存大小。
    +
  • +
  • +

    运行sort1400和sort2000时需要更新的配置,即在1400个节点上对14TB的数据进行排序和在2000个节点上对20TB的数据进行排序:

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数取值备注
    mapred.job.tracker.handler.count60 + 启用更多的JobTracker服务线程去处理来自大量TaskTracker的RPC请求。 +
    mapred.reduce.parallel.copies50
    tasktracker.http.threads50 + 为TaskTracker的Http服务启用更多的工作线程。reduce通过Http服务获取map的中间输出。 +
    mapred.child.java.opts-Xmx1024M使用更大的堆用于maps/reduces的子虚拟机
    +
  • +
+
+ +
+ +
+ Slaves + +

通常,你选择集群中的一台机器作为NameNode,另外一台不同的机器作为JobTracker。余下的机器即作为DataNode又作为TaskTracker,这些被称之为slaves

+ +

conf/slaves文件中列出所有slave的主机名或者IP地址,一行一个。

+
+ +
+ 日志 + +

Hadoop使用Apache log4j来记录日志,它由Apache Commons Logging框架来实现。编辑conf/log4j.properties文件可以改变Hadoop守护进程的日志配置(日志格式等)。

+ +
+ 历史日志 + +

作业的历史文件集中存放在hadoop.job.history.location,这个也可以是在分布式文件系统下的路径,其默认值为${HADOOP_LOG_DIR}/history。jobtracker的web UI上有历史日志的web UI链接。

+ +

历史文件在用户指定的目录hadoop.job.history.user.location也会记录一份,这个配置的缺省值为作业的输出目录。这些文件被存放在指定路径下的“_logs/history/”目录中。因此,默认情况下日志文件会在“mapred.output.dir/_logs/history/”下。如果将hadoop.job.history.user.location指定为值none,系统将不再记录此日志。

+ +

用户可使用以下命令在指定路径下查看历史日志汇总
+ $ bin/hadoop job -history output-dir
+ 这条命令会显示作业的细节信息,失败和终止的任务细节。
+ 关于作业的更多细节,比如成功的任务,以及对每个任务的所做的尝试次数等可以用下面的命令查看
+ $ bin/hadoop job -history all output-dir

+
+
+
+ +

一但全部必要的配置完成,将这些文件分发到所有机器的HADOOP_CONF_DIR路径下,通常是${HADOOP_HOME}/conf

+
+ +
+ Hadoop的机架感知 +

HDFS和Map/Reduce的组件是能够感知机架的。

+

NameNodeJobTracker通过调用管理员配置模块中的APIresolve来获取集群里每个slave的机架id。该API将slave的DNS名称(或者IP地址)转换成机架id。使用哪个模块是通过配置项topology.node.switch.mapping.impl来指定的。模块的默认实现会调用topology.script.file.name配置项指定的一个的脚本/命令。 如果topology.script.file.name未被设置,对于所有传入的IP地址,模块会返回/default-rack作为机架id。在Map/Reduce部分还有一个额外的配置项mapred.cache.task.levels,该参数决定cache的级数(在网络拓扑中)。例如,如果默认值是2,会建立两级的cache- 一级针对主机(主机 -> 任务的映射)另一级针对机架(机架 -> 任务的映射)。 +

+
+ +
+ 启动Hadoop + +

启动Hadoop集群需要启动HDFS集群和Map/Reduce集群。

+ +

+ 格式化一个新的分布式文件系统:
+ $ bin/hadoop namenode -format +

+ +

+ 在分配的NameNode上,运行下面的命令启动HDFS:
+ $ bin/start-dfs.sh +

+

bin/start-dfs.sh脚本会参照NameNode${HADOOP_CONF_DIR}/slaves文件的内容,在所有列出的slave上启动DataNode守护进程。

+

+ 在分配的JobTracker上,运行下面的命令启动Map/Reduce:
+ $ bin/start-mapred.sh +

+

bin/start-mapred.sh脚本会参照JobTracker${HADOOP_CONF_DIR}/slaves文件的内容,在所有列出的slave上启动TaskTracker守护进程。

+
+ +
+ 停止Hadoop + +

+ 在分配的NameNode上,执行下面的命令停止HDFS:
+ $ bin/stop-dfs.sh +

+

bin/stop-dfs.sh脚本会参照NameNode${HADOOP_CONF_DIR}/slaves文件的内容,在所有列出的slave上停止DataNode守护进程。

+

+ 在分配的JobTracker上,运行下面的命令停止Map/Reduce:
+ $ bin/stop-mapred.sh
+

+

bin/stop-mapred.sh脚本会参照JobTracker${HADOOP_CONF_DIR}/slaves文件的内容,在所有列出的slave上停止TaskTracker守护进程。

+
+ + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/commands_manual.xml b/src/docs/cn/src/documentation/content/xdocs/commands_manual.xml new file mode 100644 index 00000000000..f461beac212 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/commands_manual.xml @@ -0,0 +1,596 @@ + + + + + +
+ 命令手册 +
+ + +
+ 概述 +

+ 所有的hadoop命令均由bin/hadoop脚本引发。不指定参数运行hadoop脚本会打印所有命令的描述。 +

+

+ 用法:hadoop [--config confdir] [COMMAND] [GENERIC_OPTIONS] [COMMAND_OPTIONS] +

+

+ Hadoop有一个选项解析框架用于解析一般的选项和运行类。 +

+ + + + + + + + + + + + + + + +
命令选项 描述
--config confdir覆盖缺省配置目录。缺省是${HADOOP_HOME}/conf。
GENERIC_OPTIONS多个命令都支持的通用选项。
COMMAND
命令选项S
各种各样的命令和它们的选项会在下面提到。这些命令被分为 + 用户命令 + 管理命令两组。
+
+ 常规选项 +

+ 下面的选项被 + dfsadmin, + fs, fsck和 + job支持。 + 应用程序要实现 + Tool来支持 + + 常规选项。 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GENERIC_OPTION 描述
-conf <configuration file>指定应用程序的配置文件。
-D <property=value>为指定property指定值value。
-fs <local|namenode:port>指定namenode。
-jt <local|jobtracker:port>指定job tracker。只适用于job
-files <逗号分隔的文件列表>指定要拷贝到map reduce集群的文件的逗号分隔的列表。 + 只适用于job
-libjars <逗号分隔的jar列表>指定要包含到classpath中的jar文件的逗号分隔的列表。 + 只适用于job
-archives <逗号分隔的archive列表>指定要被解压到计算节点上的档案文件的逗号分割的列表。 + 只适用于job
+
+
+ +
+ 用户命令 +

hadoop集群用户的常用命令。

+
+ archive +

+ 创建一个hadoop档案文件。参考 Hadoop Archives. +

+

+ 用法:hadoop archive -archiveName NAME <src>* <dest> +

+ + + + + + + + + + + + + + +
命令选项 描述
-archiveName NAME要创建的档案的名字。
src文件系统的路径名,和通常含正则表达的一样。
dest保存档案文件的目标目录。
+
+ +
+ distcp +

+ 递归地拷贝文件或目录。参考DistCp指南以获取等多信息。 +

+

+ 用法:hadoop distcp <srcurl> <desturl> +

+ + + + + + + + + + + +
命令选项 描述
srcurl源Url
desturl目标Url
+
+ +
+ fs +

+ 用法:hadoop fs [GENERIC_OPTIONS] + [COMMAND_OPTIONS] +

+

+ 运行一个常规的文件系统客户端。 +

+

+ 各种命令选项可以参考HDFS Shell指南。 +

+
+ +
+ fsck +

+ 运行HDFS文件系统检查工具。参考Fsck了解更多。 +

+

用法:hadoop fsck [GENERIC_OPTIONS] + <path> [-move | -delete | -openforwrite] [-files [-blocks + [-locations | -racks]]]

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
命令选项 描述
<path>检查的起始目录。
-move移动受损文件到/lost+found
-delete删除受损文件。
-openforwrite打印出写打开的文件。
-files打印出正被检查的文件。
-blocks打印出块信息报告。
-locations打印出每个块的位置信息。
-racks打印出data-node的网络拓扑结构。
+
+ +
+ jar +

+ 运行jar文件。用户可以把他们的Map Reduce代码捆绑到jar文件中,使用这个命令执行。 +

+

+ 用法:hadoop jar <jar> [mainClass] args... +

+

+ streaming作业是通过这个命令执行的。参考Streaming examples中的例子。 +

+

+ Word count例子也是通过jar命令运行的。参考Wordcount example。 +

+
+ +
+ job +

+ 用于和Map Reduce作业交互和命令。 +

+

+ 用法:hadoop job [GENERIC_OPTIONS] + [-submit <job-file>] | [-status <job-id>] | + [-counter <job-id> <group-name> <counter-name>] | [-kill <job-id>] | + [-events <job-id> <from-event-#> <#-of-events>] | [-history [all] <jobOutputDir>] | + [-list [all]] | [-kill-task <task-id>] | [-fail-task <task-id>] +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
命令选项 描述
-submit <job-file>提交作业
-status <job-id>打印map和reduce完成百分比和所有计数器。
-counter <job-id> <group-name> <counter-name>打印计数器的值。
-kill <job-id>杀死指定作业。
-events <job-id> <from-event-#> <#-of-events>打印给定范围内jobtracker接收到的事件细节。
-history [all] <jobOutputDir>-history <jobOutputDir> 打印作业的细节、失败及被杀死原因的细节。更多的关于一个作业的细节比如成功的任务,做过的任务尝试等信息可以通过指定[all]选项查看。 +
-list [all]-list all显示所有作业。-list只显示将要完成的作业。
-kill-task <task-id>杀死任务。被杀死的任务不会不利于失败尝试。
-fail-task <task-id>使任务失败。被失败的任务会对失败尝试不利。
+
+ +
+ pipes +

+ 运行pipes作业。 +

+

+ 用法:hadoop pipes [-conf <path>] [-jobconf <key=value>, <key=value>, ...] + [-input <path>] [-output <path>] [-jar <jar file>] [-inputformat <class>] + [-map <class>] [-partitioner <class>] [-reduce <class>] [-writer <class>] + [-program <executable>] [-reduces <num>] +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
命令选项 描述
-conf <path>作业的配置
-jobconf <key=value>, <key=value>, ...增加/覆盖作业的配置项
-input <path>输入目录
-output <path>输出目录
-jar <jar file>Jar文件名
-inputformat <class>InputFormat类
-map <class>Java Map类
-partitioner <class>Java Partitioner
-reduce <class>Java Reduce类
-writer <class>Java RecordWriter
-program <executable>可执行程序的URI
-reduces <num>reduce个数
+
+ +
+ version +

+ 打印版本信息。 +

+

+ 用法:hadoop version +

+
+ +
+ CLASSNAME +

+ hadoop脚本可用于调调用任何类。 +

+

+ 用法:hadoop CLASSNAME +

+

+ 运行名字为CLASSNAME的类。 +

+
+ +
+ +
+ 管理命令 +

hadoop集群管理员常用的命令。

+
+ balancer +

+ 运行集群平衡工具。管理员可以简单的按Ctrl-C来停止平衡过程。参考Rebalancer了解更多。 +

+

+ 用法:hadoop balancer [-threshold <threshold>] +

+ + + + + + + +
命令选项 描述
-threshold <threshold>磁盘容量的百分比。这会覆盖缺省的阀值。
+
+ +
+ daemonlog +

+ 获取或设置每个守护进程的日志级别。 +

+

+ 用法:hadoop daemonlog -getlevel <host:port> <name>
+ 用法:hadoop daemonlog -setlevel <host:port> <name> <level> +

+ + + + + + + + + + + +
命令选项 描述
-getlevel <host:port> <name>打印运行在<host:port>的守护进程的日志级别。这个命令内部会连接http://<host:port>/logLevel?log=<name>
-setlevel <host:port> <name> <level>设置运行在<host:port>的守护进程的日志级别。这个命令内部会连接http://<host:port>/logLevel?log=<name>
+
+ +
+ datanode +

+ 运行一个HDFS的datanode。 +

+

+ 用法:hadoop datanode [-rollback] +

+ + + + + + + +
命令选项 描述
-rollback将datanode回滚到前一个版本。这需要在停止datanode,分发老的hadoop版本之后使用。 +
+
+ +
+ dfsadmin +

+ 运行一个HDFS的dfsadmin客户端。 +

+

+ 用法:hadoop dfsadmin [GENERIC_OPTIONS] [-report] [-safemode enter | leave | get | wait] [-refreshNodes] + [-finalizeUpgrade] [-upgradeProgress status | details | force] [-metasave filename] + [-setQuota <quota> <dirname>...<dirname>] [-clrQuota <dirname>...<dirname>] + [-help [cmd]] +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
命令选项 描述
-report报告文件系统的基本信息和统计信息。
-safemode enter | leave | get | wait安全模式维护命令。安全模式是Namenode的一个状态,这种状态下,Namenode
+ 1. 不接受对名字空间的更改(只读)
+ 2. 不复制或删除块
+ Namenode会在启动时自动进入安全模式,当配置的块最小百分比数满足最小的副本数条件时,会自动离开安全模式。安全模式可以手动进入,但是这样的话也必须手动关闭安全模式。 +
-refreshNodes重新读取hosts和exclude文件,更新允许连到Namenode的或那些需要退出或入编的Datanode的集合。 +
-finalizeUpgrade终结HDFS的升级操作。Datanode删除前一个版本的工作目录,之后Namenode也这样做。这个操作完结整个升级过程。 +
-upgradeProgress status | details | force请求当前系统的升级状态,状态的细节,或者强制升级操作进行。 +
-metasave filename保存Namenode的主要数据结构到hadoop.log.dir属性指定的目录下的<filename>文件。对于下面的每一项,<filename>中都会一行内容与之对应
+ 1. Namenode收到的Datanode的心跳信号
+ 2. 等待被复制的块
+ 3. 正在被复制的块
+ 4. 等待被删除的块
-setQuota <quota> <dirname>...<dirname>为每个目录 <dirname>设定配额<quota>。目录配额是一个长整型整数,强制限定了目录树下的名字个数。
+ 命令会在这个目录上工作良好,以下情况会报错:
+ 1. N不是一个正整数,或者
+ 2. 用户不是管理员,或者
+ 3. 这个目录不存在或是文件,或者
+ 4. 目录会马上超出新设定的配额。
-clrQuota <dirname>...<dirname>为每一个目录<dirname>清除配额设定。
+ 命令会在这个目录上工作良好,以下情况会报错:
+ 1. 这个目录不存在或是文件,或者
+ 2. 用户不是管理员。
+ 如果目录原来没有配额不会报错。
-help [cmd]显示给定命令的帮助信息,如果没有给定命令,则显示所有命令的帮助信息。
+
+ +
+ jobtracker +

+ 运行MapReduce job Tracker节点。 +

+

+ 用法:hadoop jobtracker +

+
+ +
+ namenode +

+ 运行namenode。有关升级,回滚,升级终结的更多信息请参考升级和回滚。 +

+

+ 用法:hadoop namenode [-format] | [-upgrade] | [-rollback] | [-finalize] | [-importCheckpoint] +

+ + + + + + + + + + + + + + + + + + + + + + + +
命令选项 描述
-format格式化namenode。它启动namenode,格式化namenode,之后关闭namenode。
-upgrade分发新版本的hadoop后,namenode应以upgrade选项启动。
-rollback将namenode回滚到前一版本。这个选项要在停止集群,分发老的hadoop版本后使用。 +
-finalizefinalize会删除文件系统的前一状态。最近的升级会被持久化,rollback选项将再不可用,升级终结操作之后,它会停掉namenode。
-importCheckpoint从检查点目录装载镜像并保存到当前检查点目录,检查点目录由fs.checkpoint.dir指定。 +
+
+ +
+ secondarynamenode +

+ 运行HDFS的secondary namenode。参考Secondary Namenode了解更多。 +

+

+ 用法:hadoop secondarynamenode [-checkpoint [force]] | [-geteditsize] +

+ + + + + + + + + + + +
命令选项 描述
-checkpoint [force]如果EditLog的大小 >= fs.checkpoint.size,启动Secondary namenode的检查点过程。 + 如果使用了-force,将不考虑EditLog的大小。
-geteditsize打印EditLog大小。
+
+ +
+ tasktracker +

+ 运行MapReduce的task Tracker节点。 +

+

+ 用法:hadoop tasktracker +

+
+ +
+ + + + + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/distcp.xml b/src/docs/cn/src/documentation/content/xdocs/distcp.xml new file mode 100644 index 00000000000..3fda68dc46b --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/distcp.xml @@ -0,0 +1,294 @@ + + + + + + + +
+ DistCp +
+ + + +
+ 概述 + +

DistCp(分布式拷贝)是用于大规模集群内部和集群之间拷贝的工具。 + 它使用Map/Reduce实现文件分发,错误处理和恢复,以及报告生成。 + 它把文件和目录的列表作为map任务的输入,每个任务会完成源列表中部分文件的拷贝。 + 由于使用了Map/Reduce方法,这个工具在语义和执行上都会有特殊的地方。 + 这篇文档会为常用DistCp操作提供指南并阐述它的工作模型。 +

+ +
+ +
+ 使用方法 + +
+ 基本使用方法 +

DistCp最常用在集群之间的拷贝:

+

bash$ hadoop distcp hdfs://nn1:8020/foo/bar \
+           +           + hdfs://nn2:8020/bar/foo

+ +

这条命令会把nn1集群的/foo/bar目录下的所有文件或目录名展开并存储到一个临时文件中,这些文件内容的拷贝工作被分配给多个map任务, + 然后每个TaskTracker分别执行从nn1到nn2的拷贝操作。注意DistCp使用绝对路径进行操作。 +

+ +

命令行中可以指定多个源目录:

+

bash$ hadoop distcp hdfs://nn1:8020/foo/a \
+           +           + hdfs://nn1:8020/foo/b \
+           +           + hdfs://nn2:8020/bar/foo

+ +

或者使用-f选项,从文件里获得多个源:
+ bash$ hadoop distcp -f hdfs://nn1:8020/srclist \
+            +            +  hdfs://nn2:8020/bar/foo

+ +

其中srclist 的内容是
+     hdfs://nn1:8020/foo/a
+     hdfs://nn1:8020/foo/b

+ +

当从多个源拷贝时,如果两个源冲突,DistCp会停止拷贝并提示出错信息, + 如果在目的位置发生冲突,会根据选项设置解决。 + 默认情况会跳过已经存在的目标文件(比如不用源文件做替换操作)。每次操作结束时 + 都会报告跳过的文件数目,但是如果某些拷贝操作失败了,但在之后的尝试成功了, + 那么报告的信息可能不够精确(请参考附录)。 +

+ +

每个TaskTracker必须都能够与源端和目的端文件系统进行访问和交互。 + 对于HDFS来说,源和目的端要运行相同版本的协议或者使用向下兼容的协议。 + (请参考不同版本间的拷贝 )。 +

+ +

拷贝完成后,建议生成源端和目的端文件的列表,并交叉检查,来确认拷贝真正成功。 + 因为DistCp使用Map/Reduce和文件系统API进行操作,所以这三者或它们之间有任何问题 + 都会影响拷贝操作。一些Distcp命令的成功执行可以通过再次执行带-update参数的该命令来完成, + 但用户在如此操作之前应该对该命令的语法很熟悉。 +

+ +

值得注意的是,当另一个客户端同时在向源文件写入时,拷贝很有可能会失败。 + 尝试覆盖HDFS上正在被写入的文件的操作也会失败。 + 如果一个源文件在拷贝之前被移动或删除了,拷贝失败同时输出异常 + FileNotFoundException。

+ +
+ +
+ 选项 + +
+ 选项索引 + + + + + + + + + + + + + + + + + + + + + + + + + +
标识 描述 备注
-p[rbugp]Preserve
+   r: replication number
+   b: block size
+   u: user
+   g: group
+   p: permission
修改次数不会被保留。并且当指定 + -update 时,更新的状态会 + 被同步,除非文件大小不同(比如文件被重新创建)。 +
-i忽略失败就像在 附录中提到的,这个选项会比默认情况提供关于拷贝的更精确的统计, 同时它还将保留失败拷贝操作的日志,这些日志信息可以用于调试。最后,如果一个map失败了,但并没完成所有分块任务的尝试,这不会导致整个作业的失败。 +
-log <logdir>记录日志到 <logdir>DistCp为每个文件的每次尝试拷贝操作都记录日志,并把日志作为map的输出。 + 如果一个map失败了,当重新执行时这个日志不会被保留。 +
-m <num_maps>同时拷贝的最大数目指定了拷贝数据时map的数目。请注意并不是map数越多吞吐量越大。 +
-overwrite覆盖目标如果一个map失败并且没有使用-i选项,不仅仅那些拷贝失败的文件,这个分块任务中的所有文件都会被重新拷贝。 + 就像下面提到的,它会改变生成目标路径的语义,所以 + 用户要小心使用这个选项。 +
-update如果源和目标的大小不一样则进行覆盖像之前提到的,这不是"同步"操作。 + 执行覆盖的唯一标准是源文件和目标文件大小是否相同;如果不同,则源文件替换目标文件。 + 像 下面提到的,它也改变生成目标路径的语义, + 用户使用要小心。 +
-f <urilist_uri>使用<urilist_uri> 作为源文件列表这等价于把所有文件名列在命令行中。 + urilist_uri 列表应该是完整合法的URI。 +
+ +
+ +
+ 更新和覆盖 + +

这里给出一些 -update-overwrite的例子。 + 考虑一个从/foo/a 和 + /foo/b/bar/foo的拷贝,源路径包括: +

+ +

    hdfs://nn1:8020/foo/a
+     hdfs://nn1:8020/foo/a/aa
+     hdfs://nn1:8020/foo/a/ab
+     hdfs://nn1:8020/foo/b
+     hdfs://nn1:8020/foo/b/ba
+     hdfs://nn1:8020/foo/b/ab

+ +

如果没设置-update-overwrite选项, + 那么两个源都会映射到目标端的 + /bar/foo/ab。 + 如果设置了这两个选项,每个源目录的内容都会和目标目录的 + 内容 做比较。DistCp碰到这类冲突的情况会终止操作并退出。

+ +

默认情况下,/bar/foo/a 和 + /bar/foo/b 目录都会被创建,所以并不会有冲突。

+ +

现在考虑一个使用-update合法的操作:
+ distcp -update hdfs://nn1:8020/foo/a \
+         +        + hdfs://nn1:8020/foo/b \
+         +        + hdfs://nn2:8020/bar

+ +

其中源路径/大小:

+ +

    hdfs://nn1:8020/foo/a
+     hdfs://nn1:8020/foo/a/aa 32
+     hdfs://nn1:8020/foo/a/ab 32
+     hdfs://nn1:8020/foo/b
+     hdfs://nn1:8020/foo/b/ba 64
+     hdfs://nn1:8020/foo/b/bb 32

+ +

和目的路径/大小:

+ +

    hdfs://nn2:8020/bar
+     hdfs://nn2:8020/bar/aa 32
+     hdfs://nn2:8020/bar/ba 32
+     hdfs://nn2:8020/bar/bb 64

+ +

会产生:

+ +

    hdfs://nn2:8020/bar
+     hdfs://nn2:8020/bar/aa 32
+     hdfs://nn2:8020/bar/ab 32
+     hdfs://nn2:8020/bar/ba 64
+     hdfs://nn2:8020/bar/bb 32

+ +

只有nn2的aa文件没有被覆盖。如果指定了 + -overwrite选项,所有文件都会被覆盖。 +

+ +
+ +
+ +
+ +
+ 附录 + +
+ Map数目 + +

DistCp会尝试着均分需要拷贝的内容,这样每个map拷贝差不多相等大小的内容。 + 但因为文件是最小的拷贝粒度,所以配置增加同时拷贝(如map)的数目不一定会增加实际同时拷贝的数目以及总吞吐量。 +

+ +

如果没使用-m选项,DistCp会尝试在调度工作时指定map的数目 + 为 min (total_bytes / bytes.per.map, 20 * num_task_trackers), + 其中bytes.per.map默认是256MB。

+ +

建议对于长时间运行或定期运行的作业,根据源和目标集群大小、拷贝数量大小以及带宽调整map的数目。 +

+ +
+ +
+ 不同HDFS版本间的拷贝 + +

对于不同Hadoop版本间的拷贝,用户应该使用HftpFileSystem。 + 这是一个只读文件系统,所以DistCp必须运行在目标端集群上(更确切的说是在能够写入目标集群的TaskTracker上)。 + 源的格式是 + hftp://<dfs.http.address>/<path> + (默认情况dfs.http.address是 + <namenode>:50070)。

+ +
+ +
+ Map/Reduce和副效应 + +

像前面提到的,map拷贝输入文件失败时,会带来一些副效应。 +

+ +
    + +
  • 除非使用了-i,任务产生的日志会被新的尝试替换掉。 +
  • + +
  • 除非使用了-overwrite,文件被之前的map成功拷贝后当又一次执行拷贝时会被标记为 + "被忽略"。
  • + +
  • 如果map失败了mapred.map.max.attempts次,剩下的map任务会被终止(除非使用了-i)。 +
  • + +
  • 如果mapred.speculative.execution被设置为 + finaltrue,则拷贝的结果是未定义的。
  • + +
+ +
+ + + +
+ + + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hadoop_archives.xml b/src/docs/cn/src/documentation/content/xdocs/hadoop_archives.xml new file mode 100644 index 00000000000..8a610f65a7f --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hadoop_archives.xml @@ -0,0 +1,69 @@ + + + + +
+ Hadoop Archives +
+ +
+ 什么是Hadoop archives? +

+ Hadoop archives是特殊的档案格式。一个Hadoop archive对应一个文件系统目录。 + Hadoop archive的扩展名是*.har。Hadoop archive包含元数据(形式是_index和_masterindx)和数据(part-*)文件。_index文件包含了档案中的文件的文件名和位置信息。 +

+
+
+ 如何创建archive? +

+ 用法: hadoop archive -archiveName name <src>* <dest> +

+

+ 由-archiveName选项指定你要创建的archive的名字。比如foo.har。archive的名字的扩展名应该是*.har。输入是文件系统的路径名,路径名的格式和平时的表达方式一样。创建的archive会保存到目标目录下。注意创建archives是一个Map/Reduce job。你应该在map reduce集群上运行这个命令。下面是一个例子: +

+

+ hadoop archive -archiveName foo.har /user/hadoop/dir1 /user/hadoop/dir2 /user/zoo/ +

+ 在上面的例子中, + /user/hadoop/dir1 和 /user/hadoop/dir2 会被归档到这个文件系统目录下 + -- /user/zoo/foo.har。当创建archive时,源文件不会被更改或删除。 +

+
+
+ 如何查看archives中的文件? +

+ archive作为文件系统层暴露给外界。所以所有的fs shell命令都能在archive上运行,但是要使用不同的URI。 + 另外,archive是不可改变的。所以重命名,删除和创建都会返回错误。Hadoop Archives 的URI是 +

har://scheme-hostname:port/archivepath/fileinarchive

+ 如果没提供scheme-hostname,它会使用默认的文件系统。这种情况下URI是这种形式 +

+ har:///archivepath/fileinarchive

+

+ 这是一个archive的例子。archive的输入是/dir。这个dir目录包含文件filea,fileb。 + 把/dir归档到/user/hadoop/foo.bar的命令是 +

+

hadoop archive -archiveName foo.har /dir /user/hadoop +

+ 获得创建的archive中的文件列表,使用命令 +

+

hadoop dfs -lsr har:///user/hadoop/foo.har

+

查看archive中的filea文件的命令- +

hadoop dfs -cat har:///user/hadoop/foo.har/dir/filea

+
+ +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hdfs_design.xml b/src/docs/cn/src/documentation/content/xdocs/hdfs_design.xml new file mode 100644 index 00000000000..86f0697792c --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hdfs_design.xml @@ -0,0 +1,376 @@ + + + + + + + + +
+ + Hadoop分布式文件系统:架构和设计 + + + + +
+ + +
+ 引言 +

+ Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。这个项目的地址是http://hadoop.apache.org/core/。 +

+
+ +
+ 前提和设计目标 + +
+ 硬件错误 +

+ 硬件错误是常态而不是异常。HDFS可能由成百上千的服务器所构成,每个服务器上存储着文件系统的部分数据。我们面对的现实是构成系统的组件数目是巨大的,而且任一组件都有可能失效,这意味着总是有一部分HDFS的组件是不工作的。因此错误检测和快速、自动的恢复是HDFS最核心的架构目标。 +

+
+
+ 流式数据访问 +

+运行在HDFS上的应用和普通的应用不同,需要流式访问它们的数据集。HDFS的设计中更多的考虑到了数据批处理,而不是用户交互处理。比之数据访问的低延迟问题,更关键的在于数据访问的高吞吐量。POSIX标准设置的很多硬性约束对HDFS应用系统不是必需的。为了提高数据的吞吐量,在一些关键方面对POSIX的语义做了一些修改。 +

+
+ +
+ 大规模数据集 +

+ 运行在HDFS上的应用具有很大的数据集。HDFS上的一个典型文件大小一般都在G字节至T字节。因此,HDFS被调节以支持大文件存储。它应该能提供整体上高的数据传输带宽,能在一个集群里扩展到数百个节点。一个单一的HDFS实例应该能支撑数以千万计的文件。 +

+
+ + +
+ 简单的一致性模型 + +

+ HDFS应用需要一个“一次写入多次读取”的文件访问模型。一个文件经过创建、写入和关闭之后就不需要改变。这一假设简化了数据一致性问题,并且使高吞吐量的数据访问成为可能。Map/Reduce应用或者网络爬虫应用都非常适合这个模型。目前还有计划在将来扩充这个模型,使之支持文件的附加写操作。 +

+ +
+ + +
+ “移动计算比移动数据更划算” +

+ 一个应用请求的计算,离它操作的数据越近就越高效,在数据达到海量级别的时候更是如此。因为这样就能降低网络阻塞的影响,提高系统数据的吞吐量。将计算移动到数据附近,比之将数据移动到应用所在显然更好。HDFS为应用提供了将它们自己移动到数据附近的接口。 +

+
+ + +
+ 异构软硬件平台间的可移植性 +

+ HDFS在设计的时候就考虑到平台的可移植性。这种特性方便了HDFS作为大规模数据应用平台的推广。 +

+
+
+ + +
+ Namenode 和 Datanode + +

+ HDFS采用master/slave架构。一个HDFS集群是由一个Namenode和一定数目的Datanodes组成。Namenode是一个中心服务器,负责管理文件系统的名字空间(namespace)以及客户端对文件的访问。集群中的Datanode一般是一个节点一个,负责管理它所在节点上的存储。HDFS暴露了文件系统的名字空间,用户能够以文件的形式在上面存储数据。从内部看,一个文件其实被分成一个或多个数据块,这些块存储在一组Datanode上。Namenode执行文件系统的名字空间操作,比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体Datanode节点的映射。Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的创建、删除和复制。 +

+ +
+

+ Namenode和Datanode被设计成可以在普通的商用机器上运行。这些机器一般运行着GNU/Linux操作系统(OS)。HDFS采用Java语言开发,因此任何支持Java的机器都可以部署Namenode或Datanode。由于采用了可移植性极强的Java语言,使得HDFS可以部署到多种类型的机器上。一个典型的部署场景是一台机器上只运行一个Namenode实例,而集群中的其它机器分别运行一个Datanode实例。这种架构并不排斥在一台机器上运行多个Datanode,只不过这样的情况比较少见。 +

+

+ 集群中单一Namenode的结构大大简化了系统的架构。Namenode是所有HDFS元数据的仲裁者和管理者,这样,用户数据永远不会流过Namenode。 +

+
+ +
+ 文件系统的名字空间 (namespace) +

+ HDFS支持传统的层次型文件组织结构。用户或者应用程序可以创建目录,然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。当前,HDFS不支持用户磁盘配额和访问权限控制,也不支持硬链接和软链接。但是HDFS架构并不妨碍实现这些特性。 +

+

+ Namenode负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被Namenode记录下来。应用程序可以设置HDFS保存的文件的副本数目。文件副本的数目称为文件的副本系数,这个信息也是由Namenode保存的。 +

+
+ +
+ 数据复制 +

+ HDFS被设计成能够在一个大集群中跨机器可靠地存储超大文件。它将每个文件存储成一系列的数据块,除了最后一个,所有的数据块都是同样大小的。为了容错,文件的所有数据块都会有副本。每个文件的数据块大小和副本系数都是可配置的。应用程序可以指定某个文件的副本数目。副本系数可以在文件创建的时候指定,也可以在之后改变。HDFS中的文件都是一次性写入的,并且严格要求在任何时候只能有一个写入者。 +

+

+ Namenode全权管理数据块的复制,它周期性地从集群中的每个Datanode接收心跳信号和块状态报告(Blockreport)。接收到心跳信号意味着该Datanode节点工作正常。块状态报告包含了一个该Datanode上所有数据块的列表。 +

+
+ +
+ 副本存放: 最最开始的一步 +

+ 副本的存放是HDFS可靠性和性能的关键。优化的副本存放策略是HDFS区分于其他大部分分布式文件系统的重要特性。这种特性需要做大量的调优,并需要经验的积累。HDFS采用一种称为机架感知(rack-aware)的策略来改进数据的可靠性、可用性和网络带宽的利用率。目前实现的副本存放策略只是在这个方向上的第一步。实现这个策略的短期目标是验证它在生产环境下的有效性,观察它的行为,为实现更先进的策略打下测试和研究的基础。 +

+

+ 大型HDFS实例一般运行在跨越多个机架的计算机组成的集群上,不同机架上的两台机器之间的通讯需要经过交换机。在大多数情况下,同一个机架内的两台机器间的带宽会比不同机架的两台机器间的带宽大。 +

+

+ 通过一个机架感知的过程,Namenode可以确定每个Datanode所属的机架id。一个简单但没有优化的策略就是将副本存放在不同的机架上。这样可以有效防止当整个机架失效时数据的丢失,并且允许读数据的时候充分利用多个机架的带宽。这种策略设置可以将副本均匀分布在集群中,有利于当组件失效情况下的负载均衡。但是,因为这种策略的一个写操作需要传输数据块到多个机架,这增加了写的代价。 +

+

+ 在大多数情况下,副本系数是3,HDFS的存放策略是将一个副本存放在本地机架的节点上,一个副本放在同一机架的另一个节点上,最后一个副本放在不同机架的节点上。这种策略减少了机架间的数据传输,这就提高了写操作的效率。机架的错误远远比节点的错误少,所以这个策略不会影响到数据的可靠性和可用性。于此同时,因为数据块只放在两个(不是三个)不同的机架上,所以此策略减少了读取数据时需要的网络传输总带宽。在这种策略下,副本并不是均匀分布在不同的机架上。三分之一的副本在一个节点上,三分之二的副本在一个机架上,其他副本均匀分布在剩下的机架中,这一策略在不损害数据可靠性和读取性能的情况下改进了写的性能。 +

+

+ 当前,这里介绍的默认副本存放策略正在开发的过程中。 +

+
+ +
+ 副本选择 +

+ 为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。如果在读取程序的同一个机架上有一个副本,那么就读取该副本。如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。 +

+
+ +
+ 安全模式 +

+ Namenode启动后会进入一个称为安全模式的特殊状态。处于安全模式的Namenode是不会进行数据块的复制的。Namenode从所有的 Datanode接收心跳信号和块状态报告。块状态报告包括了某个Datanode所有的数据块列表。每个数据块都有一个指定的最小副本数。当Namenode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated)的;在一定百分比(这个参数可配置)的数据块被Namenode检测确认是安全之后(加上一个额外的30秒等待时间),Namenode将退出安全模式状态。接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他Datanode上。 +

+
+ +
+ +
+ 文件系统元数据的持久化 +

+ Namenode上保存着HDFS的名字空间。对于任何对文件系统元数据产生修改的操作,Namenode都会使用一种称为EditLog的事务日志记录下来。例如,在HDFS中创建一个文件,Namenode就会在Editlog中插入一条记录来表示;同样地,修改文件的副本系数也将往Editlog插入一条记录。Namenode在本地操作系统的文件系统中存储这个Editlog。整个文件系统的名字空间,包括数据块到文件的映射、文件的属性等,都存储在一个称为FsImage的文件中,这个文件也是放在Namenode所在的本地文件系统上。 +

+

+ Namenode在内存中保存着整个文件系统的名字空间和文件数据块映射(Blockmap)的映像。这个关键的元数据结构设计得很紧凑,因而一个有4G内存的Namenode足够支撑大量的文件和目录。当Namenode启动时,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用在内存中的FsImage上,并将这个新版本的FsImage从内存中保存到本地磁盘上,然后删除旧的Editlog,因为这个旧的Editlog的事务都已经作用在FsImage上了。这个过程称为一个检查点(checkpoint)。在当前实现中,检查点只发生在Namenode启动时,在不久的将来将实现支持周期性的检查点。 +

+

+ Datanode将HDFS数据以文件的形式存储在本地的文件系统中,它并不知道有关HDFS文件的信息。它把每个HDFS数据块存储在本地文件系统的一个单独的文件中。Datanode并不在同一个目录创建所有的文件,实际上,它用试探的方法来确定每个目录的最佳文件数目,并且在适当的时候创建子目录。在同一个目录中创建所有的本地文件并不是最优的选择,这是因为本地文件系统可能无法高效地在单个目录中支持大量的文件。当一个Datanode启动时,它会扫描本地文件系统,产生一个这些本地文件对应的所有HDFS数据块的列表,然后作为报告发送到Namenode,这个报告就是块状态报告。 +

+
+ +
+ 通讯协议 +

+ 所有的HDFS通讯协议都是建立在TCP/IP协议之上。客户端通过一个可配置的TCP端口连接到Namenode,通过ClientProtocol协议与Namenode交互。而Datanode使用DatanodeProtocol协议与Namenode交互。一个远程过程调用(RPC)模型被抽象出来封装ClientProtocol和Datanodeprotocol协议。在设计上,Namenode不会主动发起RPC,而是响应来自客户端或 Datanode 的RPC请求。 +

+
+ +
+ 健壮性 +

+ HDFS的主要目标就是即使在出错的情况下也要保证数据存储的可靠性。常见的三种出错情况是:Namenode出错, Datanode出错和网络割裂(network partitions)。 +

+ +
+ 磁盘数据错误,心跳检测和重新复制 +

+ 每个Datanode节点周期性地向Namenode发送心跳信号。网络割裂可能导致一部分Datanode跟Namenode失去联系。Namenode通过心跳信号的缺失来检测这一情况,并将这些近期不再发送心跳信号Datanode标记为宕机,不会再将新的IO请求发给它们。任何存储在宕机Datanode上的数据将不再有效。Datanode的宕机可能会引起一些数据块的副本系数低于指定值,Namenode不断地检测这些需要复制的数据块,一旦发现就启动复制操作。在下列情况下,可能需要重新复制:某个Datanode节点失效,某个副本遭到损坏,Datanode上的硬盘错误,或者文件的副本系数增大。 +

+
+ +
+ 集群均衡 +

+ HDFS的架构支持数据均衡策略。如果某个Datanode节点上的空闲空间低于特定的临界点,按照均衡策略系统就会自动地将数据从这个Datanode移动到其他空闲的Datanode。当对某个文件的请求突然增加,那么也可能启动一个计划创建该文件新的副本,并且同时重新平衡集群中的其他数据。这些均衡策略目前还没有实现。 +

+
+ +
+ 数据完整性 +

+ + 从某个Datanode获取的数据块有可能是损坏的,损坏可能是由Datanode的存储设备错误、网络错误或者软件bug造成的。HDFS客户端软件实现了对HDFS文件内容的校验和(checksum)检查。当客户端创建一个新的HDFS文件,会计算这个文件每个数据块的校验和,并将校验和作为一个单独的隐藏文件保存在同一个HDFS名字空间下。当客户端获取文件内容后,它会检验从Datanode获取的数据跟相应的校验和文件中的校验和是否匹配,如果不匹配,客户端可以选择从其他Datanode获取该数据块的副本。 +

+
+
+ 元数据磁盘错误 +

+ FsImage和Editlog是HDFS的核心数据结构。如果这些文件损坏了,整个HDFS实例都将失效。因而,Namenode可以配置成支持维护多个FsImage和Editlog的副本。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这种多副本的同步操作可能会降低Namenode每秒处理的名字空间事务数量。然而这个代价是可以接受的,因为即使HDFS的应用是数据密集的,它们也非元数据密集的。当Namenode重启的时候,它会选取最近的完整的FsImage和Editlog来使用。 +

+

+ Namenode是HDFS集群中的单点故障(single point of failure)所在。如果Namenode机器故障,是需要手工干预的。目前,自动重启或在另一台机器上做Namenode故障转移的功能还没实现。 +

+
+ +
+ 快照 +

+ 快照支持某一特定时刻的数据的复制备份。利用快照,可以让HDFS在数据损坏时恢复到过去一个已知正确的时间点。HDFS目前还不支持快照功能,但计划在将来的版本进行支持。 +

+
+
+
+ + 数据组织 + +
+ 数据块 +

+ HDFS被设计成支持大文件,适用HDFS的是那些需要处理大规模的数据集的应用。这些应用都是只写入数据一次,但却读取一次或多次,并且读取速度应能满足流式读取的需要。HDFS支持文件的“一次写入多次读取”语义。一个典型的数据块大小是64MB。因而,HDFS中的文件总是按照64M被切分成不同的块,每个块尽可能地存储于不同的Datanode中。 +

+
+ +
+ + Staging +

+ 客户端创建文件的请求其实并没有立即发送给Namenode,事实上,在刚开始阶段HDFS客户端会先将文件数据缓存到本地的一个临时文件。应用程序的写操作被透明地重定向到这个临时文件。当这个临时文件累积的数据量超过一个数据块的大小,客户端才会联系Namenode。Namenode将文件名插入文件系统的层次结构中,并且分配一个数据块给它。然后返回Datanode的标识符和目标数据块给客户端。接着客户端将这块数据从本地临时文件上传到指定的Datanode上。当文件关闭时,在临时文件中剩余的没有上传的数据也会传输到指定的Datanode上。然后客户端告诉Namenode文件已经关闭。此时Namenode才将文件创建操作提交到日志里进行存储。如果Namenode在文件关闭前宕机了,则该文件将丢失。 +

+

+ 上述方法是对在HDFS上运行的目标应用进行认真考虑后得到的结果。这些应用需要进行文件的流式写入。如果不采用客户端缓存,由于网络速度和网络堵塞会对吞估量造成比较大的影响。这种方法并不是没有先例的,早期的文件系统,比如AFS,就用客户端缓存来提高性能。为了达到更高的数据上传效率,已经放松了POSIX标准的要求。 +

+
+ +
+ 流水线复制 +

+ 当客户端向HDFS文件写入数据的时候,一开始是写到本地临时文件中。假设该文件的副本系数设置为3,当本地临时文件累积到一个数据块的大小时,客户端会从Namenode获取一个Datanode列表用于存放副本。然后客户端开始向第一个Datanode传输数据,第一个Datanode一小部分一小部分(4 KB)地接收数据,将每一部分写入本地仓库,并同时传输该部分到列表中第二个Datanode节点。第二个Datanode也是这样,一小部分一小部分地接收数据,写入本地仓库,并同时传给第三个Datanode。最后,第三个Datanode接收数据并存储在本地。因此,Datanode能流水线式地从前一个节点接收数据,并在同时转发给下一个节点,数据以流水线的方式从前一个Datanode复制到下一个。 +

+
+ +
+ +
+ + 可访问性 + +

+ HDFS给应用提供了多种访问方式。用户可以通过Java API接口访问,也可以通过C语言的封装API访问,还可以通过浏览器的方式访问HDFS中的文件。通过WebDAV协议访问的方式正在开发中。 +

+ +
+ DFSShell +

+ HDFS以文件和目录的形式组织用户数据。它提供了一个命令行的接口(DFSShell)让用户与HDFS中的数据进行交互。命令的语法和用户熟悉的其他shell(例如 bash, csh)工具类似。下面是一些动作/命令的示例: +

+ + + + + + + + + + + + + +
动作 命令
创建一个名为 /foodir 的目录 bin/hadoop dfs -mkdir /foodir
创建一个名为 /foodir 的目录 bin/hadoop dfs -mkdir /foodir
查看名为 /foodir/myfile.txt 的文件内容 bin/hadoop dfs -cat /foodir/myfile.txt
+

+ DFSShell 可以用在那些通过脚本语言和文件系统进行交互的应用程序上。 +

+
+ +
+ DFSAdmin +

+ DFSAdmin 命令用来管理HDFS集群。这些命令只有HDSF的管理员才能使用。下面是一些动作/命令的示例: +

+ + + + + + + + + + + + + +
动作 命令
将集群置于安全模式 bin/hadoop dfsadmin -safemode enter
显示Datanode列表 bin/hadoop dfsadmin -report
使Datanode节点 datanodename退役 bin/hadoop dfsadmin -decommission datanodename
+
+ +
+ 浏览器接口 +

+ 一个典型的HDFS安装会在一个可配置的TCP端口开启一个Web服务器用于暴露HDFS的名字空间。用户可以用浏览器来浏览HDFS的名字空间和查看文件的内容。 +

+
+ +
+ +
+ 存储空间回收 + +
+ 文件的删除和恢复 +

+ 当用户或应用程序删除某个文件时,这个文件并没有立刻从HDFS中删除。实际上,HDFS会将这个文件重命名转移到/trash目录。只要文件还在/trash目录中,该文件就可以被迅速地恢复。文件在/trash中保存的时间是可配置的,当超过这个时间时,Namenode就会将该文件从名字空间中删除。删除文件会使得该文件相关的数据块被释放。注意,从用户删除文件到HDFS空闲空间的增加之间会有一定时间的延迟。

+

+只要被删除的文件还在/trash目录中,用户就可以恢复这个文件。如果用户想恢复被删除的文件,他/她可以浏览/trash目录找回该文件。/trash目录仅仅保存被删除文件的最后副本。/trash目录与其他的目录没有什么区别,除了一点:在该目录上HDFS会应用一个特殊策略来自动删除文件。目前的默认策略是删除/trash中保留时间超过6小时的文件。将来,这个策略可以通过一个被良好定义的接口配置。 +

+
+ +
+ 减少副本系数 +

+ 当一个文件的副本系数被减小后,Namenode会选择过剩的副本删除。下次心跳检测时会将该信息传递给Datanode。Datanode遂即移除相应的数据块,集群中的空闲空间加大。同样,在调用setReplication API结束和集群中空闲空间增加间会有一定的延迟。

+
+
+ + +
+ 参考资料 +

+ HDFS Java API: + + http://hadoop.apache.org/core/docs/current/api/ + +

+

+ HDFS 源代码: + + http://hadoop.apache.org/core/version_control.html + +

+
+ + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hdfs_permissions_guide.xml b/src/docs/cn/src/documentation/content/xdocs/hdfs_permissions_guide.xml new file mode 100644 index 00000000000..54f0a1f7bfd --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hdfs_permissions_guide.xml @@ -0,0 +1,193 @@ + + + + + + + + +
+ + HDFS权限管理用户指南 + +
+ + +
概述 +

+ Hadoop分布式文件系统实现了一个和POSIX系统类似的文件和目录的权限模型。每个文件和目录有一个所有者(owner)和一个组(group)。文件或目录对其所有者、同组的其他用户以及所有其他用户分别有着不同的权限。对文件而言,当读取这个文件时需要有r权限,当写入或者追加到文件时需要有w权限。对目录而言,当列出目录内容时需要具有r权限,当新建或删除子文件或子目录时需要有w权限,当访问目录的子节点时需要有x权限。不同于POSIX模型,HDFS权限模型中的文件没有stickysetuidsetgid位,因为这里没有可执行文件的概念。为了简单起见,这里也没有目录的stickysetuidsetgid位。总的来说,文件或目录的权限就是它的模式(mode)。HDFS采用了Unix表示和显示模式的习惯,包括使用八进制数来表示权限。当新建一个文件或目录,它的所有者即客户进程的用户,它的所属组是父目录的组(BSD的规定)。 +

+

+ 每个访问HDFS的用户进程的标识分为两个部分,分别是用户名组名列表。每次用户进程访问一个文件或目录foo,HDFS都要对其进行权限检查, +

+ + +

+ 如果权限检查失败,则客户的操作会失败。 +

+
+ +
用户身份 +

+在这个版本的Hadoop中,客户端用户身份是通过宿主操作系统给出。对类Unix系统来说, +

+ + +

+将来会增加其他的方式来确定用户身份(比如Kerberos、LDAP等)。期待用上文中提到的第一种方式来防止一个用户假冒另一个用户是不现实的。这种用户身份识别机制结合权限模型允许一个协作团体以一种有组织的形式共享文件系统中的资源。 +

+

+不管怎样,用户身份机制对HDFS本身来说只是外部特性。HDFS并不提供创建用户身份、创建组或处理用户凭证等功能。 +

+
+ +
理解系统的实现 +

+ 每次文件或目录操作都传递完整的路径名给name node,每一个操作都会对此路径做权限检查。客户框架会隐式地将用户身份和与name node的连接关联起来,从而减少改变现有客户端API的需求。经常会有这种情况,当对一个文件的某一操作成功后,之后同样的操作却会失败,这是因为文件或路径上的某些目录已经不复存在了。比如,客户端首先开始读一个文件,它向name node发出一个请求以获取文件第一个数据块的位置。但接下去的获取其他数据块的第二个请求可能会失败。另一方面,删除一个文件并不会撤销客户端已经获得的对文件数据块的访问权限。而权限管理能使得客户端对一个文件的访问许可在两次请求之间被收回。重复一下,权限的改变并不会撤销当前客户端对文件数据块的访问许可。 +

+

+map-reduce框架通过传递字符串来指派用户身份,没有做其他特别的安全方面的考虑。文件或目录的所有者和组属性是以字符串的形式保存,而不是像传统的Unix方式转换为用户和组的数字ID。 +

+

+这个发行版本的权限管理特性并不需要改变data node的任何行为。Data node上的数据块上并没有任何Hadoop所有者或权限等关联属性。 +

+
+ +
文件系统API变更 +

+ 如果权限检查失败,所有使用一个路径参数的方法都可能抛出AccessControlException异常。 +

+

新增方法:

+ +

+新建文件或目录的模式受配置参数umask的约束。当使用之前的 create(path, …) 方法(没有指定权限参数)时,新文件的模式是666 & ^umask。当使用新的 create(path, permission, …) 方法(指定了权限参数P)时,新文件的模式是P & ^umask & 666。当使用先前的 mkdirs(path) 方法(没有指定 权限参数)新建一个目录时,新目录的模式是777 & ^umask。当使用新的 mkdirs(path, permission ) 方法(指定了权限参数P)新建一个目录时,新目录的模式是P & ^umask & 777。 +

+
+ + +
Shell命令变更 +

新增操作:

+
+
chmod [-R] mode file …
+
+ 只有文件的所有者或者超级用户才有权限改变文件模式。 +
+
chgrp [-R] group file …
+
+ 使用chgrp命令的用户必须属于特定的组且是文件的所有者,或者用户是超级用户。 +
+
chown [-R] [owner][:[group]] file …
+
+ 文件的所有者的只能被超级用户更改。 +
+
ls file …
+
lsr file …
+
+ 输出格式做了调整以显示所有者、组和模式。 +
+
+ + +
超级用户 +

+超级用户即运行name node进程的用户。宽泛的讲,如果你启动了name node,你就是超级用户。超级用户干任何事情,因为超级用户能够通过所有的权限检查。没有永久记号保留谁过去是超级用户;当name node开始运行时,进程自动判断谁现在是超级用户。HDFS的超级用户不一定非得是name node主机上的超级用户,也不需要所有的集群的超级用户都是一个。同样的,在个人工作站上运行HDFS的实验者,不需任何配置就已方便的成为了他的部署实例的超级用户。 +

+

+ 另外,管理员可以用配置参数指定一组特定的用户,如果做了设定,这个组的成员也会是超级用户。 +

+
+ +
Web服务器 +

+Web服务器的身份是一个可配置参数。Name node并没有真实用户的概念,但是Web服务器表现地就像它具有管理员选定的用户的身份(用户名和组)一样。除非这个选定的身份是超级用户,否则会有名字空间中的一部分对Web服务器来说不可见。 +

+
+ +
在线升级 +

+如果集群在0.15版本的数据集(fsimage)上启动,所有的文件和目录都有所有者O,组G,和模式M,这里 OG 分别是超级用户的用户标识和组名,M是一个配置参数。

+
+ +
配置参数 +
+
dfs.permissions = true
+
+ 如果是 true,则打开前文所述的权限系统。如果是 false,权限检查 就是关闭的,但是其他的行为没有改变。这个配置参数的改变并不改变文件或目录的模式、所有者和组等信息。 +

+

+ 不管权限模式是开还是关,chmodchgrpchown 总是 会检查权限。这些命令只有在权限检查背景下才有用,所以不会有兼容性问题。这样,这就能让管理员在打开常规的权限检查之前可以可靠地设置文件的所有者和权限。 +
+
dfs.web.ugi = webuser,webgroup
+
+ Web服务器使用的用户名。如果将这个参数设置为超级用户的名称,则所有Web客户就可以看到所有的信息。如果将这个参数设置为一个不使用的用户,则Web客户就只能访问到“other”权限可访问的资源了。额外的组可以加在后面,形成一个用逗号分隔的列表。 +
+
dfs.permissions.supergroup = supergroup
+
+ 超级用户的组名。 +
+
dfs.upgrade.permission = 777
+
+ 升级时的初始模式。文件永不会被设置x权限。在配置文件中,可以使用十进制数51110。 +
+
dfs.umask = 022
+
+ umask参数在创建文件和目录时使用。在配置文件中,可以使用十进制数1810。 +
+
+
+ + + +
+ + diff --git a/src/docs/cn/src/documentation/content/xdocs/hdfs_quota_admin_guide.xml b/src/docs/cn/src/documentation/content/xdocs/hdfs_quota_admin_guide.xml new file mode 100644 index 00000000000..014f513d310 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hdfs_quota_admin_guide.xml @@ -0,0 +1,72 @@ + + + + + + + + +
+ + 名字空间配额管理指南 + +
+ + +

+ Hadoop分布式文件系统(HDFS)允许管理员为每个目录设置配额。 + 新建立的目录没有配额。 + 最大的配额是Long.Max_Value。配额为1可以强制目录保持为空。 +

+ +

+ 目录配额是对目录树上该目录下的名字数量做硬性限制。如果创建文件或目录时超过了配额,该操作会失败。重命名不会改变该目录的配额;如果重命名操作会导致违反配额限制,该操作将会失败。如果尝试设置一个配额而现有文件数量已经超出了这个新配额,则设置失败。 +

+ +

+ 配额和fsimage保持一致。当启动时,如果fsimage违反了某个配额限制(也许fsimage被偷偷改变了),则启动失败并生成错误报告。设置或删除一个配额会创建相应的日志记录。 +

+ +

+ 下面的新命令或新选项是用于支持配额的。 + 前两个是管理员命令。 +

+ + + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hdfs_shell.xml b/src/docs/cn/src/documentation/content/xdocs/hdfs_shell.xml new file mode 100644 index 00000000000..029936ea721 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hdfs_shell.xml @@ -0,0 +1,477 @@ + + + + +
+ Hadoop Shell命令 +
+ + +
+ FS Shell +

+ 调用文件系统(FS)Shell命令应使用 + bin/hadoop fs <args>的形式。 + 所有的的FS shell命令使用URI路径作为参数。URI格式是scheme://authority/path。对HDFS文件系统,scheme是hdfs,对本地文件系统,scheme是file。其中scheme和authority参数都是可选的,如果未加指定,就会使用配置中指定的默认scheme。一个HDFS文件或目录比如/parent/child可以表示成hdfs://namenode:namenodeport/parent/child,或者更简单的/parent/child(假设你配置文件中的默认值是namenode:namenodeport)。大多数FS Shell命令的行为和对应的Unix Shell命令类似,不同之处会在下面介绍各命令使用详情时指出。出错信息会输出到stderr,其他信息输出到stdout。 +

+
+ cat +

+ 使用方法:hadoop fs -cat URI [URI …] +

+ +

+ 将路径指定文件的内容输出到stdout。 +

+ +

示例:

+
    +
  • + hadoop fs -cat hdfs://host1:port1/file1 hdfs://host2:port2/file2 + +
  • +
  • + hadoop fs -cat file:///file3 /user/hadoop/file4 +
  • +
+

返回值:
+ + 成功返回0,失败返回-1。

+
+
+ chgrp +

+ 使用方法:hadoop fs -chgrp [-R] GROUP URI [URI …] + Change group association of files. With -R, make the change recursively through the directory structure. The user must be the owner of files, or else a super-user. Additional information is in the Permissions User Guide. +--> +

+

+ 改变文件所属的组。使用-R将使改变在目录结构下递归进行。命令的使用者必须是文件的所有者或者超级用户。更多的信息请参见HDFS权限用户指南。 +

+
+
+ chmod +

+ 使用方法:hadoop fs -chmod [-R] <MODE[,MODE]... | OCTALMODE> URI [URI …] +

+

+ 改变文件的权限。使用-R将使改变在目录结构下递归进行。命令的使用者必须是文件的所有者或者超级用户。更多的信息请参见HDFS权限用户指南。 +

+
+
+ chown +

+ 使用方法:hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ] +

+

+ 改变文件的拥有者。使用-R将使改变在目录结构下递归进行。命令的使用者必须是超级用户。更多的信息请参见HDFS权限用户指南。 +

+
+
+ copyFromLocal +

+ 使用方法:hadoop fs -copyFromLocal <localsrc> URI +

+

除了限定源路径是一个本地文件外,和put命令相似。

+
+
+ copyToLocal +

+ 使用方法:hadoop fs -copyToLocal [-ignorecrc] [-crc] URI <localdst> +

+

除了限定目标路径是一个本地文件外,和get命令类似。

+
+
+ cp +

+ 使用方法:hadoop fs -cp URI [URI …] <dest> +

+

+ 将文件从源路径复制到目标路径。这个命令允许有多个源路径,此时目标路径必须是一个目录。 +
+ 示例:

+
    +
  • + hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 +
  • +
  • + hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 /user/hadoop/dir +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ du +

+ 使用方法:hadoop fs -du URI [URI …] +

+

+ 显示目录中所有文件的大小,或者当只指定一个文件时,显示此文件的大小。
+ 示例:
hadoop fs -du /user/hadoop/dir1 /user/hadoop/file1 hdfs://host:port/user/hadoop/dir1
+ 返回值:
成功返回0,失败返回-1。

+
+
+ dus +

+ 使用方法:hadoop fs -dus <args> +

+

+ 显示文件的大小。 +

+
+
+ expunge +

+ 使用方法:hadoop fs -expunge +

+

清空回收站。请参考HDFS设计文档以获取更多关于回收站特性的信息。 +

+
+
+ get +

+ 使用方法:hadoop fs -get [-ignorecrc] [-crc] <src> <localdst> +
+

+

+ 复制文件到本地文件系统。可用-ignorecrc选项复制CRC校验失败的文件。使用-crc选项复制文件以及CRC信息。 +

+

示例:

+
    +
  • + hadoop fs -get /user/hadoop/file localfile +
  • +
  • + hadoop fs -get hdfs://host:port/user/hadoop/file localfile +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ getmerge +

+ 使用方法:hadoop fs -getmerge <src> <localdst> [addnl] +

+

+ 接受一个源目录和一个目标文件作为输入,并且将源目录中所有的文件连接成本地目标文件。addnl是可选的,用于指定在每个文件结尾添加一个换行符。 +

+
+
+ ls +

+ 使用方法:hadoop fs -ls <args> +

+

如果是文件,则按照如下格式返回文件信息:
文件名 <副本数> 文件大小 修改日期 修改时间 权限 用户ID 组ID
+ 如果是目录,则返回它直接子文件的一个列表,就像在Unix中一样。目录返回列表的信息如下:
目录名 <dir> 修改日期 修改时间 权限 用户ID 组ID
+ 示例:
hadoop fs -ls /user/hadoop/file1 /user/hadoop/file2 hdfs://host:port/user/hadoop/dir1 /nonexistentfile
+ 返回值:
成功返回0,失败返回-1。

+
+
+ lsr +

使用方法:hadoop fs -lsr <args>
+ ls命令的递归版本。类似于Unix中的ls -R。 +

+
+
+ mkdir +

+ 使用方法:hadoop fs -mkdir <paths> +
+

+

接受路径指定的uri作为参数,创建这些目录。其行为类似于Unix的mkdir -p,它会创建路径中的各级父目录。

+

示例:

+
    +
  • + hadoop fs -mkdir /user/hadoop/dir1 /user/hadoop/dir2 +
  • +
  • + hadoop fs -mkdir hdfs://host1:port1/user/hadoop/dir hdfs://host2:port2/user/hadoop/dir + +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ movefromLocal +

+ 使用方法:dfs -moveFromLocal <src> <dst> +

+

输出一个”not implemented“信息。 +

+
+
+ mv +

+ 使用方法:hadoop fs -mv URI [URI …] <dest> +

+

+ 将文件从源路径移动到目标路径。这个命令允许有多个源路径,此时目标路径必须是一个目录。不允许在不同的文件系统间移动文件。 +
+ 示例: +

+
    +
  • + hadoop fs -mv /user/hadoop/file1 /user/hadoop/file2 +
  • +
  • + hadoop fs -mv hdfs://host:port/file1 hdfs://host:port/file2 hdfs://host:port/file3 hdfs://host:port/dir1 +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ put +

+ 使用方法:hadoop fs -put <localsrc> ... <dst> +

+

从本地文件系统中复制单个或多个源路径到目标文件系统。也支持从标准输入中读取输入写入目标文件系统。
+

+
    +
  • + hadoop fs -put localfile /user/hadoop/hadoopfile +
  • +
  • + hadoop fs -put localfile1 localfile2 /user/hadoop/hadoopdir +
  • +
  • + hadoop fs -put localfile hdfs://host:port/hadoop/hadoopfile +
  • +
  • hadoop fs -put - hdfs://host:port/hadoop/hadoopfile
    从标准输入中读取输入。
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ rm +

+ 使用方法:hadoop fs -rm URI [URI …] +

+

+ 删除指定的文件。只删除非空目录和文件。请参考rmr命令了解递归删除。
+ 示例: +

+
    +
  • + hadoop fs -rm hdfs://host:port/file /user/hadoop/emptydir +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ rmr +

+ 使用方法:hadoop fs -rmr URI [URI …] +

+

delete的递归版本。
+ 示例: +

+
    +
  • + hadoop fs -rmr /user/hadoop/dir +
  • +
  • + hadoop fs -rmr hdfs://host:port/user/hadoop/dir +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ setrep +

+ 使用方法:hadoop fs -setrep [-R] <path> +

+

+ 改变一个文件的副本系数。-R选项用于递归改变目录下所有文件的副本系数。 +

+

示例:

+
    +
  • + hadoop fs -setrep -w 3 -R /user/hadoop/dir1 +
  • +
+

返回值:

+

+ 成功返回0,失败返回-1。 +

+
+
+ stat +

+ 使用方法:hadoop fs -stat URI [URI …] +

+

+ 返回指定路径的统计信息。 +

+

示例:

+
    +
  • + hadoop fs -stat path +
  • +
+

返回值:
+ 成功返回0,失败返回-1。

+
+
+ tail +

+ 使用方法:hadoop fs -tail [-f] URI +

+

+ 将文件尾部1K字节的内容输出到stdout。支持-f选项,行为和Unix中一致。 +

+

示例:

+
    +
  • + hadoop fs -tail pathname +
  • +
+

返回值:
+ 成功返回0,失败返回-1。

+
+
+ test +

+ 使用方法:hadoop fs -test -[ezd] URI +

+

+ 选项:
+ -e 检查文件是否存在。如果存在则返回0。
+ -z 检查文件是否是0字节。如果是则返回0。
+ -d 如果路径是个目录,则返回1,否则返回0。

+

示例:

+
    +
  • + hadoop fs -test -e filename +
  • +
+
+
+ text +

+ 使用方法:hadoop fs -text <src> +
+

+

+ 将源文件输出为文本格式。允许的格式是zip和TextRecordInputStream。 +

+
+
+ touchz +

+ 使用方法:hadoop fs -touchz URI [URI …] +
+

+

+ 创建一个0字节的空文件。 +

+

示例:

+
    +
  • + hadoop -touchz pathname +
  • +
+

返回值:
+ 成功返回0,失败返回-1。

+ +
+
+ +
+ + diff --git a/src/docs/cn/src/documentation/content/xdocs/hdfs_user_guide.xml b/src/docs/cn/src/documentation/content/xdocs/hdfs_user_guide.xml new file mode 100644 index 00000000000..40a43c997ac --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hdfs_user_guide.xml @@ -0,0 +1,513 @@ + + + + + + + + + +
+ + Hadoop分布式文件系统使用指南 + +
+ + +
目的 +

+ 本文档的目标是为Hadoop分布式文件系统(HDFS)的用户提供一个学习的起点,这里的HDFS既可以作为Hadoop集群的一部分,也可以作为一个独立的分布式文件系统。虽然HDFS在很多环境下被设计成是可正确工作的,但是了解HDFS的工作原理对在特定集群上改进HDFS的运行性能和错误诊断都有极大的帮助。 +

+
+ + + +
概述 +

+HDFS是Hadoop应用用到的一个最主要的分布式存储系统。一个HDFS集群主要由一个NameNode和很多个Datanode组成:Namenode管理文件系统的元数据,而Datanode存储了实际的数据。HDFS的体系结构在这里有详细的描述。本文档主要关注用户以及管理员怎样和HDFS进行交互。HDFS架构设计中的图解描述了Namenode、Datanode和客户端之间的基本的交互操作。基本上,客户端联系Namenode以获取文件的元数据或修饰属性,而真正的文件I/O操作是直接和Datanode进行交互的。 +

+

+ 下面列出了一些多数用户都比较感兴趣的重要特性。 +

+ + +
先决条件 +

+ 下面的文档描述了如何安装和搭建Hadoop集群: +

+ +

+ 文档余下部分假设用户已经安装并运行了至少包含一个Datanode节点的HDFS。就本文目的来说,Namenode和Datanode可以运行在同一个物理主机上。 +

+ +
Web接口 + +

+ NameNode和DataNode各自启动了一个内置的Web服务器,显示了集群当前的基本状态和信息。在默认配置下NameNode的首页地址是http://namenode-name:50070/。这个页面列出了集群里的所有DataNode和集群的基本状态。这个Web接口也可以用来浏览整个文件系统(使用NameNode首页上的"Browse the file system"链接)。 +

+ + +
Shell命令 +

Hadoop包括一系列的类shell的命令,可直接和HDFS以及其他Hadoop支持的文件系统进行交互。bin/hadoop fs -help 命令列出所有Hadoop Shell支持的命令。而 bin/hadoop fs -help command-name 命令能显示关于某个命令的详细信息。这些命令支持大多数普通文件系统的操作,比如复制文件、改变文件权限等。它还支持一些HDFS特有的操作,比如改变文件副本数目。 +

+ +
DFSAdmin命令 +

+ 'bin/hadoop dfsadmin' 命令支持一些和HDFS管理相关的操作。bin/hadoop dfsadmin -help 命令能列出所有当前支持的命令。比如: +

+
    +
  • + + -report:报告HDFS的基本统计信息。有些信息也可以在NameNode Web服务首页看到。 + +
  • +
  • + -safemode:虽然通常并不需要,但是管理员的确可以手动让NameNode进入或离开安全模式。 +
  • +
  • + -finalizeUpgrade:删除上一次升级时制作的集群备份。 +
  • +
+
+ +
Secondary NameNode +

NameNode将对文件系统的改动追加保存到本地文件系统上的一个日志文件(edits)。当一个NameNode启动时,它首先从一个映像文件(fsimage)中读取HDFS的状态,接着应用日志文件中的edits操作。然后它将新的HDFS状态写入(fsimage)中,并使用一个空的edits文件开始正常操作。因为NameNode只有在启动阶段才合并fsimageedits,所以久而久之日志文件可能会变得非常庞大,特别是对大型的集群。日志文件太大的另一个副作用是下一次NameNode启动会花很长时间。 +

+

+ Secondary NameNode定期合并fsimage和edits日志,将edits日志文件大小控制在一个限度下。因为内存需求和NameNode在一个数量级上,所以通常secondary NameNode和NameNode运行在不同的机器上。Secondary NameNode通过bin/start-dfs.shconf/masters中指定的节点上启动。 +

+ + +

+Secondary NameNode的检查点进程启动,是由两个配置参数控制的: +

+ +

+ Secondary NameNode保存最新检查点的目录与NameNode的目录结构相同。 + 所以NameNode可以在需要的时候读取Secondary NameNode上的检查点镜像。 +

+

+ 如果NameNode上除了最新的检查点以外,所有的其他的历史镜像和edits文件都丢失了, + NameNode可以引入这个最新的检查点。以下操作可以实现这个功能: +

+ +

+ NameNode会从fs.checkpoint.dir目录读取检查点, + 并把它保存在dfs.name.dir目录下。 + 如果dfs.name.dir目录下有合法的镜像文件,NameNode会启动失败。 + NameNode会检查fs.checkpoint.dir目录下镜像文件的一致性,但是不会去改动它。 +

+

+ 命令的使用方法请参考secondarynamenode 命令. +

+ + + + +
Rebalancer + +

+ HDFS的数据也许并不是非常均匀的分布在各个DataNode中。一个常见的原因是在现有的集群上经常会增添新的DataNode节点。当新增一个数据块(一个文件的数据被保存在一系列的块中)时,NameNode在选择DataNode接收这个数据块之前,会考虑到很多因素。其中的一些考虑的是: +

+ + +

+由于上述多种考虑需要取舍,数据可能并不会均匀分布在DataNode中。HDFS为管理员提供了一个工具,用于分析数据块分布和重新平衡DataNode上的数据分布。HADOOP-1652的附件中的一个PDF是一个简要的rebalancer管理员指南。 +

+ +

+ 使用方法请参考balancer 命令. +

+ +
机架感知(Rack awareness) +

+ 通常,大型Hadoop集群是以机架的形式来组织的,同一个机架上不同节点间的网络状况比不同机架之间的更为理想。另外,NameNode设法将数据块副本保存在不同的机架上以提高容错性。Hadoop允许集群的管理员通过配置dfs.network.script参数来确定节点所处的机架。当这个脚本配置完毕,每个节点都会运行这个脚本来获取它的机架ID。默认的安装假定所有的节点属于同一个机架。这个特性及其配置参数在HADOOP-692所附的PDF上有更详细的描述。 +

+ +
安全模式 +

+ NameNode启动时会从fsimage和edits日志文件中装载文件系统的状态信息,接着它等待各个DataNode向它报告它们各自的数据块状态,这样,NameNode就不会过早地开始复制数据块,即使在副本充足的情况下。这个阶段,NameNode处于安全模式下。NameNode的安全模式本质上是HDFS集群的一种只读模式,此时集群不允许任何对文件系统或者数据块修改的操作。通常NameNode会在开始阶段自动地退出安全模式。如果需要,你也可以通过'bin/hadoop dfsadmin -safemode'命令显式地将HDFS置于安全模式。NameNode首页会显示当前是否处于安全模式。关于安全模式的更多介绍和配置信息请参考JavaDoc:setSafeMode()。 +

+ +
fsck +

+ HDFS支持fsck命令来检查系统中的各种不一致状况。这个命令被设计来报告各种文件存在的问题,比如文件缺少数据块或者副本数目不够。不同于在本地文件系统上传统的fsck工具,这个命令并不会修正它检测到的错误。一般来说,NameNode会自动修正大多数可恢复的错误。HDFS的fsck不是一个Hadoop shell命令。它通过'bin/hadoop fsck'执行。 + +命令的使用方法请参考fsck命令 +fsck可用来检查整个文件系统,也可以只检查部分文件。 + +

+ +
升级和回滚 +

当在一个已有集群上升级Hadoop时,像其他的软件升级一样,可能会有新的bug或一些会影响到现有应用的非兼容性变更出现。在任何有实际意义的HDSF系统上,丢失数据是不被允许的,更不用说重新搭建启动HDFS了。HDFS允许管理员退回到之前的Hadoop版本,并将集群的状态回滚到升级之前。更多关于HDFS升级的细节在升级wiki上可以找到。HDFS在一个时间可以有一个这样的备份。在升级之前,管理员需要用bin/hadoop dfsadmin -finalizeUpgrade(升级终结操作)命令删除存在的备份文件。下面简单介绍一下一般的升级过程: +

+ + +
文件权限和安全性 +

+ 这里的文件权限和其他常见平台如Linux的文件权限类似。目前,安全性仅限于简单的文件权限。启动NameNode的用户被视为HDFS的超级用户。HDFS以后的版本将会支持网络验证协议(比如Kerberos)来对用户身份进行验证和对数据进行加密传输。具体的细节请参考权限使用管理指南。 +

+ +
可扩展性 +

+ 现在,Hadoop已经运行在上千个节点的集群上。Powered By Hadoop页面列出了一些已将Hadoop部署在他们的大型集群上的组织。HDFS集群只有一个NameNode节点。目前,NameNode上可用内存大小是一个主要的扩展限制。在超大型的集群中,增大HDFS存储文件的平均大小能够增大集群的规模,而不需要增加NameNode的内存。默认配置也许并不适合超大规模的集群。Hadoop FAQ页面列举了针对大型Hadoop集群的配置改进。

+ +
相关文档 +

+ 这个用户手册给用户提供了一个学习和使用HDSF文件系统的起点。本文档会不断地进行改进,同时,用户也可以参考更多的Hadoop和HDFS文档。下面的列表是用户继续学习的起点: +

+ +
+ + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hod.xml b/src/docs/cn/src/documentation/content/xdocs/hod.xml new file mode 100644 index 00000000000..dcdd92315da --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hod.xml @@ -0,0 +1,46 @@ + + + + + + + + +
+ + Hadoop On Demand + +
+ + +
+ 简介 +

Hadoop On Demand(HOD)是一个能在大型物理集群上供应虚拟hadoop集群的系统。它使用Torque资源管理器分配节点。它可以在分配的节点上启动Hadoop Map/Reduce和HDFS的守护进程。它会自动为Hadoop守护进程和客户端生成合适的配置文件(hadoop-site.xml)。HOD还可以将Hadoop分发到它分配出来的虚拟Hadoop集群的节点上。简而言之,HOD使管理员和用户轻松地快速搭建和使用hadoop。它也是Hadoop开发人员和测试人员非常有用的一个工具,他们可以使用HOD共享一个物理集群来测试各自的Hadoop版本。

+
+
+ 文档 +

读一遍下面的文档,你会在使用HOD方面了解更多

+ +
+ +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hod_admin_guide.xml b/src/docs/cn/src/documentation/content/xdocs/hod_admin_guide.xml new file mode 100644 index 00000000000..6d1a0d3d8da --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hod_admin_guide.xml @@ -0,0 +1,237 @@ + + + + + + + + +
+ + Hadoop On Demand + +
+ + +
+概述 +

Hadoop On Demand (HOD)是一个能在一个共享集群上供应和管理相互独立的Hadoop Map/Reduce和Hadoop分布式文件系统(HDFS)实例的系统。它能让管理员和用户轻松地快速搭建和使用hadoop。HOD对Hadoop的开发人员和测试人员也非常有用,他们可以通过HOD共享一个物理集群来测试各自不同的Hadoop版本。

+

HOD依赖资源管理器(RM)来分配节点,这些节点被用来在之上运行hadoop实例。目前,HOD采用的是Torque资源管理器。 +

+

+基本的HOD系统架构包含的下列组件:

+ + +

+通过与以上组件交互,HOD在给定的集群上供应和维护Hadoop Map/Reduce实例,或者HDFS实例。集群中的节点可看作由两组节点构成:

+ + +

+下面是对申请集群及在之上运行作业所需操作步骤的简要描述。 +

+ +

之后的文档会讲述如何在一个物理集群的节点上安装HOD。

+
+ +
+先决条件 +

要使用HOD,你的系统应包含下列的硬件和软件

+

操作系统: HOD目前在RHEL4上测试通过。
+节点:HOD至少需要3个由资源管理器配置的节点。

+ +

软件

+

在使用HOD之前,以下组件必须被安装到所有节点上:

+ + +

下列组件是可选的,你可以安装以获取HOD更好的功能:

+ + +

注释: HOD的配置需要以上这些组件的安装位置在集群所有节点上保持一致。如果在提交节点上的安装位置也相同,配置起来会更简单。

+
+ +
+资源管理器 +

目前,HOD使用Torque资源管理器来分配节点和提交作业。Torque是一个开源的资源管理器,来自于Cluster Resources,是一个社区基于PBS项目努力的结晶。它提供对批处理作业和分散的计算节点(Compute nodes)的控制。你可以自由地从此处下载Torque。

+

所有torque相关的文档可以在这儿的TORQUE Resource Manager一节找到。在这里可以看到wiki文档。如果想订阅TORQUE的邮件列表或查看问题存档,访问这里

+ +

使用带Torque的HOD:

+ +
+ +
+安装HOD + +

现在资源管理器已经安装好了,我们接着下载并安装HOD。

+ +
+ +
+配置HOD + +

安装HOD后你就可以配置它。为了运行HOD需要做的最小配置会在下面讲述,更多高级的配置会在HOD配置指南里面讲解。

+
+ 最小配置 +

为运行HOD,以下的最小配置是必须要做的:

+
    +
  • 在你想要运行hod的节点上,编辑<install dir>/conf目录下的hodrc文件。这个文件包含了运行hod所必需的最少量的设置。
  • +
  • +

    为这个配置文件中的定义的变量指定适合你环境的值。注意,有些变量在文件中出现了不止一次。

    + +
      +
    • ${JAVA_HOME}:Hadoop的Java的安装位置。Hadoop支持Sun JDK 1.5.x及以上版本。
    • +
    • ${CLUSTER_NAME}:集群名称,由'node property'指定,在资源管理器配置中曾提到过。
    • +
    • ${HADOOP_HOME}:Hadoop在计算节点和提交节点上的安装位置。
    • +
    • ${RM_QUEUE}:在资源管理器配置中设置的作业提交队列。
    • +
    • ${RM_HOME}:资源管理器在计算节点和提交节点的安装位置。
    • +
    +
  • + +
  • +

    以下环境变量可能需要设置,取决于你的系统环境。在你运行HOD客户端的地方这些变量必须被定义,也必须在HOD配置文件中通过设定resource_manager.env-vars的值指定。多个变量可指定为用逗号分隔的key=value对组成的列表。

    +
      +
    • HOD_PYTHON_HOME:如果python安装在计算节点或提交节点的非默认位置,那么这个值必须设定为python的可执行文件的实际位置。
    • +
    +
  • +
+
+ +
+ 高级配置 +

你可以检查和修改其它配置选项来满足你的特定需要。关于HOD配置的更多信息,请参考配置指南

+
+
+
+ 运行HOD +

当HOD配置好后,你就可以运行它了。更多信息请参考HOD用户指南

+
+ +
+ 支持工具和实用程序 +

此节描述一些可用于管理HOD部署的支持工具和应用程序。

+
+ logcondense.py - 管理日志文件 +

HOD用户指南有提到,HOD可配置成将Hadoop日志上传到一个配置好的静态HDFS上。随着时间增加,日志数量会不断增长。logcondense.py可以帮助管理员清理上传到HDFS的日志文件。

+
+ 运行logcondense.py +

logcondense.py在hod_install_location/support文件夹下。你可以使用python去运行它,比如python logcondense.py,或者授以执行权限,直接运行logcondense.py。如果启用了权限,logcondense.py需要被有足够权限,能删除HDFS上上传目录下日志文件的用户运行。比如,在配置指南中提及过,用户可以配置将日志放在HDFS上的其主目录下。在这种情况下,你需要具有超级用户权限,才能运行logcondense.py删除所有用户主目录下的日志文件。

+
+
+ logcondense.py的命令行选项 +

logcondense.py支持以下命令行选项

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
短选项长选项含义例子
-p--packagehadoop脚本的全路径。Hadoop的版本必须和运行HDFS的版本一致。/usr/bin/hadoop
-d--days删除超过指定天数的日志文件7
-c--configHadoop配置目录的路径,hadoop-site.xml存在于此目录中。hadoop-site.xml中须指明待删除日志存放的HDFS的NameNode。/home/foo/hadoop/conf
-l--logs一个HDFS路径,须和log-destination-uri指定的是同一个HDFS路径,不带hdfs:// URI串,这点在配置指南中提到过。/user
-n--dynamicdfs如果为true,logcondense.py除要删除Map/Reduce日志之外还需删除HDFS日志。否则,它只删除Map/Reduce日志,这也是不指定这个选项时的默认行为。这个选项对下面的情况非常有用:一个动态的HDFS由HOD供应,一个静态的HDFS用来收集日志文件 - 也许这是测试集群中一个非常普遍的使用场景。false
+

比如,假如要删除所有7天之前的日志文件,hadoop-site.xml存放在~/hadoop-conf下,hadoop安装于~/hadoop-0.17.0,你可以这样:

+

python logcondense.py -p ~/hadoop-0.17.0/bin/hadoop -d 7 -c ~/hadoop-conf -l /user

+
+
+
+ checklimits.sh - 监视资源限制 +

checklimits.sh是一个针对Torque/Maui环境的HOD工具(Maui集群调度器 是一个用于集群和超级计算机的开源作业调度器,来自clusterresourcces)。当新提交的作业违反或超过用户在Maui调度器里设置的限制时,checklimits.sh脚本更新torque的comment字段。它使用qstat在torque的job-list中做一次遍历确定作业是在队列中还是已完成,运行Maui工具checkjob检查每一个作业是否违反用户限制设定,之后运行torque的qalter工具更新作业的'comment'的属性。当前,它把那些违反限制的作业的comment的值更新为User-limits exceeded. Requested:([0-9]*) Used:([0-9]*) MaxLimit:([0-9]*)。之后,HOD根据这个注释内容做出相应处理。 +

+ +
+ 运行checklimits.sh +

checklimits.sh可以在hod_install_location/support目录下下找到。在具有得执行权限后,这个shell脚本可以直接通过sh checklimits.sh 或者./checklimits.sh运行。这个工具运行的机器上应有Torque和Maui的二进制运行文件,并且这些文件要在这个shell脚本进程的路径中。为了更新不同用户作业的comment值,这个工具必须以torque的管理员权限运行。这个工具必须按照一定时间间隔重复运行,来保证更新job的约束条件,比如可以通过cron。请注意,这个脚本中用到的资源管理器和调度器命令运行代价可能会比价大,所以最好不要在没有sleeping的紧凑循环中运行。 +

+
+
+ +
+ verify-account - 用于核实用户提交作业所使用的帐号的脚本 +

生产系统一般使用帐号系统来对使用共享资源的用户收费。HOD支持一个叫resource_manager.pbs-account的参数,用户可以通过这个参数来指定提交作业时使用的帐号。核实这个帐户在帐号管理系统中的有效性是有必要的。脚本hod-install-dir/bin/verify-account提供了一种机制让用户插入自定义脚本来实现这个核实过程。

+
+ 在HOD中集成verify-account +

在分配集群之前,HOD运行verify-account脚本,将resource_manager.pbs-account的值作为参数传递给用户自定义脚本来完成用户的确认。系统还可以通过这种方式来取代它本身的帐号系统。若该用户脚本中的返回值非0,就会导致HOD分配集群失败。并且在发生错误时,HOD还会将脚本中产生的错误信息打印出来。通过这种方式,任何描述性的错误信息都可以从用户脚本中返回给用户。 +

+

在HOD中自带的默认脚本是不做任何的用户核实,并返回0。

+

如果HOD没有找到上面提到的verify-account脚本,HOD就会认为该用户核实的功能被关闭,然后继续自己以后的分配工作。

+
+
+ +
+ +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hod_config_guide.xml b/src/docs/cn/src/documentation/content/xdocs/hod_config_guide.xml new file mode 100644 index 00000000000..31ddc84e076 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hod_config_guide.xml @@ -0,0 +1,158 @@ + + + + + + + +
+ + Hadoop On Demand:配置指南 + +
+ + +
+ 1. 简介 +

+ 这个文档讲述了一些最重要和常用的Hadoop On Demand(HOD)的配置项。 + 这些配置项可通过两种方式指定:INI风格的配置文件,通过--section.option[=value]格式指定的HOD shell的命令行选项。如果两个地方都指定了同一个选项,命令行中的值覆盖配置文件中的值。 +

+

+ 你可以通过以下命令获得所有配置项的简要描述: +

+
$ hod --verbose-help
+ +
+
+ 2. 段 +

HOD配置文件分成以下几个配置段:

+ +
+
+ 3. HOD配置项 +

+ 接下来的一节会先描述大部分HOD配置段中通用的一些配置项,再描述各配置段特有的配置项。 +

+ +
+ 3.1 一般的配置项 + +

某些配置项会在HOD配置中的多个段定义。在一个段中定义的配置项,会被该段所适用的所有进程使用。这些配置项意义相同,但在不同的段中可以有不同的取值。

+ +
    +
  • temp-dir: HOD进程使用的临时目录。请确保运行hod的用户有权限在这个指定的目录下创建子目录。如果想在每次分配的时候都使用不同的临时目录,可以使用环境变量,资源管理器会让这些环境变量对HOD进程可用。例如,在Torque设置的时候,使--ringmaster.temp-dir=/tmp/hod- temp-dir.$PBS_JOBID会让ringmaster在每一次申请时使用不同的临时目录;Troque会在ringmaster启动前展开这个环境变量。 +
  • +
  • debug:数值类型,取值范围是1-4。4会产生最多的log信息。
  • +
  • log-dir:日志文件的存放目录。缺省值是<install-location>/logs/。temp-dir变量的限制和注意事项在这里同样使用。 +
  • +
  • xrs-port-range:端口范围,会在这之中挑选一个可用端口用于运行XML-RPC服务。
  • +
  • http-port-range:端口范围,会在这之中挑选一个可用端口用于运行HTTP服务。
  • +
  • java-home:给Hadoop使用的Java的位置。
  • +
  • syslog-address:syslog守护进程要绑定的地址。格式为host:port。如果配置了这个选项,HOD日志信息会被记录到这个位置的syslog。
  • +
+
+ +
+ 3.2 hod的配置项 +
    +
  • cluster:集群的描述性名称。对于Torque,这个值被指定为集群中所有节点的'Node property'。HOD使用这个值计算可用节点的个数。
  • +
  • client-params:逗号分割的hadoop配置参数列表,其中的每一项都是一个key-value对。在提交节点上会据此产生一个hadoop-site.xml,用于运行Map/Reduce作业。
  • + +
  • job-feasibility-attr: 正则表达式,用于指定是否和如何检查作业的可行性 - 资源管理器限制或调度限制。目前是通过torque作业的'comment'属性实现的,缺省情况下没有开启这个功能。设置了这个配置项后,HOD会使用它来确定哪些种类的限制是启用的,以及请求超出限制或者累积超出限制时是回收机群还是留在排队状态。torque comment属性可以被某个外部机制周期性地更新。比如,comment属性被hod/support目录下的checklimits.sh更新,这样设置job-feasibility-attr的值等于TORQUE_USER_LIMITS_COMMENT_FIELD, "User-limits exceeded. Requested:([0-9]*) Used:([0-9]*) MaxLimit:([0-9]*)"会使HOD产生相应的行为。
  • +
+
+ +
+ 3.3 resouce_manager的配置项 + +
    +
  • queue:资源管理器中配置的队列名,作业会被提交到这里。
  • + +
  • batch-home:个安装目录,其下的'bin'中有资源管理器的可执行文件。
  • +
  • env-vars:逗号分隔的key-value对的列表,形式是key=value,它会被传递给运行在计算节点的作业。例如,如果ptyhon没有安装在常规位置,用户可以通过设置环境变量'HOD_PYTHON_HOME'指定python可执行文件的路径。之后,在计算节点运行的HOD的进程就可以使用这个变量了。
  • +
+
+ +
+ 3.4 ringmaster的配置项 +
    +
  • work-dirs:这是一个由逗号分隔的路径列表,这些路径将作为HOD产生和传递给Hadoop,用于存放DFS和Map/Reduce数据的目录的根目录。例如,这是DFS数据块存放的路径。一般情况下,有多少块磁盘就指定多少路径,以确保所有的磁盘都被利用到。temp-dir变量的限制和注意事项在这儿同样适用。
  • +
  • max-master-failures:hadoop主守护进启动前可以失败的次数,超出这个次数后,HOD会让这次集群分配失败。在HOD集群中,有时候由于某些问题,比如机器没安装java,没有安装Hadoop,或者Hadoop版本错误等,会存在一个或几个“坏”节点。当这个配置项被设为正整数时,只有当hadoop matser(JobTracker或者NameNode)在上述的坏节点上,由于上面提到的种种原因启动失败的次数超过设定的值时,RingMaster才会把错误返回给客户端。如果尝试启动的次数没有超过设定值,当下一个HodRing请求运行一个命令时,同一个hadoop master会指定给这个HodRing。这样,即使集群中存在一些坏的节点,HOD也会尽全力使这次分配成功。 +
  • + +
+
+
+ 3.5 gridservice-hdfs的配置项 +
    +
  • external:如果被置为false,HOD必须在通过allocate命令分配的节点上自己创建HDFS集群。注意,在这种情况下,如果集群被回收,HDFS集群会停止,所有数据会丢失。如果被置为true,它会尝试链接外部的已配置的HDFS系统。通常,因为在作业运行之前作业的输入需要被放置在HDFS上,并且作业的输出需要持久保留,在生产环境中一个内部的HDFS集群意义不大。
  • + +
  • host:外部配置好的NameNode的主机名。
  • + +
  • fs_port:NameNode RPC服务绑定的端口。
  • + +
  • info_port:NameNode web UI服务绑定的端口。
  • + +
  • pkgs:安装目录,其下有bin/hadoop可执行文件。可用来使用集群上预先安装的Hadoop版本。
  • + +
  • server-params:一个逗号分割的hadoop配置参数列表,每一项为key-value对形式。这些将用于产生被NameNode和DataNode使用到的hadoop-site.xml文件。
  • + +
  • final-server-params:除会被标记为final外和上面相同。
  • +
+
+ +
+ 3.6 gridservice-mapred的配置项 + +
    +
  • external:如果被置为false,HOD必须在通过allocate命令分配的节点上自己创建Map/Reduce集群。如果被置为true,它会尝试链接外部的已配置的Map/Reduce系统。
  • +
  • host:外部配置好的JobTracker的主机名。
  • + +
  • tracker_port:JobTracker RPC服务绑定的端口。
  • + +
  • info_port:JobTracker web UI服务绑定的端口。
  • + +
  • pkgs:安装目录,其下有bin/hadoop可执行文件。
  • + +
  • server-params:一个逗号分割的hadoop配置参数列表,每一项为key-value对形式。这些将用于产生被JobTracker和TaskTracker使用到的hadoop-site.xml文件。
  • +
  • final-server-params:除会被标记为final外和上面相同。
  • +
+
+ +
+ 3.7 hodring的配置项 + +
    +
  • mapred-system-dir-root:DFS上的目录,HOD会在这个目录下创建子目录并把全路径作为参数'mapred.system.dir'的值传递给Hadoop守护进程。全路径的格式为value-of-this-option/userid/mapredsystem/cluster-id。注意,如果HDFS启用了权限,这里指定的路径下应允许所有用户创建子目录。设置此配置项的值为/user会使HOD使用用户的home目录来产生mapred.system.dir的值。
  • +
  • log-destination-uri:一个URL,能反映一个外部的静态的DFS或者集群节点上的本地文件系统上的路径。当集群被回收时,HOD会把Hadoop日志上传到这个路径。要指定DFS路径,使用'hdfs://path'格式。要指定一个集群节点上的本地文件系统路径,使用'file://path'格式。当HOD回收集群时,作为HOD的清除过程的一部分,hadoop日志会被删除。要做到持久储这些日志,你可以使用这个配置项。路径的格式会是values-of-this-option/userid/hod-logs/cluster-id。注意,应该保证所有的用户能在这里指定的目录下创建子目录。把这个值设为hdfs://user会使这些日志被转移到用户在DFS上的home目录下。
  • +
  • pkgs:安装目录,其下有bin/hadoop可执行文件。如果给log-destination-uri指定了一个HDFS URL,HOD上传日志时会用到这个配置项。注意,当用户使用了和外部静态HDFS不同版本的tarball时,这个配置项会派上用场。
  • + +
+
+
+ +
diff --git a/src/docs/cn/src/documentation/content/xdocs/hod_user_guide.xml b/src/docs/cn/src/documentation/content/xdocs/hod_user_guide.xml new file mode 100644 index 00000000000..0137536c1a0 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/hod_user_guide.xml @@ -0,0 +1,559 @@ + + + + + +
+ + Hadoop On Demand用户指南 + +
+ + +
+ 简介 +

Hadoop On Demand (HOD)是一个能在大规模物理集群上供应虚拟Hadoop集群的系统。它使用Torque资源管理器进行节点分配。在所分配的节点上,它能启动Hadoop Map/Reduce以及HDFS守护进程。它能自动为Hadoop守护进程及客户端生成合适的配置文件(Hadoop-site.xml)。HOD还能够将Hadoop分发到它分配的虚拟集群节点上。总之,HOD方便管理者和用户快速安装与使用Hadoop。它也是需要在同一物理集群上测试各自版本的Hadoop开发者和测试者的实用工具。

+

HOD支持Hadoop 0.15及其后续版本。

+

后面的文档包括一个快速入门指南能让你快速上手HOD,一个所有HOD特性的详细手册,命令行选项,一些已知问题和故障排除的信息。

+
+
+ HOD使用入门 + +

在这部分,我们将会逐步骤地介绍使用HOD涉及到的最基本的操作。在开始遵循这些步骤之前,我们假定HOD及其依赖的软硬件均已被正确安装和配置。这步通常由集群的系统管理员负责。

+

HOD的用户界面是一个命令行工具,叫做hod。它被一个通常由系统管理员为用户设置好的配置文件所驱动。用户在使用hod的时候可以覆盖这个配置,文档的后面会由介绍。使用hod时有如下两种方式可以指定配置文件:

+ +

下面的例子中,我们将不会明确指出这个配置选项,假定其已正确指定。

+
一个典型HOD会话 +

一个典型HOD会话至少包括三个步骤:分配,执行Hadoop作业,回收。为此,执行如下步骤。

+

创建一个集群目录

+

集群目录是本地文件系统上的一个目录,hod会为它分配的集群产生对应的Hadoop配置hadoop-site.xml,放在这个目录下。这个目录可以按下文所述方式传递给hod操作。如果这个目录不存在,HOD会自动创建这个目录。一但分配好了集群,用户可通过Hadoop --config选项指定集群目录,在之上运行Hadoop作业。

+ +

allocate操作

+

allocate操作用来分配一组节点并在之上安装和提供Hadoop。它的语法如下。注意它要求指定参数集群目录(-d, --hod.clusterdir)和节点个数(-n, --hod.nodecount):

+ + + + + + +
$ hod allocate -d cluster_dir -n number_of_nodes [OPTIONS]
+

如果命令成功执行,cluster_dir/hadoop-site.xml会被生成,文件中包含了分配出的集群的信息。它也会打印出关于Hadoop的web UI的信息。

+

试运行这个命令会产生如下输出。注意在这个例子中集群目录是~/hod-clusters/test,我们要分配5个节点:

+ + + + +
$ hod allocate -d ~/hod-clusters/test -n 5
+ INFO - HDFS UI on http://foo1.bar.com:53422
+ INFO - Mapred UI on http://foo2.bar.com:55380
+ +

在分配的集群上执行Hadoop作业

+

现在,可以用一般的方式在分配的集群上执行Hadoop作业了。这是假定像JAVA_HOME,指向Hadoop安装的路径已被正确地设置了:

+ + + + + + +
$ hadoop --config cluster_dir hadoop_command hadoop_command_args
+

或者

+ + + + + + +
$ export HADOOP_CONF_DIR=cluster_dir
+ $ hadoop hadoop_command hadoop_command_args
+

继续我们的例子,下面的命令会在分配的集群上运行wordcount的例子:

+
$ hadoop --config ~/hod-clusters/test jar /path/to/hadoop/hadoop-examples.jar wordcount /path/to/input /path/to/output
+

或者

+ + + +
$ export HADOOP_CONF_DIR=~/hod-clusters/test
+ $ hadoop jar /path/to/hadoop/hadoop-examples.jar wordcount /path/to/input /path/to/output
+

deallocate操作

+

deallocate操作用来回收分配到的集群。当完成集群使用之后,必须执行回收操作使这些节点可以为其他用户所用。deallocate操作的语法如下。注意它需要集群目录(-d, --hod.clusterdir)作为参数:

+ + + + + + +
$ hod deallocate -d cluster_dir
+

继续我们的例子,如下命令会回收集群:

+
$ hod deallocate -d ~/hod-clusters/test
+

如你所见,HOD允许用户分配一个集群,随意的使用它来运行Hadoop作业。例如,通过从多个shell中启动使用同一个配置的hadoop,用户可以做到在同一个集群上并发运行多个作业。

+
+
使用HOD运行Hadoop脚本 +

HOD的script操作能将集群的分配,使用和回收组织在一起。这对那些想运行Hadoop作业脚本,期望HOD能在脚本结束后自动完成清理操作的用户特别管用。用hod执行Hadoop脚本,需要这么做:

+

创建脚本文件

+

这是一个普通的shell脚本,通常里面会包含hadoop命令,如:

+ +
$ hadoop jar jar_file options
+

当然,用户可以向脚本中添加任何有效的命令。HOD会在执行这个脚本时自动地设置HADOOP_CONF_DIR指向分配的集群。用户不必对此担心。不过,像分配操作时一样,用户需要指定一个集群目录。

+

运行脚本

+

脚本操作的语法如下。注意它需要集群目录(-d, --hod.clusterdir),节点个数(-n, --hod.nodecount)以及脚本文件(-s, --hod.script)作为参数:

+ + + + + + +
$ hod script -d cluster_directory -n number_of_nodes -s script_file
+

注意一但脚本执行完毕,HOD就会回收集群,这意味着脚本必须要做到等hadoop作业完成后脚本才结束。用户写脚本时必须注意这点。

+
+
+
+ HOD的功能 +
供应与管理Hadoop集群 +

HOD主要功能是供应Hadoop的Map/Reduce和HDFS集群。这些在见入门一节已经做过描述。 此外,要是还有节点可用,并且组织上也批准,一个用户可以在同一时间内使用HOD分配多个Map/Reduce集群。对于分配到的不同集群,用户需要为上面提到的cluster_dir参数指定不同的路径。HOD提供listinfo操作可以管理多个集群。

+

list操作

+

list操作能列举到目前为止用户所创建的所有集群。存放hadoop-site.xml的集群目录,与JobTracker和/或HDFS的连接及状态也会被显示出来。list操作的使用语法如下:

+ + + + + + +
$ hod list
+

info操作

+

info操作会显示指定集群相关的信息。这些信息包括Torque作业id,HOD Ringmaster进程,Hadoop的JobTracker和NameNode守护进程等重要守护进程的位置。info操作的语法如下。注意它需要集群目录(-d, --hod.clusterdir)作为参数:

+ + + + + + +
$ hod info -d cluster_dir
+

cluster_dir应为前面allocate操作中指定的有效集群目录。

+
+
使用tarball分发Hadoop +

供应Hadoop时,HOD可以使用集群节点上已经安装好的Hadoop,也可以将hadoop的tarball作为供应操作的一部分在节点上进行分发和安装。如果使用tarball选项,就不必非得使用预装的Hadoop了,也不要求集群节点上必须有一个预装的版本。这对开发/QE环境下在一个共享集群上测试不同版本hadoop的开发者尤其有用。

+

要使用预装的Hadoop,你必须在hodrc中的gridservice-hdfs部分和gridservice-mapred部分指定pkgs选项。它必须指向集群中所有节点上Hadoop的安装路径。

+

指定Tarball的语法如下:

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes -t hadoop_tarball_location
+

例如,下面的命令根据tarball~/share/hadoop.tar.gz分配Hadoop:

+
$ hod allocate -d ~/hadoop-cluster -n 10 -t ~/share/hadoop.tar.gz
+

类似地,使用hod脚本的语法如下:

+ + + + +
$ hod script -d cluster_directory -s scritp_file -n number_of_nodes -t hadoop_tarball_location
+

上面语法中指定的hadoop_tarball_location应指向从所有计算节点都可以访问的共享文件系统的路径。当前,HOD只支持挂载的NFS。

+

注意:

+
    +
  • 为了获得更好分发性能,建议Hadoop tarball只包含库与二进制文件,不包含源代码或文档。
  • +
  • 当你希望在用tarball方式分配的集群上执行作业,你必须使用兼容的Hadoop版本提交你的作业。最好的方式是解压,使用Tarball中的版本。
  • +
  • 你需要确保在tar分发包的conf目录下没有Hadoop配置文件hadoop-env.sh和hadoop-site.xml。如果这些文件存在并包含错误的值,集群分配可能会失败。 +
  • +
+
+
使用外部HDFS +

在典型的由HOD提供的Hadoop集群中,HDFS已经被静态地(未使用HOD)设置好。这能使数据在HOD提供的集群被回收后还可以持久保存在HDFS中。为使用静态配置的HDFS,你的hodrc必须指向一个外部HDFS。具体就是,在hodrc的gridservice-hdfs部分将下面选项设置为正确的值:

+
external = true
host = HDFS NameNode主机名
fs_port = HDFS NameNode端口
info_port = HDFS NameNode web UI的端口
+

注意:你也可以从命令行开启这个选项。即,你这样去使用一个静态HDFS:
+

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes --gridservice-hdfs.external
+

如果需要,HOD即可以供应HDFS集群也可以供应Map/Reduce的集群HOD。这需要设置hodrc中的gridservice-hdfs部分的下列选项:

+
external = false
+
+
配置Hadoop的选项 +

HOD提供一个非常方便的机制能配置它提供的Hadoop守护进程和它在客户端生成的hadoop-site.xml。通过在HOD配置文件中指定配置参数,或在分配集群时在命令行指定都可做到这点。

+

配置Hadoop守护进程

+

要配置Hadoop守护进程,你可以这么做:

+

对于Map/Reduce,指定gridservice-mapred部分的server-params项的指为一个以逗号分割的key-value对列表。同配置动态HDFS集群一样,设置gridservice-hdfs部分的server-params项。如果这些参数应被标记成final,将这些参数包含到相应部分的final-server-params项中。

+

例如:

+ +
server-params = mapred.reduce.parallel.copies=20,io.sort.factor=100,io.sort.mb=128,io.file.buffer.size=131072
final-server-params = mapred.child.java.opts=-Xmx512m,dfs.block.size=134217728,fs.inmemory.size.mb=128
+

要从命令行指定选项,你可以用如下语法:

+

配置Map/Reduce守护进程:

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes -Mmapred.reduce.parallel.copies=20 -Mio.sort.factor=100
+

在上述例子中,mapred.reduce.parallel.copies参数和io.sort.factor参数将会被添加到server-params中,如果已经在server-params中存在,则它们会被覆盖。要将这些参数指定成final类型,你可以:

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes -Fmapred.reduce.parallel.copies=20 -Fio.sort.factor=100
+

不过,应注意final参数无法被命令行改写的,只有在未指定的情形才能追加。

+

配置动态供应的HDFS守护进程的选项与此相似。用-H替换-M以,用-S替换-F即可。

+

配置Hadoop的作业提交(客户端)程序

+

如上所述,当allocate操作成功后,cluster_dir/hadoop-site.xml将会生成,其中会包含分配的集群的JobTracker和NameNode的信息。这个配置用于向集群提交作业。HOD提供选项可将其它的hadoop配置参数添加到该文件,其语法如下:

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes -Cmapred.userlog.limit.kb=200 -Cmapred.child.java.opts=-Xmx512m
+

上例中,mapred.userlog.limit.kbmapred.child.java.opts会被添加到hod产生的hadoop-site.xml中。

+
+
查看Hadoop的Web-UI +

HOD的allocate操作会打印出JobTracker和NameNode的Web UI的URL。例如:

+
$ hod allocate -d ~/hadoop-cluster -n 10 -c ~/hod-conf-dir/hodrc
+ INFO - HDFS UI on http://host242.foo.com:55391
+ INFO - Mapred UI on http://host521.foo.com:54874 +
+

上面提到的info操作可以给你同样的信息。

+
+
收集和查看Hadoop日志 +

要获取在某些分配节点上运行的守护进程的Hadoop日志:

+
    +
  • 登录感兴趣的节点。如果你想查看JobTracker或者NameNode的日志,listinfo操作能告诉你这些进程在那些节点上运行。
  • +
  • 获取感兴趣的守护进程的进程信息(例如,ps ux | grep TaskTracker
  • +
  • 在这些进程信息中,查找变量-Dhadoop.log.dir的值。通常是hod配置文件里hodring.temp-dir目录的一个子目录 。
  • +
  • 切换到hadoop.log.dir目录以查看守护进程日志和用户日志。
  • +
+

HOD也提供了一个机制,能让你在集群回收后将日志收集存放到文件系统,或者一个在外部配置的HDFS中。这样的话,在作业完成,节点回收后你还可以看这些日志。要做到这点,像下面一样为log-destination-uri指定一个URI:

+ + +
log-destination-uri= hdfs://host123:45678/user/hod/logs或者
log-destination-uri= file://path/to/store/log/files
+

在上面指定的的根目录中,HOD会创建路径user_name/torque_jobid,把作业涉及到的每个节点上的日志文件gzip压缩,存放在里面。

+

注意要在HDFS上存储这些文件,你得将hodring.pkgs项配置为和刚才提到的HDFS兼容的版本。否则,HOD会尝试使用它供应Hadoop集群时用到的Hadoop版本。

+
+
闲置集群的自动回收 +

HOD会自动回收在一段时间内没有运行Hadoop作业的集群。每次的HOD分配会带有一个监控设施不停地检查Hadoop作业的执行。如果侦测到在一定时间内没Hadoop作业在执行,它就回收这个集群,释放那些未被有效利用的节点。

+

注意:当集群被回收时,集群目录没有被自动清空。用户须通过一个正式的deallcocate操作清理它。

+
+
指定额外的作业属性 +

HOD允许用户为一个Torque作业指定一个时钟时间和一个名称(或者标题)。

+

时钟时间是对Torque作业有效时间的一个估计。这个时间过期后,Torque将自动删除这个作业,释放其节点。指定这个时钟时间还能帮助作业调度程序更好的安排作业,提高对集群资源的使用率。

+

指定时钟时间的语法如下:

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes -l time_in_seconds
+

Torque作业的名称或标题能给用户以友好的作业标识。每次展示Torque作业的属性的时候,这个字符串就会出现,包括qstat命令。

+

指定名称或标题的语法如下:

+ + + + +
$ hod allocate -d cluster_dir -n number_of_nodes -N name_of_job
+

注意:由于底层Torque资源管理器的限制,不以字母开头或者包含空格的名字将导致作业失败。失败信息会表明问题存在于指定的作业名称中。

+
+
捕获HOD在Torque中的退出码 +

HOD退出码出现在Torque的exit_status字段中。这有助于使用者和系统管理员区分成功的HOD执行和失败的HOD执行。如果分配成功且所有Hadoop作业在所分配的集群上正确的执行,退出码为0。如果分配失败或者部分hadoop作业在分配集群上运行失败,退出码非0。下表列出了可能出现的退出码。注意:只有所使用的Hadoop版本是0.16或以上时,Hadoop作业状态才可以被捕获。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
退出码含义
6 Ringmaster故障
7 DFS故障
8 Job tracker故障
10 集群死亡
12 集群已分配
13 HDFS死亡
14 Mapred死亡
16 集群中所有的Map/Reduce作业失败。查看hadoop日志了解更多细节。
17 集群中部分的Map/Reduce作业失败。查看hadoop日志了解更多细节。
+
+
+ 命令行 +

HOD命令行的通用的语法如下:
+ hod <operation> [ARGS] [OPTIONS]
+ 允许的操作有‘allocate’,‘deallocate’,‘info’,‘list’,‘script’以及‘help’。要获取某特定操作的帮助你可以执行:hod help <operation>。要查看可能的操作你可以执行hod help options

+

allocate
+ 用法:hod allocate -d cluster_dir -n number_of_nodes [OPTIONS]
+ 分配一个指定节点数目的集群,把分配信息存放在cluster_dir方便后续hadoop命令使用。注意cluster_dir必须在运行该命令前已经存在。

+

list
+ 用法:hod list [OPTIONS]
+ 列举出用户分配的所有集群。提供的信息包括集群对应的的Torque作业标识,存储分配信息的集群目录,Map/Reduce守护进程是否存活。

+

info
+ 用法:hod info -d cluster_dir [OPTIONS]
+ 列举集群分配信息存放于某指定集群目录的集群信息。

+

deallocate
+ 用法:hod deallocate -d cluster_dir [OPTIONS]
+ 回收集群分配信息存放于某指定集群目录的集群。

+

script
+ 用法:hod script -s script_file -d cluster_directory -n number_of_node [OPTIONS]
+ 用HODscript操作执行一个hadoop脚本。在给定数目的节点上提供Hadoop,在提交的节点执行这个脚本,并在脚本执行结束后回收集群。

+

help
+ 用法:hod help [operation | 'options']
+ 未指定参数时,hod help给出用法以及基本选项,等同于hod --help (见下文)。当指定参数‘options’时,显示hod的基本选项。当指定operation时,它会显示出该特定operation的用法和相应的描述。例如,希望了解allocate操作,你可以执行hod help allocate

+

除上面的操作外,HOD还能接受下列命令行选项。

+

--help
+ 打印出用法和基本选项的帮助信息。

+

--verbose-help
+ hodrc文件中所有的配置项均可通过命令行传递,使用语法--section_name.option_name[=vlaue]。这种方式下,命令行传递的参数会覆盖hodrc中的配置项。verbose-help命令会列出hodrc文件中全部可用项。这也是一个了解配置选项含义的好方法。

+

下一部分有多数重要的hod配置项的描述。对于基本选项,你可以通过hod help options了解,对于所有的hod配置中的可能选项,你可以参看hod --verbose-help的输出。了解所有选项的描述,请参看配置指南

+
+
HOD配置选项 +

如上所述,HOD的配置是通过系统管理员设置配置文件完成。这是一个INI风格的配置文件,文件分成多个段,每个段包含一些配置项。这些段分别和HOD的进程:client,ringmaster,hodring,mapreduce或hdfs相关。每一个配置项有选项名和值构成。

+

有两种方式可让用户覆盖默认配置文件里的设定:

+
    +
  • 在每条命令前,用户可以向HOD提供自己的配置文件,使用-c选项。
  • +
  • 用户可以在命令行指定HOD的配置选项覆盖正使用的配置文件中提供的值。
  • +
+

这一节介绍一些最常用的配置项。为了指定方便,这些常用选项通常会有一个选项名。所有其它选项可能用随后介绍的选项指定。

+

-c config_file
+ 提供要使用的配置文件。可与其他任何的HOD选项一起使用。此外,可定义HOD_CONF_DIR环境变量为一个包含hodrc文件的目录,避免每条HOD命令都要指定配置文件。

+

-d cluster_dir
+ 大多数hod操作都要求这个选项。如此处描述的,集群目录是在本地文件系统上的一个目录,hod将它分配集群的相应Hadoop配置产生在这个目录里,即hadoop-site.xml。使用-d或者--hod.clusterdir将这个参数传递给hod操作,如果目录不存在,HOD会自动创建该目录。集群分配好后,用户可在这个集群上,通过指定hadoop--config为集群目录来执行Hadoop作业。

+

-n number_of_nodes
+ hod allocation操作和script操作要求这个选项。表示要分配的节点数。

+

-s script-file
+ 脚本操作时需要,用于指定要执行的脚本文件。

+

-b 1|2|3|4
+ 启用给定的调试级别。能与其他HOD选项一起使用。级别4最为详尽。

+

-t hadoop_tarball
+ 从指定tar.gz文件提供Hadoop分发。此选项值只适用于allocate操作。为获得更好的分发性能,强烈推荐创建Hadoop tarball删除其中的源代码或文档。

+

-N job-name
+ 内部使用的资源管理作业名。比如,对于Torque作为资源管理器的情况,会被解释成qsub -N选项,使用qstat命令时可以看到这个作业名。

+

-l wall-clock-time
+ 用户希望在分配的集群作业的时间总量。它被传递给HOD底层的资源管理器,用于更有效地调度和利用集群。注意对于Torque的情形,这个时间到期后,集群会在被自动回收。

+

-j java-home
+ JAVA_HOME环境变量里指定的路径。在script操作中使用。HOD将JAVA_HOME环境变量设置为这个值,并在此环境下启动用户脚本。

+

-A account-string
+ 传递给后台资源管理器的核计信息。

+

-Q queue-name
+ 接受作业提交的后台资源管理器中队列的名称。

+

-Mkey1=value1 -Mkey2=value2
+ 为供应的Map/Reduce守护进程(JobTracker以及TaskTracker)提供配置参数。在集群节点上,会根据这些值产生一个hadoop-site.xml。
+ 注意:值中的下列字符:空格,逗号,等号,分号需要使用‘\’转义, 且放置在引号中。你也可以使用‘\’来转义‘\’。

+

-Hkey1=value1 -Hkey2=value2
+ 为供应的HDFS守护进程(NameNode以及DataNode)提供配置参数。在集群节点上,会根据这些值产生一个hadoop-site.xml。
+ 注意:值中的下列字符:空格,逗号,等号,分号需要使用‘\’转义, 且放置在引号中。你也可以使用‘\’来转义‘\’。

+

-Ckey1=value1 -Ckey2=value2
+ 为提交作业的客户端提供配置参数。在提交节点上,会根据这些值产生一个hadoop-site.xml。
+ 注意:参数值可以使用以下符号:空格,逗号,等号,需要‘\’做转义符的分号,上述符号要用引号进行分割。你也可以使用‘\’转义‘\’。

+

--section-name.option-name=value
+ 这是用格式提供配置选项的方法。比如,你可以--hod.script-wait-time=20

+
+
+
+ 故障排除 +

下节列出了一些用户使用HOD时可能碰到的多发错误的条件以及解决问题的方法

+
分配操作时<code>hod</code>挂起 +

可能原因:HOD或Hadoop的一个组件启动失败。这种情况下,hod命令会在一段时间(通常是2-3分钟)后返回,退出码是错误代码部分定义的错误码7或8。参考该部分以获得更多细节。

+

可能原因:使用tarball模式申请了大规模的集群。有时由于网络负载,或者是分配节点上的负载,tarball分发过程可能会慢的比较明显,需要几分钟才能响应。等待命令完成。还可以检查一下tarball,看是否不含Hadoop源码或文档。

+

可能原因:Torque相关的问题。如果原因与Torque相关,hod命令5分钟内是不会返回的。在调试模式下运行hod你会发现qstat命令被重复执行。在另一个shell中执行qstat命令你会发现作业处于Q(排队)状态。这通常说明Torque出现了问题。可能原因有个别节点宕机,或者增加了新节点但Torque不知。通常,需要系统管理员帮助解决此问题。

+
+
回收操作时<code>hod</code>挂起 +

可能原因:Torque相关的问题,通常是Torque server上的负载较大,或者是分配的集群非常大。一般来说,你唯一能做的是等待命令执行完成。

+
+
<code>hod</code>失败时的错误代码和错误信息 +

如果hod命令的退出码不是0,参考下面的退出代码表确定此情况发生的原因和相应的调试方法。

+

错误代码

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
错误代码含义可能原因及补救方法
1 配置错误 hodrc中的参数错误,或者其他与HOD配置相关的错误。此类情况下,错误信息已经足够帮你发现和解决问题。
2 无效操作执行hod help查看有效的操作列表。
3 无效操作参数执行hod help operation查看特定操作的用法。
4 调度失败 1. 请求分配了过多的资源。执行checknodes cluster_name查看是否有足够多的可用节点。
+ 2. 请求的资源超出了资源管理器的限制。
+ 3. Torque配置错误,Torque可执行文件路径配置错误,或者其它Torque相关问题。联系系统管理员。
5 执行作业失败 1. Torque作业被外部删除。执行Torque qstat命令查看是否有作业处于R(运行)状态。如果没有,尝试重新运行HOD。
+ 2. Torque的问题诸如服务器暂时性宕机,或者无响应。联系系统管理员。
+ 3. 系统管理员可能配置了帐号核实,并且一个非法的帐号被指定。请联系系统管理员。
6 Ringmaster故障 HOD会打印信息"Cluster could not be allocated because of the following errors on the ringmaster host <hostname>"。实际的错误信息可能指示下列情形中的一种:
+ 1. 运行ringmaster的节点配置不合法,错误信息中的hostname会指明具体的机器。
+ 2. ringmaster段的配置无效,
+ 3. gridservice-mapred或者gridservice-hdfs段中pkgs项的配置无效,
+ 4. 无效的hadoop tarball,或者tarball中conf目录下存在无效的配置文件,
+ 5. Hadoop中的MapReduce与外部HDFS版本不匹配。
+ Torque qstat命令很可能会显示一个出于C(Completed,已完成)状态的作业。
+ 你可以登录到HOD失败信息中给出的ringmaster主机,根据错误信息的提示解决问题。如果错误信息没有给出完整的信息,ringmaster日志也可能帮助找到问题的根源。参考下面定位Ringmaster日志一节了解更多信息。
7 DFS故障 当HOD由于DFS故障(或者Job tracker失败,错误码8,下文有介绍)分配失败时,它会打印错误信息 "Hodring at <hostname> failed with following errors:",并给出真正的错误信息,这个信息可能表明下列情形中的一种:
+ 1. 启动Hadoop集群时出现问题。通常错误信息会表明之前提到的主机出现错误的真正原因。你也要检查HOD配置中文件中Hadoop相关的配置。按上面收集和查看Hadoop日志一节中介绍的方法查看Hadoop的日志。
+ 2. 运行hodring的节点上的配置无效,错误信息中的hostname会指明机器
+ 3. hodrc中hodring段的配置无效。ssh到错误信息中提到的节点,在hdring日志中grepERRORCRITICAL。参考下面定位Hodring日志部分获取更多信息。
+ 4. 指定了无效的tarball,可能未正确打包。
+ 5. 无法与外部配置的HDFS通信。
+ 当DFS或Job tracker出现故障时,你可以登录到HOD失败信息中提到的主机上,进行debug。解决问题的时候,你也应通过查看ringmaster日志中的其它日志信息,来检查其他机器是否在启动jobtracker/namenode时也出现了问题,而不只是检查错误信息中提到的主机。其他机器也可能发生问题是因为HOD会按照配置项ringmaster.max-master-failures的设置在多个机器上连续尝试和启动hadoop守护进程。更多关于ringmaster日志的信息请参考下文定位Ringmaster日志。 +
8 Job tracker故障DFS故障情形中的原因类似。
10 集群死亡1. 集群因为较长时间空闲被自动回收。
+ 2. 集群因系统管理员或者用户指定的时钟时间到期被自动回收。
+ 3. 无法与成功分配的JobTracker以及HDFS的NameNode通信。回收集群,重新分配。
12 集群已分配指定的集群目录是已被用于先前的分配操作,且尚未回收。指定另外一个目录,或者先回收先前分配的。
13 HDFS死亡无法与HDFS的NameNode通信。HDFS的NameNode停掉了。
14 Mapred死亡 1. 集群因为长时间闲置被自动回收。
+ 2. 集群因系统管理员或用户指定的时钟时间到期被自动回收。
+ 3. 无法与Map/Reduce的JobTracker通信。JobTracker节点宕机。
+
15 集群未分配一个需要已分配集群的操作被指以一个没有状态信息的集群目录。
任意非0退出代码HOD脚本错误如果使用了hod的脚本选项,很可能这个退出代码是脚本的退出吗。不幸的是,这可能会与hod自己的退出码冲突。为帮助用户区分两者,如果脚本返回了一个退出码,hod将此退出码写到了集群目录下的script.exitcode文件。你可以cat这个文件以确定脚本的退出码。如果文件不存在,则退出代码是hod命令的退出码。
+
+ +
Hadoop DFSClient警告NotReplicatedYetException信息 +

有时,当你申请到一个HOD集群后马上尝试上传文件到HDFS时,DFSClient会警告NotReplicatedYetException。通常会有一个这样的信息 -

WARN +hdfs.DFSClient: NotReplicatedYetException sleeping <filename> retries +left 3
08/01/25 16:31:40 INFO hdfs.DFSClient: +org.apache.hadoop.ipc.RemoteException: java.io.IOException: File +<filename> could only be replicated to 0 nodes, instead of +1

当你向一个DataNodes正在和NameNode联络的集群上传文件的时候,这种现象就会发生。在上传新文件到HDFS之前多等待一段时间就可以解决这个问题,因为这使得足够多的DataNode启动并且联络上了NameNode。

+
+ + +
成功分配的集群上无法运行Hadoop作业 +

这一情景通常发生在这种情形:一个集群已经分配,并且一段时间内处于不活跃状态,之后hadoop作业试图在这个集群上运行。Hadoop作业会失败,产生如下异常信息:

+
08/01/25 16:31:40 INFO ipc.Client: Retrying connect to server: foo.bar.com/1.1.1.1:53567. Already tried 1 time(s).
+

可能原因:相当长的时间内无hadoop作业运行,集群会如闲置集群的自动回收一节介绍的那样被自动回收。回收该集群,然后重新分配。

+

可能原因:从分配开始算起,Torque管理员指定的或指定额外的作业属性一节中定义的-l选项指定的时间上限过期。这种情况下集群可能已被释放。回收集群,然后重新分配。

+

可能原因:提交作业使用的hadoop版本和供应集群的Hadoop版本(通常通过tarball选项)不匹配。确保使用的兼容的版本。

+

可能原因: 提交job的hadoop客户端与提供的hadoop(通常通过tarball选项)版本不兼容。 确保所使用hadoop软件版本兼容。

+

可能原因: 你使用了-M or -H中的一个指定Hadoop配置,其中有未正确转义的字符比如空格或逗号。参考HOD配置选项一节以了解如何正确指定这些选项。

+
+
我的Hadoop作业被中止了 +

可能原因:从分配开始算起,Torque管理员指定的或指定额外的作业属性一节中定义的-l选项指定的时间上限过期。这种情况下集群可能已被释放。回收集群,然后重新分配,这次要制定一个大点儿的时钟时间。

+

可能原因: JobTracker节点出现问题。参考收集和查看Hadoop日志一节以获取更多信息。

+
+
Hadoop作业失败并返回消息:‘Job tracker still initializing’ +

可能原因:hadoop作业是作为HOD脚本的一部分运行的,它在JobTracker完全就绪前开始了执行。分配集群时为配置选--hod.script-wait-time设定一个大点儿的值。通常取120是可以工作的,尽管通常没必要这么大。

+
+
Torque的退出代码没有包含HOD的 +

可能原因:此功能需要Hadoop 0.16。所用的Hadoop版本不满足这个条件。请使用合适的Hadoop版本。

+

可能原因:没有使用hod命令回收集群;例如直接使用qdel。当使用这种方式回收集群时,HOD进程被信号中止。这会导致退出码是基于signal number的,而不是程序的退出码。

+
+
Hadoop日志未被上传到DFS +

可能原因:上传日志的使用的hadoop与外部的HDFS版本不兼容。确保hodring.pkgs选项指定了正确的版本。

+
+
定位Ringmaster日志 +

遵循以下步骤定位ringmaster日志:

+
    +
  • 用-b选项在调试模式执行hod。这会打印出当前运行的Torque作业的标识。
  • +
  • 执行qstat -f torque_job_id,在输出中查找exec_host参数的值。列表中的第一个主机就是ringmaster节点。
  • +
  • 登陆该节点。
  • +
  • ringmaster日志的位置由hodrc中的ringmaster.log-dir项指定。日志文件的名字会是username.torque_job_id/ringmaster-main.log
  • +
  • 如果你没有获取到足够的信息,你可以将ringmaster的调试级别设为4。这可通过向hod命令行传递--ringmaster.debug 4做到。
  • +
+
+
定位Hodring日志 +

遵循以下步骤定位hodring日志:

+
    +
  • 用-b选项在调试模式下运行hod。这将打印当前运行的Torque作业的标识。
  • +
  • 执行qstat -f torque_job_id,查看输出中exec_host参数的值。列表中的的所有节点上都有一个hodring。
  • +
  • 登陆到任何一个节点。
  • +
  • hodring日志的位置由hodrc中的hodring.log-dir项指定。日志文件的名字会是username.torque_job_id/hodring-main.log
  • +
  • 如果你没有获得足够的信息,你或许想将hodring的调试等级更改为4。这可以向hod命令行传递--hodring.debug 4 来做到。
  • +
+
+
+ +
diff --git a/src/docs/cn/src/documentation/content/xdocs/index.xml b/src/docs/cn/src/documentation/content/xdocs/index.xml new file mode 100644 index 00000000000..de63185b52f --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/index.xml @@ -0,0 +1,46 @@ + + + + + + + +
+ Hadoop文档 +
+ + +

+ 下面的文档是一些概念介绍和操作教程,可帮助你开始使用Hadoop。如果遇到了问题,你可以向邮件列表求助或者浏览一下存档邮件。 +

+ +

+

+ + + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/mapred_tutorial.xml b/src/docs/cn/src/documentation/content/xdocs/mapred_tutorial.xml new file mode 100644 index 00000000000..d842f4d93a8 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/mapred_tutorial.xml @@ -0,0 +1,2466 @@ + + + + + + +
+ Hadoop Map/Reduce教程 +
+ + + +
+ 目的 + +

这篇教程从用户的角度出发,全面地介绍了Hadoop Map/Reduce框架的各个方面。

+
+ +
+ 先决条件 + +

请先确认Hadoop被正确安装、配置和正常运行中。更多信息见:

+ +
+ +
+ 概述 + +

Hadoop Map/Reduce是一个使用简易的软件框架,基于它写出来的应用程序能够运行在由上千个商用机器组成的大型集群上,并以一种可靠容错的方式并行处理上T级别的数据集。

+ +

一个Map/Reduce 作业(job) 通常会把输入的数据集切分为若干独立的数据块,由 + map任务(task)以完全并行的方式处理它们。框架会对map的输出先进行排序, + 然后把结果输入给reduce任务。通常作业的输入和输出都会被存储在文件系统中。 + 整个框架负责任务的调度和监控,以及重新执行已经失败的任务。

+ +

通常,Map/Reduce框架和分布式文件系统是运行在一组相同的节点上的,也就是说,计算节点和存储节点通常在一起。这种配置允许框架在那些已经存好数据的节点上高效地调度任务,这可以使整个集群的网络带宽被非常高效地利用。

+ +

Map/Reduce框架由一个单独的master JobTracker 和每个集群节点一个slave TaskTracker共同组成。master负责调度构成一个作业的所有任务,这些任务分布在不同的slave上,master监控它们的执行,重新执行已经失败的任务。而slave仅负责执行由master指派的任务。

+ +

应用程序至少应该指明输入/输出的位置(路径),并通过实现合适的接口或抽象类提供map和reduce函数。再加上其他作业的参数,就构成了作业配置(job configuration)。然后,Hadoop的 job client提交作业(jar包/可执行程序等)和配置信息给JobTracker,后者负责分发这些软件和配置信息给slave、调度任务并监控它们的执行,同时提供状态和诊断信息给job-client。

+ +

虽然Hadoop框架是用JavaTM实现的,但Map/Reduce应用程序则不一定要用 + Java来写 。

+ +
+ +
+ 输入与输出 + +

Map/Reduce框架运转在<key, value> 键值对上,也就是说, + 框架把作业的输入看为是一组<key, value> 键值对,同样也产出一组 + <key, value> 键值对做为作业的输出,这两组键值对的类型可能不同。

+ +

框架需要对keyvalue的类(classes)进行序列化操作, + 因此,这些类需要实现 Writable接口。 + 另外,为了方便框架执行排序操作,key类必须实现 + + WritableComparable接口。 +

+ +

一个Map/Reduce 作业的输入和输出类型如下所示:

+

+ (input) <k1, v1> + -> + map + -> + <k2, v2> + -> + combine + -> + <k2, v2> + -> + reduce + -> + <k3, v3> (output) +

+
+ +
+ 例子:WordCount v1.0 + +

在深入细节之前,让我们先看一个Map/Reduce的应用示例,以便对它们的工作方式有一个初步的认识。

+

WordCount是一个简单的应用,它可以计算出指定数据集中每一个单词出现的次数。

+

这个应用适用于 + 单机模式, + 伪分布式模式 或 + 完全分布式模式 + 三种Hadoop安装方式。

+ +
+ 源代码 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WordCount.java
1. + package org.myorg; +
2.
3. + import java.io.IOException; +
4. + import java.util.*; +
5.
6. + import org.apache.hadoop.fs.Path; +
7. + import org.apache.hadoop.conf.*; +
8. + import org.apache.hadoop.io.*; +
9. + import org.apache.hadoop.mapred.*; +
10. + import org.apache.hadoop.util.*; +
11.
12. + public class WordCount { +
13.
14. +    + + public static class Map extends MapReduceBase + implements Mapper<LongWritable, Text, Text, IntWritable> { + +
15. +      + + private final static IntWritable one = new IntWritable(1); + +
16. +      + private Text word = new Text(); +
17.
18. +      + + public void map(LongWritable key, Text value, + OutputCollector<Text, IntWritable> output, + Reporter reporter) throws IOException { + +
19. +        + String line = value.toString(); +
20. +        + StringTokenizer tokenizer = new StringTokenizer(line); +
21. +        + while (tokenizer.hasMoreTokens()) { +
22. +          + word.set(tokenizer.nextToken()); +
23. +          + output.collect(word, one); +
24. +        + } +
25. +      + } +
26. +    + } +
27.
28. +    + + public static class Reduce extends MapReduceBase implements + Reducer<Text, IntWritable, Text, IntWritable> { + +
29. +      + + public void reduce(Text key, Iterator<IntWritable> values, + OutputCollector<Text, IntWritable> output, + Reporter reporter) throws IOException { + +
30. +        + int sum = 0; +
31. +        + while (values.hasNext()) { +
32. +          + sum += values.next().get(); +
33. +        + } +
34. +        + output.collect(key, new IntWritable(sum)); +
35. +      + } +
36. +    + } +
37.
38. +    + + public static void main(String[] args) throws Exception { + +
39. +      + + JobConf conf = new JobConf(WordCount.class); + +
40. +      + conf.setJobName("wordcount"); +
41.
42. +      + conf.setOutputKeyClass(Text.class); +
43. +      + conf.setOutputValueClass(IntWritable.class); +
44.
45. +      + conf.setMapperClass(Map.class); +
46. +      + conf.setCombinerClass(Reduce.class); +
47. +      + conf.setReducerClass(Reduce.class); +
48.
49. +      + conf.setInputFormat(TextInputFormat.class); +
50. +      + conf.setOutputFormat(TextOutputFormat.class); +
51.
52. +      + FileInputFormat.setInputPaths(conf, new Path(args[0])); +
53. +      + FileOutputFormat.setOutputPath(conf, new Path(args[1])); +
54.
55. +      + JobClient.runJob(conf); +
57. +    + } +
58. + } +
59.
+
+ +
+ 用法 + +

假设环境变量HADOOP_HOME对应安装时的根目录,HADOOP_VERSION对应Hadoop的当前安装版本,编译WordCount.java来创建jar包,可如下操作:

+

+ $ mkdir wordcount_classes
+ + $ javac -classpath ${HADOOP_HOME}/hadoop-${HADOOP_VERSION}-core.jar + -d wordcount_classes WordCount.java +
+ $ jar -cvf /usr/joe/wordcount.jar -C wordcount_classes/ . +

+ +

假设:

+
    +
  • + /usr/joe/wordcount/input - 是HDFS中的输入路径 +
  • +
  • + /usr/joe/wordcount/output - 是HDFS中的输出路径 +
  • +
+ +

用示例文本文件做为输入:

+

+ $ bin/hadoop dfs -ls /usr/joe/wordcount/input/
+ /usr/joe/wordcount/input/file01
+ /usr/joe/wordcount/input/file02
+
+ $ bin/hadoop dfs -cat /usr/joe/wordcount/input/file01
+ Hello World Bye World
+
+ $ bin/hadoop dfs -cat /usr/joe/wordcount/input/file02
+ Hello Hadoop Goodbye Hadoop +

+ +

运行应用程序:

+

+ + $ bin/hadoop jar /usr/joe/wordcount.jar org.myorg.WordCount + /usr/joe/wordcount/input /usr/joe/wordcount/output + +

+ +

输出是:

+

+ + $ bin/hadoop dfs -cat /usr/joe/wordcount/output/part-00000 + +
+ Bye 1
+ Goodbye 1
+ Hadoop 2
+ Hello 2
+ World 2
+

+

应用程序能够使用-files选项来指定一个由逗号分隔的路径列表,这些路径是task的当前工作目录。使用选项-libjars可以向map和reduce的classpath中添加jar包。使用-archives选项程序可以传递档案文件做为参数,这些档案文件会被解压并且在task的当前工作目录下会创建一个指向解压生成的目录的符号链接(以压缩包的名字命名)。 + 有关命令行选项的更多细节请参考 + Commands manual

+ +

使用-libjars-files运行wordcount例子:
+ hadoop jar hadoop-examples.jar wordcount -files cachefile.txt + -libjars mylib.jar input output +

+ +
+ +
+ 解释 + +

WordCount应用程序非常直截了当。

+ +

Mapper(14-26行)中的map方法(18-25行)通过指定的 + TextInputFormat(49行)一次处理一行。然后,它通过StringTokenizer + 以空格为分隔符将一行切分为若干tokens,之后,输出< <word>, 1> + 形式的键值对。

+ +

+ 对于示例中的第一个输入,map输出是:
+ < Hello, 1>
+ < World, 1>
+ < Bye, 1>
+ < World, 1>
+

+ +

+ 第二个输入,map输出是:
+ < Hello, 1>
+ < Hadoop, 1>
+ < Goodbye, 1>
+ < Hadoop, 1>
+

+ +

关于组成一个指定作业的map数目的确定,以及如何以更精细的方式去控制这些map,我们将在教程的后续部分学习到更多的内容。

+ +

WordCount还指定了一个combiner (46行)。因此,每次map运行之后,会对输出按照key进行排序,然后把输出传递给本地的combiner(按照作业的配置与Reducer一样),进行本地聚合。

+ +

+ 第一个map的输出是:
+ < Bye, 1>
+ < Hello, 1>
+ < World, 2>
+

+ +

+ 第二个map的输出是:
+ < Goodbye, 1>
+ < Hadoop, 2>
+ < Hello, 1>
+

+ +

Reducer(28-36行)中的reduce方法(29-35行) + 仅是将每个key(本例中就是单词)出现的次数求和。 +

+ +

+ 因此这个作业的输出就是:
+ < Bye, 1>
+ < Goodbye, 1>
+ < Hadoop, 2>
+ < Hello, 2>
+ < World, 2>
+

+ +

代码中的run方法中指定了作业的几个方面, + 例如:通过命令行传递过来的输入/输出路径、key/value的类型、输入/输出的格式等等JobConf中的配置信息。随后程序调用了JobClient.runJob(55行)来提交作业并且监控它的执行。

+ +

我们将在本教程的后续部分学习更多的关于JobConfJobClient, + Tool和其他接口及类(class)。

+
+
+ +
+ Map/Reduce - 用户界面 + +

这部分文档为用户将会面临的Map/Reduce框架中的各个环节提供了适当的细节。这应该会帮助用户更细粒度地去实现、配置和调优作业。然而,请注意每个类/接口的javadoc文档提供最全面的文档;本文只是想起到指南的作用。 +

+ +

我们会先看看MapperReducer接口。应用程序通常会通过提供mapreduce方法来实现它们。 +

+ +

然后,我们会讨论其他的核心接口,其中包括: + JobConfJobClientPartitioner, + OutputCollectorReporter, + InputFormatOutputFormat等等。

+ +

最后,我们将通过讨论框架中一些有用的功能点(例如:DistributedCache, + IsolationRunner等等)来收尾。

+ +
+ 核心功能描述 + +

应用程序通常会通过提供mapreduce来实现 + MapperReducer接口,它们组成作业的核心。

+ +
+ Mapper + +

+ Mapper将输入键值对(key/value pair)映射到一组中间格式的键值对集合。

+ +

Map是一类将输入记录集转换为中间格式记录集的独立任务。 + 这种转换的中间格式记录集不需要与输入记录集的类型一致。一个给定的输入键值对可以映射成0个或多个输出键值对。

+ +

Hadoop Map/Reduce框架为每一个InputSplit产生一个map任务,而每个InputSplit是由该作业的InputFormat产生的。

+ +

概括地说,对Mapper的实现者需要重写 + + JobConfigurable.configure(JobConf)方法,这个方法需要传递一个JobConf参数,目的是完成Mapper的初始化工作。然后,框架为这个任务的InputSplit中每个键值对调用一次 + + map(WritableComparable, Writable, OutputCollector, Reporter)操作。应用程序可以通过重写Closeable.close()方法来执行相应的清理工作。

+ +

输出键值对不需要与输入键值对的类型一致。一个给定的输入键值对可以映射成0个或多个输出键值对。通过调用 + OutputCollector.collect(WritableComparable,Writable)可以收集输出的键值对。

+ +

应用程序可以使用Reporter报告进度,设定应用级别的状态消息,更新Counters(计数器),或者仅是表明自己运行正常。

+ +

框架随后会把与一个特定key关联的所有中间过程的值(value)分成组,然后把它们传给Reducer以产出最终的结果。用户可以通过 + + JobConf.setOutputKeyComparatorClass(Class)来指定具体负责分组的 + Comparator

+ +

Mapper的输出被排序后,就被划分给每个Reducer。分块的总数目和一个作业的reduce任务的数目是一样的。用户可以通过实现自定义的 Partitioner来控制哪个key被分配给哪个 Reducer

+ +

用户可选择通过 + JobConf.setCombinerClass(Class)指定一个combiner,它负责对中间过程的输出进行本地的聚集,这会有助于降低从Mapper到 + Reducer数据传输量。 +

+

这些被排好序的中间过程的输出结果保存的格式是(key-len, key, value-len, value),应用程序可以通过JobConf控制对这些中间结果是否进行压缩以及怎么压缩,使用哪种 + CompressionCodec。 +

+ +
+ 需要多少个Map? + +

Map的数目通常是由输入数据的大小决定的,一般就是所有输入文件的总块(block)数。

+ +

Map正常的并行规模大致是每个节点(node)大约10到100个map,对于CPU + 消耗较小的map任务可以设到300个左右。由于每个任务初始化需要一定的时间,因此,比较合理的情况是map执行的时间至少超过1分钟。

+ +

这样,如果你输入10TB的数据,每个块(block)的大小是128MB,你将需要大约82,000个map来完成任务,除非使用 + + setNumMapTasks(int)(注意:这里仅仅是对框架进行了一个提示(hint),实际决定因素见这里)将这个数值设置得更高。

+
+
+ +
+ Reducer + +

+ Reducer将与一个key关联的一组中间数值集归约(reduce)为一个更小的数值集。

+ +

用户可以通过 + JobConf.setNumReduceTasks(int)设定一个作业中reduce任务的数目。

+ +

概括地说,对Reducer的实现者需要重写 + + JobConfigurable.configure(JobConf)方法,这个方法需要传递一个JobConf参数,目的是完成Reducer的初始化工作。然后,框架为成组的输入数据中的每个<key, (list of values)>对调用一次 + + reduce(WritableComparable, Iterator, OutputCollector, Reporter)方法。之后,应用程序可以通过重写Closeable.close()来执行相应的清理工作。

+ +

Reducer有3个主要阶段:shuffle、sort和reduce。 +

+ +
+ Shuffle + +

Reducer的输入就是Mapper已经排好序的输出。在这个阶段,框架通过HTTP为每个Reducer获得所有Mapper输出中与之相关的分块。

+
+ +
+ Sort + +

这个阶段,框架将按照key的值对Reducer的输入进行分组 + (因为不同mapper的输出中可能会有相同的key)。

+ +

Shuffle和Sort两个阶段是同时进行的;map的输出也是一边被取回一边被合并的。

+ +
+ Secondary Sort + +

如果需要中间过程对key的分组规则和reduce前对key的分组规则不同,那么可以通过 + JobConf.setOutputValueGroupingComparator(Class)来指定一个Comparator。再加上 + + JobConf.setOutputKeyComparatorClass(Class)可用于控制中间过程的key如何被分组,所以结合两者可以实现按值的二次排序。 +

+
+
+ +
+ Reduce + +

在这个阶段,框架为已分组的输入数据中的每个 + <key, (list of values)>对调用一次 + + reduce(WritableComparable, Iterator, OutputCollector, Reporter)方法。

+ +

Reduce任务的输出通常是通过调用 + + OutputCollector.collect(WritableComparable, Writable)写入 + + 文件系统的。

+ +

应用程序可以使用Reporter报告进度,设定应用程序级别的状态消息,更新Counters(计数器),或者仅是表明自己运行正常。

+ +

Reducer的输出是没有排序的

+
+ +
+ 需要多少个Reduce? + +

Reduce的数目建议是0.951.75乘以 + (<no. of nodes> * + mapred.tasktracker.reduce.tasks.maximum)。 +

+ +

用0.95,所有reduce可以在maps一完成时就立刻启动,开始传输map的输出结果。用1.75,速度快的节点可以在完成第一轮reduce任务后,可以开始第二轮,这样可以得到比较好的负载均衡的效果。

+ +

增加reduce的数目会增加整个框架的开销,但可以改善负载均衡,降低由于执行失败带来的负面影响。

+ +

上述比例因子比整体数目稍小一些是为了给框架中的推测性任务(speculative-tasks) + 或失败的任务预留一些reduce的资源。

+
+ +
+ 无Reducer + +

如果没有归约要进行,那么设置reduce任务的数目为是合法的。

+ +

这种情况下,map任务的输出会直接被写入由 + + setOutputPath(Path)指定的输出路径。框架在把它们写入FileSystem之前没有对它们进行排序。 +

+
+
+ +
+ Partitioner + +

+ Partitioner用于划分键值空间(key space)。

+ +

Partitioner负责控制map输出结果key的分割。Key(或者一个key子集)被用于产生分区,通常使用的是Hash函数。分区的数目与一个作业的reduce任务的数目是一样的。因此,它控制将中间过程的key(也就是这条记录)应该发送给m个reduce任务中的哪一个来进行reduce操作。 +

+ +

+ HashPartitioner是默认的 Partitioner

+
+ +
+ Reporter + +

+ Reporter是用于Map/Reduce应用程序报告进度,设定应用级别的状态消息, + 更新Counters(计数器)的机制。

+ +

MapperReducer的实现可以利用Reporter + 来报告进度,或者仅是表明自己运行正常。在那种应用程序需要花很长时间处理个别键值对的场景中,这种机制是很关键的,因为框架可能会以为这个任务超时了,从而将它强行杀死。另一个避免这种情况发生的方式是,将配置参数mapred.task.timeout设置为一个足够高的值(或者干脆设置为零,则没有超时限制了)。 +

+ +

应用程序可以用Reporter来更新Counter(计数器)。 +

+
+ +
+ OutputCollector + +

+ OutputCollector是一个Map/Reduce框架提供的用于收集 + MapperReducer输出数据的通用机制 + (包括中间输出结果和作业的输出结果)。

+
+ +

Hadoop Map/Reduce框架附带了一个包含许多实用型的mapper、reducer和partitioner + 的类库

+
+ +
+ 作业配置 + +

+ JobConf代表一个Map/Reduce作业的配置。

+ +

JobConf是用户向Hadoop框架描述一个Map/Reduce作业如何执行的主要接口。框架会按照JobConf描述的信息忠实地去尝试完成这个作业,然而:

+
    +
  • + 一些参数可能会被管理者标记为 + final,这意味它们不能被更改。 +
  • +
  • + 一些作业的参数可以被直截了当地进行设置(例如: + + setNumReduceTasks(int)),而另一些参数则与框架或者作业的其他参数之间微妙地相互影响,并且设置起来比较复杂(例如: + setNumMapTasks(int))。 +
  • +
+ +

通常,JobConf会指明Mapper、Combiner(如果有的话)、 + PartitionerReducerInputFormat和 + OutputFormat的具体实现。JobConf还能指定一组输入文件 + (setInputPaths(JobConf, Path...) + /addInputPath(JobConf, Path)) + 和(setInputPaths(JobConf, String) + /addInputPaths(JobConf, String)) + 以及输出文件应该写在哪儿 + (setOutputPath(Path))。

+ +

JobConf可选择地对作业设置一些高级选项,例如:设置Comparator; + 放到DistributedCache上的文件;中间结果或者作业输出结果是否需要压缩以及怎么压缩; + 利用用户提供的脚本(setMapDebugScript(String)/setReduceDebugScript(String)) + 进行调试;作业是否允许预防性(speculative)任务的执行 + (setMapSpeculativeExecution(boolean))/(setReduceSpeculativeExecution(boolean)) + ;每个任务最大的尝试次数 + (setMaxMapAttempts(int)/setMaxReduceAttempts(int)) + ;一个作业能容忍的任务失败的百分比 + (setMaxMapTaskFailuresPercent(int)/setMaxReduceTaskFailuresPercent(int)) + ;等等。

+ +

当然,用户能使用 + set(String, String)/get(String, String) + 来设置或者取得应用程序需要的任意参数。然而,DistributedCache的使用是面向大规模只读数据的。

+
+ +
+ 任务的执行和环境 + +

TaskTracker是在一个单独的jvm上以子进程的形式执行 + Mapper/Reducer任务(Task)的。 +

+ +

子任务会继承父TaskTracker的环境。用户可以通过JobConf中的 + mapred.child.java.opts配置参数来设定子jvm上的附加选项,例如: + 通过-Djava.library.path=<> 将一个非标准路径设为运行时的链接用以搜索共享库,等等。如果mapred.child.java.opts包含一个符号@taskid@, + 它会被替换成map/reduce的taskid的值。

+ +

下面是一个包含多个参数和替换的例子,其中包括:记录jvm GC日志; + JVM JMX代理程序以无密码的方式启动,这样它就能连接到jconsole上,从而可以查看子进程的内存和线程,得到线程的dump;还把子jvm的最大堆尺寸设置为512MB, + 并为子jvm的java.library.path添加了一个附加路径。

+ +

+ <property>
+   <name>mapred.child.java.opts</name>
+   <value>
+      + -Xmx512M -Djava.library.path=/home/mycompany/lib + -verbose:gc -Xloggc:/tmp/@taskid@.gc
+      + -Dcom.sun.management.jmxremote.authenticate=false + -Dcom.sun.management.jmxremote.ssl=false
+   </value>
+ </property> +

+

用户或管理员也可以使用mapred.child.ulimit设定运行的子任务的最大虚拟内存。mapred.child.ulimit的值以(KB)为单位,并且必须大于或等于-Xmx参数传给JavaVM的值,否则VM会无法启动。

+

注意:mapred.child.java.opts只用于设置task tracker启动的子任务。为守护进程设置内存选项请查看 + + cluster_setup.html

+

${mapred.local.dir}/taskTracker/是task tracker的本地目录, + 用于创建本地缓存和job。它可以指定多个目录(跨越多个磁盘),文件会半随机的保存到本地路径下的某个目录。当job启动时,task tracker根据配置文档创建本地job目录,目录结构如以下所示:

+
    +
  • ${mapred.local.dir}/taskTracker/archive/ :分布式缓存。这个目录保存本地的分布式缓存。因此本地分布式缓存是在所有task和job间共享的。
  • +
  • ${mapred.local.dir}/taskTracker/jobcache/$jobid/ : + 本地job目录。 +
      +
    • ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/: + job指定的共享目录。各个任务可以使用这个空间做为暂存空间,用于它们之间共享文件。这个目录通过job.local.dir 参数暴露给用户。这个路径可以通过API + JobConf.getJobLocalDir()来访问。它也可以被做为系统属性获得。因此,用户(比如运行streaming)可以调用System.getProperty("job.local.dir")获得该目录。 +
    • +
    • ${mapred.local.dir}/taskTracker/jobcache/$jobid/jars/: + 存放jar包的路径,用于存放作业的jar文件和展开的jar。job.jar是应用程序的jar文件,它会被自动分发到各台机器,在task启动前会被自动展开。使用api + + JobConf.getJar() 函数可以得到job.jar的位置。使用JobConf.getJar().getParent()可以访问存放展开的jar包的目录。 +
    • +
    • ${mapred.local.dir}/taskTracker/jobcache/$jobid/job.xml: + 一个job.xml文件,本地的通用的作业配置文件。 +
    • +
    • ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid: + 每个任务有一个目录task-id,它里面有如下的目录结构: +
        +
      • ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/job.xml: + 一个job.xml文件,本地化的任务作业配置文件。任务本地化是指为该task设定特定的属性值。这些值会在下面具体说明。 +
      • +
      • ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/output + 一个存放中间过程的输出文件的目录。它保存了由framwork产生的临时map reduce数据,比如map的输出文件等。
      • +
      • ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/work: + task的当前工作目录。
      • +
      • ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/work/tmp: + task的临时目录。(用户可以设定属性mapred.child.tmp + 来为map和reduce task设定临时目录。缺省值是./tmp。如果这个值不是绝对路径, + 它会把task的工作路径加到该路径前面作为task的临时文件路径。如果这个值是绝对路径则直接使用这个值。 + 如果指定的目录不存在,会自动创建该目录。之后,按照选项 + -Djava.io.tmpdir='临时文件的绝对路径'执行java子任务。 + pipes和streaming的临时文件路径是通过环境变量TMPDIR='the absolute path of the tmp dir'设定的)。 + 如果mapred.child.tmp./tmp值,这个目录会被创建。
      • +
      +
    • +
    +
  • +
+

下面的属性是为每个task执行时使用的本地参数,它们保存在本地化的任务作业配置文件里:

+ + + + + + + + + + + + + + + + + + + + + + + +
名称类型描述
mapred.job.idStringjob id
mapred.jarStringjob目录下job.jar的位置
job.local.dir Stringjob指定的共享存储空间
mapred.tip.id String task id
mapred.task.id String task尝试id
mapred.task.is.map boolean 是否是map task
mapred.task.partition int task在job中的id
map.input.file String map读取的文件名
map.input.start long map输入的数据块的起始位置偏移
map.input.length long map输入的数据块的字节数
mapred.work.output.dir String task临时输出目录
+

task的标准输出和错误输出流会被读到TaskTracker中,并且记录到 + ${HADOOP_LOG_DIR}/userlogs

+

DistributedCache + 可用于map或reduce task中分发jar包和本地库。子jvm总是把 + 当前工作目录 加到 + java.library.pathLD_LIBRARY_PATH。 + 因此,可以通过 + + System.loadLibrary或 + + System.load装载缓存的库。有关使用分布式缓存加载共享库的细节请参考 + + native_libraries.html

+
+ +
+ 作业的提交与监控 + +

+ JobClient是用户提交的作业与JobTracker交互的主要接口。 +

+ +

JobClient 提供提交作业,追踪进程,访问子任务的日志记录,获得Map/Reduce集群状态信息等功能。 +

+ +

作业提交过程包括:

+
    +
  1. 检查作业输入输出样式细节
  2. +
  3. 为作业计算InputSplit值。
  4. +
  5. + 如果需要的话,为作业的DistributedCache建立必须的统计信息。 +
  6. +
  7. + 拷贝作业的jar包和配置文件到FileSystem上的Map/Reduce系统目录下。 +
  8. +
  9. + 提交作业到JobTracker并且监控它的状态。 +
  10. +
+

作业的历史文件记录到指定目录的"_logs/history/"子目录下。这个指定目录由hadoop.job.history.user.location设定,默认是作业输出的目录。因此默认情况下,文件会存放在mapred.output.dir/_logs/history目录下。用户可以设置hadoop.job.history.user.locationnone来停止日志记录。 +

+ +

用户使用下面的命令可以看到在指定目录下的历史日志记录的摘要。 +
+ $ bin/hadoop job -history output-dir
+ 这个命令会打印出作业的细节,以及失败的和被杀死的任务细节。
+ 要查看有关作业的更多细节例如成功的任务、每个任务尝试的次数(task attempt)等,可以使用下面的命令 +
+ $ bin/hadoop job -history all output-dir

+ +

用户可以使用 + OutputLogFilter + 从输出目录列表中筛选日志文件。

+ +

一般情况,用户利用JobConf创建应用程序并配置作业属性, + 然后用 + JobClient 提交作业并监视它的进程。

+ +
+ 作业的控制 + +

有时候,用一个单独的Map/Reduce作业并不能完成一个复杂的任务,用户也许要链接多个Map/Reduce作业才行。这是容易实现的,因为作业通常输出到分布式文件系统上的,所以可以把这个作业的输出作为下一个作业的输入实现串联。 +

+ +

然而,这也意味着,确保每一作业完成(成功或失败)的责任就直接落在了客户身上。在这种情况下,可以用的控制作业的选项有: +

+ +
+
+ +
+ 作业的输入 + +

+ InputFormat 为Map/Reduce作业描述输入的细节规范。 +

+ +

Map/Reduce框架根据作业的InputFormat来: +

+
    +
  1. 检查作业输入的有效性。
  2. +
  3. + 把输入文件切分成多个逻辑InputSplit实例, + 并把每一实例分别分发给一个 + Mapper。 +
  4. +
  5. + 提供RecordReader的实现,这个RecordReader从逻辑InputSplit中获得输入记录, + 这些记录将由Mapper处理。 +
  6. +
+ +

基于文件的InputFormat实现(通常是 + + FileInputFormat的子类) + 默认行为是按照输入文件的字节大小,把输入数据切分成逻辑分块(logical + InputSplit )。 + 其中输入文件所在的FileSystem的数据块尺寸是分块大小的上限。下限可以设置mapred.min.split.size + 的值。

+ +

考虑到边界情况,对于很多应用程序来说,很明显按照文件大小进行逻辑分割是不能满足需求的。 + 在这种情况下,应用程序需要实现一个RecordReader来处理记录的边界并为每个任务提供一个逻辑分块的面向记录的视图。 +

+ +

+ TextInputFormat 是默认的InputFormat

+ +

如果一个作业的InputformatTextInputFormat, + 并且框架检测到输入文件的后缀是.gz.lzo,就会使用对应的CompressionCodec自动解压缩这些文件。 + 但是需要注意,上述带后缀的压缩文件不会被切分,并且整个压缩文件会分给一个mapper来处理。 +

+ +
+ InputSplit + +

+ InputSplit 是一个单独的Mapper要处理的数据块。

+ +

一般的InputSplit 是字节样式输入,然后由RecordReader处理并转化成记录样式。 +

+ +

+ FileSplit 是默认的InputSplit。 它把 + map.input.file 设定为输入文件的路径,输入文件是逻辑分块文件。 +

+
+ +
+ RecordReader + +

+ RecordReaderInputSlit读入<key, value>对。 +

+ +

一般的,RecordReader 把由InputSplit + 提供的字节样式的输入文件,转化成由Mapper处理的记录样式的文件。 + 因此RecordReader负责处理记录的边界情况和把数据表示成keys/values对形式。 +

+
+
+ +
+ 作业的输出 + +

+ OutputFormat 描述Map/Reduce作业的输出样式。 +

+ +

Map/Reduce框架根据作业的OutputFormat来: +

+
    +
  1. + 检验作业的输出,例如检查输出路径是否已经存在。 +
  2. +
  3. + 提供一个RecordWriter的实现,用来输出作业结果。 + 输出文件保存在FileSystem上。 +
  4. +
+ +

TextOutputFormat是默认的 + OutputFormat

+ +
+ 任务的Side-Effect File + +

在一些应用程序中,子任务需要产生一些side-file,这些文件与作业实际输出结果的文件不同。 +

+ +

在这种情况下,同一个Mapper或者Reducer的两个实例(比如预防性任务)同时打开或者写 + FileSystem上的同一文件就会产生冲突。因此应用程序在写文件的时候需要为每次任务尝试(不仅仅是每次任务,每个任务可以尝试执行很多次)选取一个独一无二的文件名(使用attemptid,例如task_200709221812_0001_m_000000_0)。 +

+ +

为了避免冲突,Map/Reduce框架为每次尝试执行任务都建立和维护一个特殊的 + ${mapred.output.dir}/_temporary/_${taskid}子目录,这个目录位于本次尝试执行任务输出结果所在的FileSystem上,可以通过 + ${mapred.work.output.dir}来访问这个子目录。 + 对于成功完成的任务尝试,只有${mapred.output.dir}/_temporary/_${taskid}下的文件会移动${mapred.output.dir}。当然,框架会丢弃那些失败的任务尝试的子目录。这种处理过程对于应用程序来说是完全透明的。

+ +

在任务执行期间,应用程序在写文件时可以利用这个特性,比如 + 通过 + FileOutputFormat.getWorkOutputPath()获得${mapred.work.output.dir}目录, + 并在其下创建任意任务执行时所需的side-file,框架在任务尝试成功时会马上移动这些文件,因此不需要在程序内为每次任务尝试选取一个独一无二的名字。 +

+ +

注意:在每次任务尝试执行期间,${mapred.work.output.dir} 的值实际上是 + ${mapred.output.dir}/_temporary/_{$taskid},这个值是Map/Reduce框架创建的。 + 所以使用这个特性的方法是,在 + FileOutputFormat.getWorkOutputPath() + 路径下创建side-file即可。 +

+ +

对于只使用map不使用reduce的作业,这个结论也成立。这种情况下,map的输出结果直接生成到HDFS上。 +

+
+ +
+ RecordWriter + +

+ RecordWriter 生成<key, value> + 对到输出文件。

+ +

RecordWriter的实现把作业的输出结果写到 + FileSystem

+
+
+ +
+ 其他有用的特性 + +
+ Counters + +

Counters 是多个由Map/Reduce框架或者应用程序定义的全局计数器。 + 每一个Counter可以是任何一种 + Enum类型。同一特定Enum类型的Counter可以汇集到一个组,其类型为Counters.Group

+ +

应用程序可以定义任意(Enum类型)的Counters并且可以通过 map 或者 + reduce方法中的 + + Reporter.incrCounter(Enum, long)或者 + + Reporter.incrCounter(String, String, long) + 更新。之后框架会汇总这些全局counters。 +

+
+ +
+ DistributedCache + +

+ DistributedCache 可将具体应用相关的、大尺寸的、只读的文件有效地分布放置。 +

+ +

DistributedCache 是Map/Reduce框架提供的功能,能够缓存应用程序所需的文件 + (包括文本,档案文件,jar文件等)。 +

+

应用程序在JobConf中通过url(hdfs://)指定需要被缓存的文件。 + DistributedCache假定由hdfs://格式url指定的文件已经在 + FileSystem上了。

+ +

Map-Redcue框架在作业所有任务执行之前会把必要的文件拷贝到slave节点上。 + 它运行高效是因为每个作业的文件只拷贝一次并且为那些没有文档的slave节点缓存文档。 +

+ +

DistributedCache 根据缓存文档修改的时间戳进行追踪。 + 在作业执行期间,当前应用程序或者外部程序不能修改缓存文件。 +

+ +

distributedCache可以分发简单的只读数据或文本文件,也可以分发复杂类型的文件例如归档文件和jar文件。归档文件(zip,tar,tgz和tar.gz文件)在slave节点上会被解档(un-archived)。 + 这些文件可以设置执行权限

+

用户可以通过设置mapred.cache.{files|archives}来分发文件。 + 如果要分发多个文件,可以使用逗号分隔文件所在路径。也可以利用API来设置该属性: + + DistributedCache.addCacheFile(URI,conf)/ + + DistributedCache.addCacheArchive(URI,conf) and + + DistributedCache.setCacheFiles(URIs,conf)/ + + DistributedCache.setCacheArchives(URIs,conf) + 其中URI的形式是 + hdfs://host:port/absolute-path#link-name + 在Streaming程序中,可以通过命令行选项 + -cacheFile/-cacheArchive + 分发文件。

+

+ 用户可以通过 + DistributedCache.createSymlink(Configuration)方法让DistributedCache + 在当前工作目录下创建到缓存文件的符号链接。 + 或者通过设置配置文件属性mapred.create.symlinkyes。 + 分布式缓存会截取URI的片段作为链接的名字。 + 例如,URI是 hdfs://namenode:port/lib.so.1#lib.so, + 则在task当前工作目录会有名为lib.so的链接, + 它会链接分布式缓存中的lib.so.1。 +

+ +

DistributedCache可在map/reduce任务中作为 + 一种基础软件分发机制使用。它可以被用于分发jar包和本地库(native libraries)。 + + DistributedCache.addArchiveToClassPath(Path, Configuration)和 + + DistributedCache.addFileToClassPath(Path, Configuration) API能够被用于 + 缓存文件和jar包,并把它们加入子jvm的classpath。也可以通过设置配置文档里的属性 + mapred.job.classpath.{files|archives}达到相同的效果。缓存文件可用于分发和装载本地库。 +

+
+ +
+ Tool + +

Tool + 接口支持处理常用的Hadoop命令行选项。 +

+ +

Tool 是Map/Reduce工具或应用的标准。应用程序应只处理其定制参数, + 要把标准命令行选项通过 + ToolRunner.run(Tool, String[]) + 委托给 + + GenericOptionsParser处理。 +

+ +

+ Hadoop命令行的常用选项有:
+ + -conf <configuration file> + +
+ + -D <property=value> + +
+ + -fs <local|namenode:port> + +
+ + -jt <local|jobtracker:port> + +

+
+ +
+ IsolationRunner + +

+ IsolationRunner 是帮助调试Map/Reduce程序的工具。

+ +

使用IsolationRunner的方法是,首先设置 + keep.failed.task.files属性为true + (同时参考keep.task.files.pattern)。

+ +

+ 然后,登录到任务运行失败的节点上,进入 + TaskTracker的本地路径运行 + IsolationRunner
+ $ cd <local path>/taskTracker/${taskid}/work
+ + $ bin/hadoop org.apache.hadoop.mapred.IsolationRunner ../job.xml + +

+ +

IsolationRunner会把失败的任务放在单独的一个能够调试的jvm上运行,并且采用和之前完全一样的输入数据。 +

+
+ +
+ Profiling +

Profiling是一个工具,它使用内置的java profiler工具进行分析获得(2-3个)map或reduce样例运行分析报告。

+

用户可以通过设置属性mapred.task.profile指定系统是否采集profiler信息。 + 利用api + JobConf.setProfileEnabled(boolean)可以修改属性值。如果设为true, + 则开启profiling功能。profiler信息保存在用户日志目录下。缺省情况,profiling功能是关闭的。

+

如果用户设定使用profiling功能,可以使用配置文档里的属性 + mapred.task.profile.{maps|reduces} + 设置要profile map/reduce task的范围。设置该属性值的api是 + + JobConf.setProfileTaskRange(boolean,String)。 + 范围的缺省值是0-2

+

用户可以通过设定配置文档里的属性mapred.task.profile.params + 来指定profiler配置参数。修改属性要使用api + + JobConf.setProfileParams(String)。当运行task时,如果字符串包含%s。 + 它会被替换成profileing的输出文件名。这些参数会在命令行里传递到子JVM中。缺省的profiling + 参数是 + -agentlib:hprof=cpu=samples,heap=sites,force=n,thread=y,verbose=n,file=%s。 +

+
+ +
+ 调试 +

Map/Reduce框架能够运行用户提供的用于调试的脚本程序。 + 当map/reduce任务失败时,用户可以通过运行脚本在任务日志(例如任务的标准输出、标准错误、系统日志以及作业配置文件)上做后续处理工作。用户提供的调试脚本程序的标准输出和标准错误会输出为诊断文件。如果需要的话这些输出结果也可以打印在用户界面上。

+ +

在接下来的章节,我们讨论如何与作业一起提交调试脚本。为了提交调试脚本, + 首先要把这个脚本分发出去,而且还要在配置文件里设置。 +

+
+ 如何分发脚本文件: +

用户要用 + DistributedCache + 机制来分发链接脚本文件

+
+
+ 如何提交脚本: +

一个快速提交调试脚本的方法是分别为需要调试的map任务和reduce任务设置 + "mapred.map.task.debug.script" 和 "mapred.reduce.task.debug.script" + 属性的值。这些属性也可以通过 + + JobConf.setMapDebugScript(String) 和 + + JobConf.setReduceDebugScript(String) API来设置。对于streaming, + 可以分别为需要调试的map任务和reduce任务使用命令行选项-mapdebug 和 -reducedegug来提交调试脚本。 +

+ +

脚本的参数是任务的标准输出、标准错误、系统日志以及作业配置文件。在运行map/reduce失败的节点上运行调试命令是: +
+ $script $stdout $stderr $syslog $jobconf

+ +

Pipes 程序根据第五个参数获得c++程序名。 + 因此调试pipes程序的命令是
+ $script $stdout $stderr $syslog $jobconf $program +

+
+ +
+ 默认行为 +

对于pipes,默认的脚本会用gdb处理core dump, + 打印 stack trace并且给出正在运行线程的信息。

+
+
+ +
+ JobControl + +

+ JobControl是一个工具,它封装了一组Map/Reduce作业以及他们之间的依赖关系。 +

+
+ +
+ 数据压缩 + +

Hadoop Map/Reduce框架为应用程序的写入文件操作提供压缩工具,这些工具可以为map输出的中间数据和作业最终输出数据(例如reduce的输出)提供支持。它还附带了一些 + + CompressionCodec的实现,比如实现了 + zliblzo压缩算法。 + Hadoop同样支持gzip文件格式。 +

+ +

考虑到性能问题(zlib)以及Java类库的缺失(lzo)等因素,Hadoop也为上述压缩解压算法提供本地库的实现。更多的细节请参考 + 这里

+ +
+ 中间输出 + +

应用程序可以通过 + + JobConf.setCompressMapOutput(boolean)api控制map输出的中间结果,并且可以通过 + + JobConf.setMapOutputCompressorClass(Class)api指定 + CompressionCodec。 +

+
+ +
+ 作业输出 +

应用程序可以通过 + + FileOutputFormat.setCompressOutput(JobConf, boolean) + api控制输出是否需要压缩并且可以使用 + + FileOutputFormat.setOutputCompressorClass(JobConf, Class)api指定CompressionCodec

+ +

如果作业输出要保存成 + + SequenceFileOutputFormat格式,需要使用 + + SequenceFileOutputFormat.setOutputCompressionType(JobConf, + SequenceFile.CompressionType)api,来设定 + SequenceFile.CompressionType (i.e. RECORD / + BLOCK - 默认是RECORD)。 +

+
+
+ +
+
+ +
+ 例子:WordCount v2.0 + +

这里是一个更全面的WordCount例子,它使用了我们已经讨论过的很多Map/Reduce框架提供的功能。 +

+ +

运行这个例子需要HDFS的某些功能,特别是 + DistributedCache相关功能。因此这个例子只能运行在 + 伪分布式 或者 + 完全分布式模式的 + Hadoop上。

+ +
+ 源代码 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WordCount.java
1. + package org.myorg; +
2.
3. + import java.io.*; +
4. + import java.util.*; +
5.
6. + import org.apache.hadoop.fs.Path; +
7. + import org.apache.hadoop.filecache.DistributedCache; +
8. + import org.apache.hadoop.conf.*; +
9. + import org.apache.hadoop.io.*; +
10. + import org.apache.hadoop.mapred.*; +
11. + import org.apache.hadoop.util.*; +
12.
13. + public class WordCount extends Configured implements Tool { +
14.
15. +    + + public static class Map extends MapReduceBase + implements Mapper<LongWritable, Text, Text, IntWritable> { + +
16.
17. +      + + static enum Counters { INPUT_WORDS } + +
18.
19. +      + + private final static IntWritable one = new IntWritable(1); + +
20. +      + private Text word = new Text(); +
21.
22. +      + private boolean caseSensitive = true; +
23. +      + private Set<String> patternsToSkip = new HashSet<String>(); +
24.
25. +      + private long numRecords = 0; +
26. +      + private String inputFile; +
27.
28. +      + public void configure(JobConf job) { +
29. +        + + caseSensitive = job.getBoolean("wordcount.case.sensitive", true); + +
30. +        + inputFile = job.get("map.input.file"); +
31.
32. +        + if (job.getBoolean("wordcount.skip.patterns", false)) { +
33. +          + Path[] patternsFiles = new Path[0]; +
34. +          + try { +
35. +            + + patternsFiles = DistributedCache.getLocalCacheFiles(job); + +
36. +          + } catch (IOException ioe) { +
37. +            + + System.err.println("Caught exception while getting cached files: " + + StringUtils.stringifyException(ioe)); + +
38. +          + } +
39. +          + for (Path patternsFile : patternsFiles) { +
40. +            + parseSkipFile(patternsFile); +
41. +          + } +
42. +        + } +
43. +      + } +
44.
45. +      + private void parseSkipFile(Path patternsFile) { +
46. +        + try { +
47. +          + + BufferedReader fis = + new BufferedReader(new FileReader(patternsFile.toString())); + +
48. +          + String pattern = null; +
49. +          + while ((pattern = fis.readLine()) != null) { +
50. +            + patternsToSkip.add(pattern); +
51. +          + } +
52. +        + } catch (IOException ioe) { +
53. +          + + System.err.println("Caught exception while parsing the cached file '" + + patternsFile + "' : " + + StringUtils.stringifyException(ioe)); + + +
54. +        + } +
55. +      + } +
56.
57. +      + + public void map(LongWritable key, Text value, + OutputCollector<Text, IntWritable> output, + Reporter reporter) throws IOException { + +
58. +        + + String line = + (caseSensitive) ? value.toString() : + value.toString().toLowerCase(); + +
59.
60. +        + for (String pattern : patternsToSkip) { +
61. +          + line = line.replaceAll(pattern, ""); +
62. +        + } +
63.
64. +        + StringTokenizer tokenizer = new StringTokenizer(line); +
65. +        + while (tokenizer.hasMoreTokens()) { +
66. +          + word.set(tokenizer.nextToken()); +
67. +          + output.collect(word, one); +
68. +          + reporter.incrCounter(Counters.INPUT_WORDS, 1); +
69. +        + } +
70.
71. +        + if ((++numRecords % 100) == 0) { +
72. +          + + reporter.setStatus("Finished processing " + numRecords + + " records " + "from the input file: " + + inputFile); + +
73. +        + } +
74. +      + } +
75. +    + } +
76.
77. +    + + public static class Reduce extends MapReduceBase implements + Reducer<Text, IntWritable, Text, IntWritable> { + +
78. +      + + public void reduce(Text key, Iterator<IntWritable> values, + OutputCollector<Text, IntWritable> output, + Reporter reporter) throws IOException { + +
79. +        + int sum = 0; +
80. +        + while (values.hasNext()) { +
81. +          + sum += values.next().get(); +
82. +        + } +
83. +        + output.collect(key, new IntWritable(sum)); +
84. +      + } +
85. +    + } +
86.
87. +    + public int run(String[] args) throws Exception { +
88. +      + + JobConf conf = new JobConf(getConf(), WordCount.class); + +
89. +      + conf.setJobName("wordcount"); +
90.
91. +      + conf.setOutputKeyClass(Text.class); +
92. +      + conf.setOutputValueClass(IntWritable.class); +
93.
94. +      + conf.setMapperClass(Map.class); +
95. +      + conf.setCombinerClass(Reduce.class); +
96. +      + conf.setReducerClass(Reduce.class); +
97.
98. +      + conf.setInputFormat(TextInputFormat.class); +
99. +      + conf.setOutputFormat(TextOutputFormat.class); +
100.
101. +      + + List<String> other_args = new ArrayList<String>(); + +
102. +      + for (int i=0; i < args.length; ++i) { +
103. +        + if ("-skip".equals(args[i])) { +
104. +          + + DistributedCache.addCacheFile(new Path(args[++i]).toUri(), conf); + +
105. +          + + conf.setBoolean("wordcount.skip.patterns", true); + +
106. +        + } else { +
107. +          + other_args.add(args[i]); +
108. +        + } +
109. +      + } +
110.
111. +      + FileInputFormat.setInputPaths(conf, new Path(other_args.get(0))); +
112. +      + FileOutputFormat.setOutputPath(conf, new Path(other_args.get(1))); +
113.
114. +      + JobClient.runJob(conf); +
115. +      + return 0; +
116. +    + } +
117.
118. +    + + public static void main(String[] args) throws Exception { + +
119. +      + + int res = ToolRunner.run(new Configuration(), new WordCount(), + args); + +
120. +      + System.exit(res); +
121. +    + } +
122. + } +
123.
+
+ +
+ 运行样例 + +

输入样例:

+

+ $ bin/hadoop dfs -ls /usr/joe/wordcount/input/
+ /usr/joe/wordcount/input/file01
+ /usr/joe/wordcount/input/file02
+
+ $ bin/hadoop dfs -cat /usr/joe/wordcount/input/file01
+ Hello World, Bye World!
+
+ $ bin/hadoop dfs -cat /usr/joe/wordcount/input/file02
+ Hello Hadoop, Goodbye to hadoop. +

+ +

运行程序:

+

+ + $ bin/hadoop jar /usr/joe/wordcount.jar org.myorg.WordCount + /usr/joe/wordcount/input /usr/joe/wordcount/output + +

+ +

输出:

+

+ + $ bin/hadoop dfs -cat /usr/joe/wordcount/output/part-00000 + +
+ Bye 1
+ Goodbye 1
+ Hadoop, 1
+ Hello 2
+ World! 1
+ World, 1
+ hadoop. 1
+ to 1
+

+ +

注意此时的输入与第一个版本的不同,输出的结果也有不同。 +

+ +

现在通过DistributedCache插入一个模式文件,文件中保存了要被忽略的单词模式。 +

+ +

+ $ hadoop dfs -cat /user/joe/wordcount/patterns.txt
+ \.
+ \,
+ \!
+ to
+

+ +

再运行一次,这次使用更多的选项:

+

+ + $ bin/hadoop jar /usr/joe/wordcount.jar org.myorg.WordCount + -Dwordcount.case.sensitive=true /usr/joe/wordcount/input + /usr/joe/wordcount/output -skip /user/joe/wordcount/patterns.txt + +

+ +

应该得到这样的输出:

+

+ + $ bin/hadoop dfs -cat /usr/joe/wordcount/output/part-00000 + +
+ Bye 1
+ Goodbye 1
+ Hadoop 1
+ Hello 2
+ World 2
+ hadoop 1
+

+ +

再运行一次,这一次关闭大小写敏感性(case-sensitivity):

+

+ + $ bin/hadoop jar /usr/joe/wordcount.jar org.myorg.WordCount + -Dwordcount.case.sensitive=false /usr/joe/wordcount/input + /usr/joe/wordcount/output -skip /user/joe/wordcount/patterns.txt + +

+ +

输出:

+

+ + $ bin/hadoop dfs -cat /usr/joe/wordcount/output/part-00000 + +
+ bye 1
+ goodbye 1
+ hadoop 2
+ hello 2
+ world 2
+

+
+ +
+ 程序要点 + +

+ 通过使用一些Map/Reduce框架提供的功能,WordCount的第二个版本在原始版本基础上有了如下的改进: +

+
    +
  • + 展示了应用程序如何在Mapper (和Reducer)中通过configure方法 + 修改配置参数(28-43行)。 +
  • +
  • + 展示了作业如何使用DistributedCache 来分发只读数据。 + 这里允许用户指定单词的模式,在计数时忽略那些符合模式的单词(104行)。 +
  • +
  • + 展示Tool接口和GenericOptionsParser处理Hadoop命令行选项的功能 + (87-116, 119行)。 +
  • +
  • + 展示了应用程序如何使用Counters(68行),如何通过传递给map(和reduce) + 方法的Reporter实例来设置应用程序的状态信息(72行)。 +
  • +
+ +
+
+ +

+ Java和JNI是Sun Microsystems, Inc.在美国和其它国家的注册商标。 +

+ + + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/native_libraries.xml b/src/docs/cn/src/documentation/content/xdocs/native_libraries.xml new file mode 100644 index 00000000000..091cffc3e92 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/native_libraries.xml @@ -0,0 +1,230 @@ + + + + + + + +
+ Hadoop本地库 +
+ + + +
+ 目的 + +

+ 鉴于性能问题以及某些Java类库的缺失,对于某些组件,Hadoop提供了自己的本地实现。 + 这些组件保存在Hadoop的一个独立的动态链接的库里。这个库在*nix平台上叫libhadoop.so. 本文主要介绍本地库的使用方法以及如何构建本地库。 +

+
+ +
+ 组件 + +

Hadoop现在已经有以下 + + compression codecs本地组件:

+ + +

在以上组件中,lzo和gzip压缩编解码器必须使用hadoop本地库才能运行。 +

+
+ +
+ 使用方法 + +

hadoop本地库的用法很简单:

+ + + +

bin/hadoop 脚本通过系统属性 + -Djava.library.path=<path>来确认hadoop本地库是否包含在库路径里。

+ +

检查hadoop日志文件可以查看hadoop库是否正常,正常情况下会看到:

+ +

+ + DEBUG util.NativeCodeLoader - Trying to load the custom-built + native-hadoop library... +
+ + INFO util.NativeCodeLoader - Loaded the native-hadoop library + +

+ +

如果出错,会看到:

+

+ + INFO util.NativeCodeLoader - Unable to load native-hadoop library for + your platform... using builtin-java classes where applicable + +

+
+ +
+ 支持的平台 + +

Hadoop本地库只支持*nix平台,已经广泛使用在GNU/Linux平台上,但是不支持 + Cygwin + 和 Mac OS X。 +

+ +

已经测试过的GNU/Linux发行版本:

+ + +

在上述平台上,32/64位Hadoop本地库分别能和32/64位的jvm一起正常运行。 +

+
+ +
+ 构建Hadoop本地库 + +

Hadoop本地库使用 + ANSI C 编写,使用GNU autotools工具链 (autoconf, autoheader, automake, autoscan, libtool)构建。也就是说构建hadoop库的平台需要有标准C的编译器和GNU autotools工具链。请参看 + 支持的平台

+ +

你的目标平台上可能会需要的软件包: +

+ + +

如果已经满足了上述先决条件,可以使用build.xml + 文件,并把其中的compile.native置为 + true,这样就可以生成hadoop本地库:

+ +

$ ant -Dcompile.native=true <target>

+ +

因为不是所有用户都需要Hadoop本地库,所以默认情况下hadoop不生成该库。 +

+ +

你可以在下面的路径查看新生成的hadoop本地库:

+ +

$ build/native/<platform>/lib

+ +

其中<platform>是下列系统属性的组合 + ${os.name}-${os.arch}-${sun.arch.data.model};例如 + Linux-i386-32。

+ +
+ 注意 + +
    +
  • + 在生成hadoop本地库的目标平台上必须 安装了zlib和lzo开发包;但是如果你只希望使用其中一个的话,在部署时,安装其中任何一个都是足够的。 +
  • +
  • + 在目标平台上生成以及部署hadoop本地库时,都需要根据32/64位jvm选取对应的32/64位zlib/lzo软件包。 +
  • +
+
+
+ +
+ 使用DistributedCache 加载本地库 +

用户可以通过 + DistributedCache + 加载本地共享库,并分发和建立库文件的符号链接。 +

+ +

这个例子描述了如何分发库文件并在从map/reduce任务中装载库文件。 +

+
    +
  1. 首先拷贝库文件到HDFS。
    + bin/hadoop fs -copyFromLocal mylib.so.1 /libraries/mylib.so.1 +
  2. +
  3. 启动作业时包含以下代码:
    + DistributedCache.createSymlink(conf);
    + DistributedCache.addCacheFile("hdfs://host:port/libraries/mylib.so.1#mylib.so", conf); + +
  4. +
  5. map/reduce任务中包含以下代码:
    + System.loadLibrary("mylib.so"); +
  6. +
+
+ + +
+ diff --git a/src/docs/cn/src/documentation/content/xdocs/quickstart.xml b/src/docs/cn/src/documentation/content/xdocs/quickstart.xml new file mode 100644 index 00000000000..e13395563ef --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/quickstart.xml @@ -0,0 +1,252 @@ + + + + + + +
+ Hadoop快速入门 +
+ + + +
+ 目的 +

这篇文档的目的是帮助你快速完成单机上的Hadoop安装与使用以便你对Hadoop分布式文件系统(HDFS)和Map-Reduce框架有所体会,比如在HDFS上运行示例程序或简单作业等。

+
+
+ 先决条件 + +
+ 支持平台 + +
    +
  • + GNU/Linux是产品开发和运行的平台。 + Hadoop已在有2000个节点的GNU/Linux主机组成的集群系统上得到验证。 +
  • +
  • + Win32平台是作为开发平台支持的。由于分布式操作尚未在Win32平台上充分测试,所以还不作为一个生产平台被支持。 +
  • +
+
+ +
+ 所需软件 +

Linux和Windows所需软件包括:

+
    +
  1. + JavaTM1.5.x,必须安装,建议选择Sun公司发行的Java版本。 +
  2. +
  3. + ssh 必须安装并且保证 sshd一直运行,以便用Hadoop + 脚本管理远端Hadoop守护进程。 +
  4. +
+

Windows下的附加软件需求

+
    +
  1. + Cygwin - 提供上述软件之外的shell支持。 +
  2. +
+
+ +
+ 安装软件 + +

如果你的集群尚未安装所需软件,你得首先安装它们。

+ +

以Ubuntu Linux为例:

+

+ $ sudo apt-get install ssh
+ $ sudo apt-get install rsync +

+ +

在Windows平台上,如果安装cygwin时未安装全部所需软件,则需启动cyqwin安装管理器安装如下软件包:

+
    +
  • openssh - Net
  • +
+
+ +
+ +
+ 下载 + +

+ 为了获取Hadoop的发行版,从Apache的某个镜像服务器上下载最近的 + 稳定发行版

+
+ +
+ 运行Hadoop集群的准备工作 +

+ 解压所下载的Hadoop发行版。编辑 + conf/hadoop-env.sh文件,至少需要将JAVA_HOME设置为Java安装根路径。 +

+ +

+ 尝试如下命令:
+ $ bin/hadoop
+ 将会显示hadoop 脚本的使用文档。 +

+ +

现在你可以用以下三种支持的模式中的一种启动Hadoop集群: +

+ +
+ +
+ 单机模式的操作方法 + +

默认情况下,Hadoop被配置成以非分布式模式运行的一个独立Java进程。这对调试非常有帮助。

+ +

+ 下面的实例将已解压的 conf 目录拷贝作为输入,查找并显示匹配给定正则表达式的条目。输出写入到指定的output目录。 +
+ $ mkdir input
+ $ cp conf/*.xml input
+ + $ bin/hadoop jar hadoop-*-examples.jar grep input output 'dfs[a-z.]+' +
+ $ cat output/* +

+
+ +
+ 伪分布式模式的操作方法 + +

Hadoop可以在单节点上以所谓的伪分布式模式运行,此时每一个Hadoop守护进程都作为一个独立的Java进程运行。

+ +
+ 配置 +

使用如下的 conf/hadoop-site.xml:

+ + + + + + + + + + + + + + + + + + + +
<configuration>
  <property>
    <name>fs.default.name</name>
    <value>localhost:9000</value>
  </property>
  <property>
    <name>mapred.job.tracker</name>
    <value>localhost:9001</value>
  </property>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>
+
+ +
+ 免密码<em>ssh</em>设置 + +

+ 现在确认能否不输入口令就用ssh登录localhost:
+ $ ssh localhost +

+ +

+ 如果不输入口令就无法用ssh登陆localhost,执行下面的命令:
+ $ ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
+ $ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys +

+
+ +
+ 执行 + +

+ 格式化一个新的分布式文件系统:
+ $ bin/hadoop namenode -format +

+ +

+ 启动Hadoop守护进程:
+ $ bin/start-all.sh +

+ +

Hadoop守护进程的日志写入到 + ${HADOOP_LOG_DIR} 目录 (默认是 + ${HADOOP_HOME}/logs).

+ +

浏览NameNode和JobTracker的网络接口,它们的地址默认为:

+ + +

+ 将输入文件拷贝到分布式文件系统:
+ $ bin/hadoop fs -put conf input +

+ +

+ 运行发行版提供的示例程序:
+ + $ bin/hadoop jar hadoop-*-examples.jar grep input output 'dfs[a-z.]+' + +

+ +

查看输出文件:

+

+ 将输出文件从分布式文件系统拷贝到本地文件系统查看:
+ $ bin/hadoop fs -get output output
+ $ cat output/* +

+

或者

+

+ 在分布式文件系统上查看输出文件:
+ $ bin/hadoop fs -cat output/* +

+ +

+ 完成全部操作后,停止守护进程:
+ $ bin/stop-all.sh +

+
+
+ +
+ 完全分布式模式的操作方法 + +

关于搭建完全分布式模式的,有实际意义的集群的资料可以在这里找到。

+
+ +

+ Java与JNI是Sun Microsystems, Inc.在美国以及其他国家地区的商标或注册商标。 +

+ + + +
diff --git a/src/docs/cn/src/documentation/content/xdocs/site.xml b/src/docs/cn/src/documentation/content/xdocs/site.xml new file mode 100644 index 00000000000..329186df68d --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/site.xml @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/content/xdocs/streaming.xml b/src/docs/cn/src/documentation/content/xdocs/streaming.xml new file mode 100644 index 00000000000..221205e13f1 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/streaming.xml @@ -0,0 +1,618 @@ + + + + + + + +
+Hadoop Streaming +Content-Type +text/html; +utf-8 +
+ +
+Hadoop Streaming +

+Hadoop streaming是Hadoop的一个工具, + 它帮助用户创建和运行一类特殊的map/reduce作业, + 这些特殊的map/reduce作业是由一些可执行文件或脚本文件充当mapper或者reducer。例如: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper /bin/cat \ + -reducer /bin/wc + +
+ +
+Streaming工作原理 +

+在上面的例子里,mapper和reducer都是可执行文件,它们从标准输入读入数据(一行一行读), +并把计算结果发给标准输出。Streaming工具会创建一个Map/Reduce作业, +并把它发送给合适的集群,同时监视这个作业的整个执行过程。 +

+如果一个可执行文件被用于mapper,则在mapper初始化时, +每一个mapper任务会把这个可执行文件作为一个单独的进程启动。 +mapper任务运行时,它把输入切分成行并把每一行提供给可执行文件进程的标准输入。 +同时,mapper收集可执行文件进程标准输出的内容,并把收到的每一行内容转化成key/value对,作为mapper的输出。 +默认情况下,一行中第一个tab之前的部分作为key,之后的(不包括tab)作为value。 +如果没有tab,整行作为key值,value值为null。不过,这可以定制,在下文中将会讨论如何自定义key和value的切分方式。 +

+

如果一个可执行文件被用于reducer,每个reducer任务会把这个可执行文件作为一个单独的进程启动。 +Reducer任务运行时,它把输入切分成行并把每一行提供给可执行文件进程的标准输入。 +同时,reducer收集可执行文件进程标准输出的内容,并把每一行内容转化成key/value对,作为reducer的输出。 +默认情况下,一行中第一个tab之前的部分作为key,之后的(不包括tab)作为value。在下文中将会讨论如何自定义key和value的切分方式。 +

+这是Map/Reduce框架和streaming mapper/reducer之间的基本通信协议。 +

+用户也可以使用java类作为mapper或者reducer。上面的例子与这里的代码等价: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper org.apache.hadoop.mapred.lib.IdentityMapper \ + -reducer /bin/wc + +

用户可以设定stream.non.zero.exit.is.failure +truefalse 来表明streaming task的返回值非零时是 +Failure +还是Success。默认情况,streaming task返回非零时表示失败。 +

+
+ +
+将文件打包到提交的作业中 +

+任何可执行文件都可以被指定为mapper/reducer。这些可执行文件不需要事先存放在集群上; +如果在集群上还没有,则需要用-file选项让framework把可执行文件作为作业的一部分,一起打包提交。例如: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper myPythonScript.py \ + -reducer /bin/wc \ + -file myPythonScript.py + +

+上面的例子描述了一个用户把可执行python文件作为mapper。 +其中的选项“-file myPythonScirpt.py”使可执行python文件作为作业提交的一部分被上传到集群的机器上。 +

+

+除了可执行文件外,其他mapper或reducer需要用到的辅助文件(比如字典,配置文件等)也可以用这种方式打包上传。例如: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper myPythonScript.py \ + -reducer /bin/wc \ + -file myPythonScript.py \ + -file myDictionary.txt + +
+ +
+Streaming选项与用法 + +
+只使用Mapper的作业 +

+有时只需要map函数处理输入数据。这时只需把mapred.reduce.tasks设置为零,Map/reduce框架就不会创建reducer任务,mapper任务的输出就是整个作业的最终输出。 +

+为了做到向下兼容,Hadoop Streaming也支持“-reduce None”选项,它与“-jobconf mapred.reduce.tasks=0”等价。 +

+
+ +
+为作业指定其他插件 +

+和其他普通的Map/Reduce作业一样,用户可以为streaming作业指定其他插件: +

+ + -inputformat JavaClassName + -outputformat JavaClassName + -partitioner JavaClassName + -combiner JavaClassName + +

用于处理输入格式的类要能返回Text类型的key/value对。如果不指定输入格式,则默认会使用TextInputFormat。 +因为TextInputFormat得到的key值是LongWritable类型的(其实key值并不是输入文件中的内容,而是value偏移量), +所以key会被丢弃,只把value用管道方式发给mapper。 +

+用户提供的定义输出格式的类需要能够处理Text类型的key/value对。如果不指定输出格式,则默认会使用TextOutputFormat类。 +

+
+ +
+Hadoop Streaming中的大文件和档案 + +

任务使用-cacheFile和-cacheArchive选项在集群中分发文件和档案,选项的参数是用户已上传至HDFS的文件或档案的URI。这些文件和档案在不同的作业间缓存。用户可以通过fs.default.name.config配置参数的值得到文件所在的host和fs_port。 +

+

+这个是使用-cacheFile选项的例子: +

+ +-cacheFile hdfs://host:fs_port/user/testfile.txt#testlink + +

在上面的例子里,url中#后面的部分是建立在任务当前工作目录下的符号链接的名字。这里的任务的当前工作目录下有一个“testlink”符号链接,它指向testfile.txt文件在本地的拷贝。如果有多个文件,选项可以写成: +

+ +-cacheFile hdfs://host:fs_port/user/testfile1.txt#testlink1 -cacheFile hdfs://host:fs_port/user/testfile2.txt#testlink2 + +

+-cacheArchive选项用于把jar文件拷贝到任务当前工作目录并自动把jar文件解压缩。例如: +

+ +-cacheArchive hdfs://host:fs_port/user/testfile.jar#testlink3 + +

+在上面的例子中,testlink3是当前工作目录下的符号链接,它指向testfile.jar解压后的目录。 +

+

+下面是使用-cacheArchive选项的另一个例子。其中,input.txt文件有两行内容,分别是两个文件的名字:testlink/cache.txt和testlink/cache2.txt。“testlink”是指向档案目录(jar文件解压后的目录)的符号链接,这个目录下有“cache.txt”和“cache2.txt”两个文件。 +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input "/user/me/samples/cachefile/input.txt" \ + -mapper "xargs cat" \ + -reducer "cat" \ + -output "/user/me/samples/cachefile/out" \ + -cacheArchive 'hdfs://hadoop-nn1.example.com/user/me/samples/cachefile/cachedir.jar#testlink' \ + -jobconf mapred.map.tasks=1 \ + -jobconf mapred.reduce.tasks=1 \ + -jobconf mapred.job.name="Experiment" + +$ ls test_jar/ +cache.txt cache2.txt + +$ jar cvf cachedir.jar -C test_jar/ . +added manifest +adding: cache.txt(in = 30) (out= 29)(deflated 3%) +adding: cache2.txt(in = 37) (out= 35)(deflated 5%) + +$ hadoop dfs -put cachedir.jar samples/cachefile + +$ hadoop dfs -cat /user/me/samples/cachefile/input.txt +testlink/cache.txt +testlink/cache2.txt + +$ cat test_jar/cache.txt +This is just the cache string + +$ cat test_jar/cache2.txt +This is just the second cache string + +$ hadoop dfs -ls /user/me/samples/cachefile/out +Found 1 items +/user/me/samples/cachefile/out/part-00000 <r 3> 69 + +$ hadoop dfs -cat /user/me/samples/cachefile/out/part-00000 +This is just the cache string +This is just the second cache string + + +
+ +
+为作业指定附加配置参数 +

+用户可以使用“-jobconf <n>=<v>”增加一些配置变量。例如: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper org.apache.hadoop.mapred.lib.IdentityMapper\ + -reducer /bin/wc \ + -jobconf mapred.reduce.tasks=2 + +

+上面的例子中,-jobconf mapred.reduce.tasks=2表明用两个reducer完成作业。 +

+

+关于jobconf参数的更多细节可以参考:hadoop-default.html

+
+ +
+其他选项 +

+Streaming 作业的其他选项如下表: +

+ + + + + + + + + + + + + +
选项可选/必须描述
-cluster name 可选 在本地Hadoop集群与一个或多个远程集群间切换
-dfs host:port or local 可选 覆盖作业的HDFS配置
-jt host:port or local 可选 覆盖作业的JobTracker配置
-additionalconfspec specfile 可选 用一个类似于hadoop-site.xml的XML文件保存所有配置,从而不需要用多个"-jobconf name=value"类型的选项单独为每个配置变量赋值
-cmdenv name=value 可选 传递环境变量给streaming命令
-cacheFile fileNameURI 可选 指定一个上传到HDFS的文件
-cacheArchive fileNameURI 可选 指定一个上传到HDFS的jar文件,这个jar文件会被自动解压缩到当前工作目录下
-inputreader JavaClassName 可选 为了向下兼容:指定一个record reader类(而不是input format类)
-verbose 可选 详细输出
+

+使用-cluster <name>实现“本地”Hadoop和一个或多个远程Hadoop集群间切换。默认情况下,使用hadoop-default.xml和hadoop-site.xml;当使用-cluster <name>选项时,会使用$HADOOP_HOME/conf/hadoop-<name>.xml。 +

+

+下面的选项改变temp目录: +

+ + -jobconf dfs.data.dir=/tmp + +

+下面的选项指定其他本地temp目录: +

+ + -jobconf mapred.local.dir=/tmp/local + -jobconf mapred.system.dir=/tmp/system + -jobconf mapred.temp.dir=/tmp/temp + +

+更多有关jobconf的细节请参考:http://wiki.apache.org/hadoop/JobConfFile +

+在streaming命令中设置环境变量: +

+ +-cmdenv EXAMPLE_DIR=/home/example/dictionaries/ + +
+
+ +
+其他例子 + +
+使用自定义的方法切分行来形成Key/Value对 +

+之前已经提到,当Map/Reduce框架从mapper的标准输入读取一行时,它把这一行切分为key/value对。 +在默认情况下,每行第一个tab符之前的部分作为key,之后的部分作为value(不包括tab符)。 +

+

+但是,用户可以自定义,可以指定分隔符是其他字符而不是默认的tab符,或者指定在第n(n>=1)个分割符处分割而不是默认的第一个。例如: +

+ + +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper org.apache.hadoop.mapred.lib.IdentityMapper \ + -reducer org.apache.hadoop.mapred.lib.IdentityReducer \ + -jobconf stream.map.output.field.separator=. \ + -jobconf stream.num.map.output.key.fields=4 + +

+在上面的例子,“-jobconf stream.map.output.field.separator=.”指定“.”作为map输出内容的分隔符,并且从在第四个“.”之前的部分作为key,之后的部分作为value(不包括这第四个“.”)。 +如果一行中的“.”少于四个,则整行的内容作为key,value设为空的Text对象(就像这样创建了一个Text:new Text(""))。 +

+同样,用户可以使用“-jobconf stream.reduce.output.field.separator=SEP”和“-jobconf stream.num.reduce.output.fields=NUM”来指定reduce输出的行中,第几个分隔符处分割key和value。 +

+
+ + +
+一个实用的Partitioner类<!--A Useful Partitioner Class--> (二次排序,-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner 选项) +

+Hadoop有一个工具类org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner, +它在应用程序中很有用。Map/reduce框架用这个类切分map的输出, +切分是基于key值的前缀,而不是整个key。例如: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper org.apache.hadoop.mapred.lib.IdentityMapper \ + -reducer org.apache.hadoop.mapred.lib.IdentityReducer \ + -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \ + -jobconf stream.map.output.field.separator=. \ + -jobconf stream.num.map.output.key.fields=4 \ + -jobconf map.output.key.field.separator=. \ + -jobconf num.key.fields.for.partition=2 \ + -jobconf mapred.reduce.tasks=12 + +

+其中,-jobconf stream.map.output.field.separator=.-jobconf stream.num.map.output.key.fields=4是前文中的例子。Streaming用这两个变量来得到mapper的key/value对。 +

+上面的Map/Reduce 作业中map输出的key一般是由“.”分割成的四块。但是因为使用了 +-jobconf num.key.fields.for.partition=2 +选项,所以Map/Reduce框架使用key的前两块来切分map的输出。其中, +-jobconf map.output.key.field.separator=. +指定了这次切分使用的key的分隔符。这样可以保证在所有key/value对中, +key值前两个块值相同的所有key被分到一组,分配给一个reducer。 +

+这种高效的方法等价于指定前两块作为主键,后两块作为副键。 +主键用于切分块,主键和副键的组合用于排序。一个简单的示例如下: +

+

+Map的输出(key)

+11.12.1.2 +11.14.2.3 +11.11.4.1 +11.12.1.1 +11.14.2.2 + + +

+切分给3个reducer(前两块的值用于切分)

+11.11.4.1 +----------- +11.12.1.2 +11.12.1.1 +----------- +11.14.2.3 +11.14.2.2 + +

+在每个切分后的组内排序(四个块的值都用于排序) +

+11.11.4.1 +----------- +11.12.1.1 +11.12.1.2 +----------- +11.14.2.2 +11.14.2.3 + +
+ +
+ Hadoop聚合功能包的使用(-reduce aggregate 选项) +

+Hadoop有一个工具包“Aggregate”( +https://svn.apache.org/repos/asf/hadoop/core/trunk/src/java/org/apache/hadoop/mapred/lib/aggregate)。 +“Aggregate”提供一个特殊的reducer类和一个特殊的combiner类, +并且有一系列的“聚合器”(“aggregator”)(例如“sum”,“max”,“min”等)用于聚合一组value的序列。 +用户可以使用Aggregate定义一个mapper插件类, +这个类用于为mapper输入的每个key/value对产生“可聚合项”。 +combiner/reducer利用适当的聚合器聚合这些可聚合项。 +

+要使用Aggregate,只需指定“-reducer aggregate”:

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper myAggregatorForKeyCount.py \ + -reducer aggregate \ + -file myAggregatorForKeyCount.py \ + -jobconf mapred.reduce.tasks=12 + +

+python程序myAggregatorForKeyCount.py例子: +

+ +#!/usr/bin/python + +import sys; + +def generateLongCountToken(id): + return "LongValueSum:" + id + "\t" + "1" + +def main(argv): + line = sys.stdin.readline(); + try: + while line: + line = line[:-1]; + fields = line.split("\t"); + print generateLongCountToken(fields[0]); + line = sys.stdin.readline(); + except "end of file": + return None +if __name__ == "__main__": + main(sys.argv) + +
+ +
+字段的选取(类似于unix中的 'cut' 命令) +

+Hadoop的工具类org.apache.hadoop.mapred.lib.FieldSelectionMapReduce帮助用户高效处理文本数据, +就像unix中的“cut”工具。工具类中的map函数把输入的key/value对看作字段的列表。 +用户可以指定字段的分隔符(默认是tab), +可以选择字段列表中任意一段(由列表中一个或多个字段组成)作为map输出的key或者value。 +同样,工具类中的reduce函数也把输入的key/value对看作字段的列表,用户可以选取任意一段作为reduce输出的key或value。例如: +

+ +$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input myInputDirs \ + -output myOutputDir \ + -mapper org.apache.hadoop.mapred.lib.FieldSelectionMapReduce\ + -reducer org.apache.hadoop.mapred.lib.FieldSelectionMapReduce\ + -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \ + -jobconf map.output.key.field.separa=. \ + -jobconf num.key.fields.for.partition=2 \ + -jobconf mapred.data.field.separator=. \ + -jobconf map.output.key.value.fields.spec=6,5,1-3:0- \ + -jobconf reduce.output.key.value.fields.spec=0-2:5- \ + -jobconf mapred.reduce.tasks=12 + +

+选项“-jobconf map.output.key.value.fields.spec=6,5,1-3:0-”指定了如何为map的输出选取key和value。Key选取规则和value选取规则由“:”分割。 +在这个例子中,map输出的key由字段6,5,1,2和3组成。输出的value由所有字段组成(“0-”指字段0以及之后所有字段)。 +

+

+选项“-jobconf reduce.output.key.value.fields.spec=0-2:0-”(译者注:此处应为”0-2:5-“)指定如何为reduce的输出选取value。 +本例中,reduce的输出的key将包含字段0,1,2(对应于原始的字段6,5,1)。 +reduce输出的value将包含起自字段5的所有字段(对应于所有的原始字段)。 +

+
+
+ +
+常见问题 + +
+我该怎样使用Hadoop Streaming运行一组独立(相关)的任务呢? +

+多数情况下,你不需要Map Reduce的全部功能, +而只需要运行同一程序的多个实例,或者使用不同数据,或者在相同数据上使用不同的参数。 +你可以通过Hadoop Streaming来实现。

+ +
+ +
+如何处理多个文件,其中每个文件一个map? +

+例如这样一个问题,在集群上压缩(zipping)一些文件,你可以使用以下几种方法:

    +
  1. 使用Hadoop Streaming和用户编写的mapper脚本程序:
      +
    • 生成一个文件,文件中包含所有要压缩的文件在HDFS上的完整路径。每个map 任务获得一个路径名作为输入。
    • +
    • 创建一个mapper脚本程序,实现如下功能:获得文件名,把该文件拷贝到本地,压缩该文件并把它发到期望的输出目录。
    • +
  2. +
  3. 使用现有的Hadoop框架:
      +
    • 在main函数中添加如下命令: + + FileOutputFormat.setCompressOutput(conf, true); + FileOutputFormat.setOutputCompressorClass(conf, org.apache.hadoop.io.compress.GzipCodec.class); + conf.setOutputFormat(NonSplitableTextInputFormat.class); + conf.setNumReduceTasks(0); +
    • +
    • 编写map函数: + + + public void map(WritableComparable key, Writable value, + OutputCollector output, + Reporter reporter) throws IOException { + output.collect((Text)value, null); + } +
    • +
    • 注意输出的文件名和原文件名不同
    • +
  4. +
+
+ +
+应该使用多少个reducer? +

+请参考Hadoop Wiki:Reducer +

+
+ +
+ +如果在Shell脚本里设置一个别名,并放在-mapper之后,Streaming会正常运行吗? +例如,alias cl='cut -fl',-mapper "cl"会运行正常吗? + +

+脚本里无法使用别名,但是允许变量替换,例如: +

+ +$ hadoop dfs -cat samples/student_marks +alice 50 +bruce 70 +charlie 80 +dan 75 + +$ c2='cut -f2'; $HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \ + -input /user/me/samples/student_marks + -mapper \"$c2\" -reducer 'cat' + -output /user/me/samples/student_out + -jobconf mapred.job.name='Experiment' + +$ hadoop dfs -ls samples/student_out +Found 1 items/user/me/samples/student_out/part-00000 <r 3> 16 + +$ hadoop dfs -cat samples/student_out/part-00000 +50 +70 +75 +80 + +
+ +
+ +我可以使用UNIX pipes吗?例如 –mapper "cut –fl | set s/foo/bar/g"管用么? + +

+现在不支持,而且会给出错误信息“java.io.IOException: Broken pipe”。这或许是一个bug,需要进一步研究。 +

+
+ +
+在streaming作业中用-file选项运行一个<strong>分布式的超大可执行文件(例如,3.6G)</strong>时, +我得到了一个错误信息“No space left on device”。如何解决? + +

+配置变量stream.tmpdir指定了一个目录,在这个目录下要进行打jar包的操作。stream.tmpdir的默认值是/tmp,你需要将这个值设置为一个有更大空间的目录: +

+ +-jobconf stream.tmpdir=/export/bigspace/... + +
+ +
+如何设置多个输入目录? +

+可以使用多个-input选项设置多个输入目录: +

+ hadoop jar hadoop-streaming.jar -input '/user/foo/dir1' -input '/user/foo/dir2' + +
+ +
+如何生成gzip格式的输出文件? +

+除了纯文本格式的输出,你还可以生成gzip文件格式的输出,你只需设置streaming作业中的选项‘-jobconf mapred.output.compress=true -jobconf mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCode’。 +

+
+ +
+Streaming中如何自定义input/output format? +

+至少在Hadoop 0.14版本以前,不支持多个jar文件。所以当指定自定义的类时,你要把他们和原有的streaming jar打包在一起,并用这个自定义的jar包替换默认的hadoop streaming jar包。 +

+
+ +
+Streaming如何解析XML文档? +

+你可以使用StreamXmlRecordReader来解析XML文档。 +

+ +hadoop jar hadoop-streaming.jar -inputreader "StreamXmlRecord,begin=BEGIN_STRING,end=END_STRING" ..... (rest of the command) + +

+Map任务会把BEGIN_STRING和END_STRING之间的部分看作一条记录。 +

+
+ +
+在streaming应用程序中如何更新计数器? +

+streaming进程能够使用stderr发出计数器信息。 +reporter:counter:<group>,<counter>,<amount> +应该被发送到stderr来更新计数器。 +

+
+
+如何更新streaming应用程序的状态? +

+streaming进程能够使用stderr发出状态信息。 +reporter:status:<message> 要被发送到stderr来设置状态。 +

+
+ +
+ +
diff --git a/src/docs/cn/src/documentation/content/xdocs/tabs.xml b/src/docs/cn/src/documentation/content/xdocs/tabs.xml new file mode 100644 index 00000000000..83e53efc072 --- /dev/null +++ b/src/docs/cn/src/documentation/content/xdocs/tabs.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/resources/images/architecture.gif b/src/docs/cn/src/documentation/resources/images/architecture.gif new file mode 100644 index 00000000000..8d84a23b07a Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/architecture.gif differ diff --git a/src/docs/cn/src/documentation/resources/images/core-logo.gif b/src/docs/cn/src/documentation/resources/images/core-logo.gif new file mode 100644 index 00000000000..57879bb6dd1 Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/core-logo.gif differ diff --git a/src/docs/cn/src/documentation/resources/images/favicon.ico b/src/docs/cn/src/documentation/resources/images/favicon.ico new file mode 100644 index 00000000000..161bcf7841c Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/favicon.ico differ diff --git a/src/docs/cn/src/documentation/resources/images/hadoop-logo-big.jpg b/src/docs/cn/src/documentation/resources/images/hadoop-logo-big.jpg new file mode 100644 index 00000000000..0c6996cdcb0 Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hadoop-logo-big.jpg differ diff --git a/src/docs/cn/src/documentation/resources/images/hadoop-logo.jpg b/src/docs/cn/src/documentation/resources/images/hadoop-logo.jpg new file mode 100644 index 00000000000..809525d9f15 Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hadoop-logo.jpg differ diff --git a/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.gif b/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.gif new file mode 100644 index 00000000000..7bbe150dacd Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.gif differ diff --git a/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.odg b/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.odg new file mode 100644 index 00000000000..dae62bfa75b Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.odg differ diff --git a/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.png b/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.png new file mode 100644 index 00000000000..70e434c1dc6 Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hdfsarchitecture.png differ diff --git a/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.gif b/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.gif new file mode 100644 index 00000000000..b01f56fe2e5 Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.gif differ diff --git a/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.odg b/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.odg new file mode 100644 index 00000000000..e5d92275719 Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.odg differ diff --git a/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.png b/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.png new file mode 100644 index 00000000000..40ea2ec9d1c Binary files /dev/null and b/src/docs/cn/src/documentation/resources/images/hdfsdatanodes.png differ diff --git a/src/docs/cn/src/documentation/skinconf.xml b/src/docs/cn/src/documentation/skinconf.xml new file mode 100644 index 00000000000..e40b8a09156 --- /dev/null +++ b/src/docs/cn/src/documentation/skinconf.xml @@ -0,0 +1,346 @@ + + + + + + + + + + + + true + + false + + true + + true + + + true + + + true + + + true + + + false + + + true + + + Hadoop + Scalable Computing Platform + http://hadoop.apache.org/core/ + images/core-logo.gif + + + Hadoop + Apache Hadoop + http://hadoop.apache.org/ + images/hadoop-logo.jpg + + + + + + + images/favicon.ico + + + 2009 + The Apache Software Foundation. + http://www.apache.org/licenses/ + + + + + + + + + + + + + + + + + + + p.quote { + margin-left: 2em; + padding: .5em; + background-color: #f0f0f0; + font-family: monospace; + } + + + + + + + + + + + + + + + + + + + + + + + + + 1in + 1in + 1.25in + 1in + + + + false + + + false + + + + + + Built with Apache Forrest + http://forrest.apache.org/ + images/built-with-forrest-button.png + 88 + 31 + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/css/forrest.css.xslt b/src/docs/cn/src/documentation/skins/common/css/forrest.css.xslt new file mode 100644 index 00000000000..fbfa020f60f --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/css/forrest.css.xslt @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + +/* ==================== aural ============================ */ + +@media aural { + h1, h2, h3, h4, h5, h6 { voice-family: paul, male; stress: 20; richness: 90 } + h1 { pitch: x-low; pitch-range: 90 } + h2 { pitch: x-low; pitch-range: 80 } + h3 { pitch: low; pitch-range: 70 } + h4 { pitch: medium; pitch-range: 60 } + h5 { pitch: medium; pitch-range: 50 } + h6 { pitch: medium; pitch-range: 40 } + li, dt, dd { pitch: medium; richness: 60 } + dt { stress: 80 } + pre, code, tt { pitch: medium; pitch-range: 0; stress: 0; richness: 80 } + em { pitch: medium; pitch-range: 60; stress: 60; richness: 50 } + strong { pitch: medium; pitch-range: 60; stress: 90; richness: 90 } + dfn { pitch: high; pitch-range: 60; stress: 60 } + s, strike { richness: 0 } + i { pitch: medium; pitch-range: 60; stress: 60; richness: 50 } + b { pitch: medium; pitch-range: 60; stress: 90; richness: 90 } + u { richness: 0 } + + :link { voice-family: harry, male } + :visited { voice-family: betty, female } + :active { voice-family: betty, female; pitch-range: 80; pitch: x-high } +} + + +a.external { + padding: 0 20px 0px 0px; + display:inline; + background-repeat: no-repeat; + background-position: center right; + background-image: url(images/external-link.gif); +} + + +/* extra-css */ + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/images/README.txt b/src/docs/cn/src/documentation/skins/common/images/README.txt new file mode 100644 index 00000000000..e0932f4a46d --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/images/README.txt @@ -0,0 +1 @@ +The images in this directory are used if the current skin lacks them. diff --git a/src/docs/cn/src/documentation/skins/common/images/add.jpg b/src/docs/cn/src/documentation/skins/common/images/add.jpg new file mode 100644 index 00000000000..06831eeb3dd Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/add.jpg differ diff --git a/src/docs/cn/src/documentation/skins/common/images/built-with-forrest-button.png b/src/docs/cn/src/documentation/skins/common/images/built-with-forrest-button.png new file mode 100644 index 00000000000..4a787abe4dc Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/built-with-forrest-button.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/corner-imports.svg.xslt b/src/docs/cn/src/documentation/skins/common/images/corner-imports.svg.xslt new file mode 100644 index 00000000000..95b58d26f3c --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/images/corner-imports.svg.xslt @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + 0 + + + + + + fill:; + + + fill:; + + + stroke:; + + + + + + + + + 1 + -1 + + + + + 1 + -1 + + + + + 0 + - + + + + + + 0 + - + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/images/dc.svg.xslt b/src/docs/cn/src/documentation/skins/common/images/dc.svg.xslt new file mode 100644 index 00000000000..f900f2f9549 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/images/dc.svg.xslt @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/images/external-link.gif b/src/docs/cn/src/documentation/skins/common/images/external-link.gif new file mode 100644 index 00000000000..ff2f7b2a0ed Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/external-link.gif differ diff --git a/src/docs/cn/src/documentation/skins/common/images/fix.jpg b/src/docs/cn/src/documentation/skins/common/images/fix.jpg new file mode 100644 index 00000000000..1d6820b43b7 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/fix.jpg differ diff --git a/src/docs/cn/src/documentation/skins/common/images/forrest-credit-logo.png b/src/docs/cn/src/documentation/skins/common/images/forrest-credit-logo.png new file mode 100644 index 00000000000..8a63e42038d Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/forrest-credit-logo.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/hack.jpg b/src/docs/cn/src/documentation/skins/common/images/hack.jpg new file mode 100644 index 00000000000..f38d50fe4e8 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/hack.jpg differ diff --git a/src/docs/cn/src/documentation/skins/common/images/pdfdoc.gif b/src/docs/cn/src/documentation/skins/common/images/pdfdoc.gif new file mode 100644 index 00000000000..00dee28aad4 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/pdfdoc.gif differ diff --git a/src/docs/cn/src/documentation/skins/common/images/poddoc.png b/src/docs/cn/src/documentation/skins/common/images/poddoc.png new file mode 100644 index 00000000000..a393df73723 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/poddoc.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/poddoc.svg.xslt b/src/docs/cn/src/documentation/skins/common/images/poddoc.svg.xslt new file mode 100644 index 00000000000..6097d2b357d --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/images/poddoc.svg.xslt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + POD + + + diff --git a/src/docs/cn/src/documentation/skins/common/images/printer.gif b/src/docs/cn/src/documentation/skins/common/images/printer.gif new file mode 100644 index 00000000000..5021187b064 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/printer.gif differ diff --git a/src/docs/cn/src/documentation/skins/common/images/rc.svg.xslt b/src/docs/cn/src/documentation/skins/common/images/rc.svg.xslt new file mode 100644 index 00000000000..d8924fec1b4 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/images/rc.svg.xslt @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/images/remove.jpg b/src/docs/cn/src/documentation/skins/common/images/remove.jpg new file mode 100644 index 00000000000..8c9b9efa8fe Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/remove.jpg differ diff --git a/src/docs/cn/src/documentation/skins/common/images/rss.png b/src/docs/cn/src/documentation/skins/common/images/rss.png new file mode 100644 index 00000000000..f0796ac8862 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/rss.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/spacer.gif b/src/docs/cn/src/documentation/skins/common/images/spacer.gif new file mode 100644 index 00000000000..35d42e808f0 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/spacer.gif differ diff --git a/src/docs/cn/src/documentation/skins/common/images/txtdoc.png b/src/docs/cn/src/documentation/skins/common/images/txtdoc.png new file mode 100644 index 00000000000..bf8b3744ab2 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/txtdoc.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/txtdoc.svg.xslt b/src/docs/cn/src/documentation/skins/common/images/txtdoc.svg.xslt new file mode 100644 index 00000000000..d85715c750d --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/images/txtdoc.svg.xslt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + TXT + + + diff --git a/src/docs/cn/src/documentation/skins/common/images/update.jpg b/src/docs/cn/src/documentation/skins/common/images/update.jpg new file mode 100644 index 00000000000..beb9207336f Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/update.jpg differ diff --git a/src/docs/cn/src/documentation/skins/common/images/valid-html401.png b/src/docs/cn/src/documentation/skins/common/images/valid-html401.png new file mode 100644 index 00000000000..3855210c6c3 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/valid-html401.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/vcss.png b/src/docs/cn/src/documentation/skins/common/images/vcss.png new file mode 100644 index 00000000000..9b2f596e016 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/vcss.png differ diff --git a/src/docs/cn/src/documentation/skins/common/images/xmldoc.gif b/src/docs/cn/src/documentation/skins/common/images/xmldoc.gif new file mode 100644 index 00000000000..ca1224f61f1 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/common/images/xmldoc.gif differ diff --git a/src/docs/cn/src/documentation/skins/common/scripts/breadcrumbs-optimized.js b/src/docs/cn/src/documentation/skins/common/scripts/breadcrumbs-optimized.js new file mode 100644 index 00000000000..507612af872 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/breadcrumbs-optimized.js @@ -0,0 +1,90 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var PREPREND_CRUMBS=new Array(); +var link1="@skinconfig.trail.link1.name@"; +var link2="@skinconfig.trail.link2.name@"; +var link3="@skinconfig.trail.link3.name@"; +if(!(link1=="")&&!link1.indexOf( "@" ) == 0){ + PREPREND_CRUMBS.push( new Array( link1, @skinconfig.trail.link1.href@ ) ); } +if(!(link2=="")&&!link2.indexOf( "@" ) == 0){ + PREPREND_CRUMBS.push( new Array( link2, @skinconfig.trail.link2.href@ ) ); } +if(!(link3=="")&&!link3.indexOf( "@" ) == 0){ + PREPREND_CRUMBS.push( new Array( link3, @skinconfig.trail.link3.href@ ) ); } +var DISPLAY_SEPARATOR=" > "; +var DISPLAY_PREPREND=" > "; +var DISPLAY_POSTPREND=":"; +var CSS_CLASS_CRUMB="breadcrumb"; +var CSS_CLASS_TRAIL="breadcrumbTrail"; +var CSS_CLASS_SEPARATOR="crumbSeparator"; +var FILE_EXTENSIONS=new Array( ".html", ".htm", ".jsp", ".php", ".php3", ".php4" ); +var PATH_SEPARATOR="/"; + +function sc(s) { + var l=s.toLowerCase(); + return l.substr(0,1).toUpperCase()+l.substr(1); +} +function getdirs() { + var t=document.location.pathname.split(PATH_SEPARATOR); + var lc=t[t.length-1]; + for(var i=0;i < FILE_EXTENSIONS.length;i++) + { + if(lc.indexOf(FILE_EXTENSIONS[i])) + return t.slice(1,t.length-1); } + return t.slice(1,t.length); +} +function getcrumbs( d ) +{ + var pre = "/"; + var post = "/"; + var c = new Array(); + if( d != null ) + { + for(var i=0;i < d.length;i++) { + pre+=d[i]+postfix; + c.push(new Array(d[i],pre)); } + } + if(PREPREND_CRUMBS.length > 0 ) + return PREPREND_CRUMBS.concat( c ); + return c; +} +function gettrail( c ) +{ + var h=DISPLAY_PREPREND; + for(var i=0;i < c.length;i++) + { + h+=''+sc(c[i][0])+''; + if(i!=(c.length-1)) + h+=DISPLAY_SEPARATOR; } + return h+DISPLAY_POSTPREND; +} + +function gettrailXHTML( c ) +{ + var h=''+DISPLAY_PREPREND; + for(var i=0;i < c.length;i++) + { + h+=''+sc(c[i][0])+''; + if(i!=(c.length-1)) + h+=''+DISPLAY_SEPARATOR+''; } + return h+DISPLAY_POSTPREND+''; +} + +if(document.location.href.toLowerCase().indexOf("http://")==-1) + document.write(gettrail(getcrumbs())); +else + document.write(gettrail(getcrumbs(getdirs()))); + diff --git a/src/docs/cn/src/documentation/skins/common/scripts/breadcrumbs.js b/src/docs/cn/src/documentation/skins/common/scripts/breadcrumbs.js new file mode 100644 index 00000000000..aea80ec045b --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/breadcrumbs.js @@ -0,0 +1,237 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * This script, when included in a html file, builds a neat breadcrumb trail + * based on its url. That is, if it doesn't contains bugs (I'm relatively + * sure it does). + * + * Typical usage: + * + */ + +/** + * IE 5 on Mac doesn't know Array.push. + * + * Implement it - courtesy to fritz. + */ +var abc = new Array(); +if (!abc.push) { + Array.prototype.push = function(what){this[this.length]=what} +} + +/* ======================================================================== + CONSTANTS + ======================================================================== */ + +/** + * Two-dimensional array containing extra crumbs to place at the front of + * the trail. Specify first the name of the crumb, then the URI that belongs + * to it. You'll need to modify this for every domain or subdomain where + * you use this script (you can leave it as an empty array if you wish) + */ +var PREPREND_CRUMBS = new Array(); + +var link1 = "@skinconfig.trail.link1.name@"; +var link2 = "@skinconfig.trail.link2.name@"; +var link3 = "@skinconfig.trail.link3.name@"; + +var href1 = "@skinconfig.trail.link1.href@"; +var href2 = "@skinconfig.trail.link2.href@"; +var href3 = "@skinconfig.trail.link3.href@"; + + if(!(link1=="")&&!link1.indexOf( "@" ) == 0){ + PREPREND_CRUMBS.push( new Array( link1, href1 ) ); + } + if(!(link2=="")&&!link2.indexOf( "@" ) == 0){ + PREPREND_CRUMBS.push( new Array( link2, href2 ) ); + } + if(!(link3=="")&&!link3.indexOf( "@" ) == 0){ + PREPREND_CRUMBS.push( new Array( link3, href3 ) ); + } + +/** + * String to include between crumbs: + */ +var DISPLAY_SEPARATOR = " > "; +/** + * String to include at the beginning of the trail + */ +var DISPLAY_PREPREND = " > "; +/** + * String to include at the end of the trail + */ +var DISPLAY_POSTPREND = ""; + +/** + * CSS Class to use for a single crumb: + */ +var CSS_CLASS_CRUMB = "breadcrumb"; + +/** + * CSS Class to use for the complete trail: + */ +var CSS_CLASS_TRAIL = "breadcrumbTrail"; + +/** + * CSS Class to use for crumb separator: + */ +var CSS_CLASS_SEPARATOR = "crumbSeparator"; + +/** + * Array of strings containing common file extensions. We use this to + * determine what part of the url to ignore (if it contains one of the + * string specified here, we ignore it). + */ +var FILE_EXTENSIONS = new Array( ".html", ".htm", ".jsp", ".php", ".php3", ".php4" ); + +/** + * String that separates parts of the breadcrumb trail from each other. + * When this is no longer a slash, I'm sure I'll be old and grey. + */ +var PATH_SEPARATOR = "/"; + +/* ======================================================================== + UTILITY FUNCTIONS + ======================================================================== */ +/** + * Capitalize first letter of the provided string and return the modified + * string. + */ +function sentenceCase( string ) +{ return string; + //var lower = string.toLowerCase(); + //return lower.substr(0,1).toUpperCase() + lower.substr(1); +} + +/** + * Returns an array containing the names of all the directories in the + * current document URL + */ +function getDirectoriesInURL() +{ + var trail = document.location.pathname.split( PATH_SEPARATOR ); + + // check whether last section is a file or a directory + var lastcrumb = trail[trail.length-1]; + for( var i = 0; i < FILE_EXTENSIONS.length; i++ ) + { + if( lastcrumb.indexOf( FILE_EXTENSIONS[i] ) ) + { + // it is, remove it and send results + return trail.slice( 1, trail.length-1 ); + } + } + + // it's not; send the trail unmodified + return trail.slice( 1, trail.length ); +} + +/* ======================================================================== + BREADCRUMB FUNCTIONALITY + ======================================================================== */ +/** + * Return a two-dimensional array describing the breadcrumbs based on the + * array of directories passed in. + */ +function getBreadcrumbs( dirs ) +{ + var prefix = "/"; + var postfix = "/"; + + // the array we will return + var crumbs = new Array(); + + if( dirs != null ) + { + for( var i = 0; i < dirs.length; i++ ) + { + prefix += dirs[i] + postfix; + crumbs.push( new Array( dirs[i], prefix ) ); + } + } + + // preprend the PREPREND_CRUMBS + if(PREPREND_CRUMBS.length > 0 ) + { + return PREPREND_CRUMBS.concat( crumbs ); + } + + return crumbs; +} + +/** + * Return a string containing a simple text breadcrumb trail based on the + * two-dimensional array passed in. + */ +function getCrumbTrail( crumbs ) +{ + var xhtml = DISPLAY_PREPREND; + + for( var i = 0; i < crumbs.length; i++ ) + { + xhtml += ''; + xhtml += unescape( crumbs[i][0] ) + ''; + if( i != (crumbs.length-1) ) + { + xhtml += DISPLAY_SEPARATOR; + } + } + + xhtml += DISPLAY_POSTPREND; + + return xhtml; +} + +/** + * Return a string containing an XHTML breadcrumb trail based on the + * two-dimensional array passed in. + */ +function getCrumbTrailXHTML( crumbs ) +{ + var xhtml = ''; + xhtml += DISPLAY_PREPREND; + + for( var i = 0; i < crumbs.length; i++ ) + { + xhtml += ''; + xhtml += unescape( crumbs[i][0] ) + ''; + if( i != (crumbs.length-1) ) + { + xhtml += '' + DISPLAY_SEPARATOR + ''; + } + } + + xhtml += DISPLAY_POSTPREND; + xhtml += ''; + + return xhtml; +} + +/* ======================================================================== + PRINT BREADCRUMB TRAIL + ======================================================================== */ + +// check if we're local; if so, only print the PREPREND_CRUMBS +if( document.location.href.toLowerCase().indexOf( "http://" ) == -1 ) +{ + document.write( getCrumbTrail( getBreadcrumbs() ) ); +} +else +{ + document.write( getCrumbTrail( getBreadcrumbs( getDirectoriesInURL() ) ) ); +} + diff --git a/src/docs/cn/src/documentation/skins/common/scripts/fontsize.js b/src/docs/cn/src/documentation/skins/common/scripts/fontsize.js new file mode 100644 index 00000000000..11722bfc52f --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/fontsize.js @@ -0,0 +1,166 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function init() +{ //embedded in the doc + //ndeSetTextSize(); +} + +function checkBrowser(){ + if (!document.getElementsByTagName){ + return true; + } + else{ + return false; + } +} + + +function ndeSetTextSize(chgsize,rs) +{ + var startSize; + var newSize; + + if (!checkBrowser) + { + return; + } + + startSize = parseInt(ndeGetDocTextSize()); + + if (!startSize) + { + startSize = 16; + } + + switch (chgsize) + { + case 'incr': + newSize = startSize + 2; + break; + + case 'decr': + newSize = startSize - 2; + break; + + case 'reset': + if (rs) {newSize = rs;} else {newSize = 16;} + break; + + default: + try{ + newSize = parseInt(ndeReadCookie("nde-textsize")); + } + catch(e){ + alert(e); + } + + if (!newSize || newSize == 'NaN') + { + newSize = startSize; + } + break; + + } + + if (newSize < 10) + { + newSize = 10; + } + + newSize += 'px'; + + document.getElementsByTagName('html')[0].style.fontSize = newSize; + document.getElementsByTagName('body')[0].style.fontSize = newSize; + + ndeCreateCookie("nde-textsize", newSize, 365); +} + +function ndeGetDocTextSize() +{ + if (!checkBrowser) + { + return 0; + } + + var size = 0; + var body = document.getElementsByTagName('body')[0]; + + if (body.style && body.style.fontSize) + { + size = body.style.fontSize; + } + else if (typeof(getComputedStyle) != 'undefined') + { + size = getComputedStyle(body,'').getPropertyValue('font-size'); + } + else if (body.currentStyle) + { + size = body.currentStyle.fontSize; + } + + //fix IE bug + if( isNaN(size)){ + if(size.substring(size.length-1)=="%"){ + return + } + + } + + return size; + +} + + + +function ndeCreateCookie(name,value,days) +{ + var cookie = name + "=" + value + ";"; + + if (days) + { + var date = new Date(); + date.setTime(date.getTime()+(days*24*60*60*1000)); + cookie += " expires=" + date.toGMTString() + ";"; + } + cookie += " path=/"; + + document.cookie = cookie; + +} + +function ndeReadCookie(name) +{ + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + + + for(var i = 0; i < ca.length; i++) + { + var c = ca[i]; + while (c.charAt(0) == ' ') + { + c = c.substring(1, c.length); + } + + ctest = c.substring(0,name.length); + + if(ctest == name){ + return c.substring(nameEQ.length,c.length); + } + } + return null; +} diff --git a/src/docs/cn/src/documentation/skins/common/scripts/getBlank.js b/src/docs/cn/src/documentation/skins/common/scripts/getBlank.js new file mode 100644 index 00000000000..d9978c0b3e6 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/getBlank.js @@ -0,0 +1,40 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * getBlank script - when included in a html file and called from a form text field, will set the value of this field to "" + * if the text value is still the standard value. + * getPrompt script - when included in a html file and called from a form text field, will set the value of this field to the prompt + * if the text value is empty. + * + * Typical usage: + * + * + */ + diff --git a/src/docs/cn/src/documentation/skins/common/scripts/getMenu.js b/src/docs/cn/src/documentation/skins/common/scripts/getMenu.js new file mode 100644 index 00000000000..b17aad61858 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/getMenu.js @@ -0,0 +1,45 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * This script, when included in a html file, can be used to make collapsible menus + * + * Typical usage: + * + */ + +if (document.getElementById){ + document.write('') +} + + +function SwitchMenu(obj, thePath) +{ +var open = 'url("'+thePath + 'images/chapter_open.gif")'; +var close = 'url("'+thePath + 'images/chapter.gif")'; + if(document.getElementById) { + var el = document.getElementById(obj); + var title = document.getElementById(obj+'Title'); + + if(el.style.display != "block"){ + title.style.backgroundImage = open; + el.style.display = "block"; + }else{ + title.style.backgroundImage = close; + el.style.display = "none"; + } + }// end - if(document.getElementById) +}//end - function SwitchMenu(obj) diff --git a/src/docs/cn/src/documentation/skins/common/scripts/menu.js b/src/docs/cn/src/documentation/skins/common/scripts/menu.js new file mode 100644 index 00000000000..06ea471dc57 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/menu.js @@ -0,0 +1,48 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * This script, when included in a html file, can be used to make collapsible menus + * + * Typical usage: + * + */ + +if (document.getElementById){ + document.write('') +} + +function SwitchMenu(obj) +{ + if(document.getElementById) { + var el = document.getElementById(obj); + var title = document.getElementById(obj+'Title'); + + if(obj.indexOf("_selected_")==0&&el.style.display == ""){ + el.style.display = "block"; + title.className = "pagegroupselected"; + } + + if(el.style.display != "block"){ + el.style.display = "block"; + title.className = "pagegroupopen"; + } + else{ + el.style.display = "none"; + title.className = "pagegroup"; + } + }// end - if(document.getElementById) +}//end - function SwitchMenu(obj) diff --git a/src/docs/cn/src/documentation/skins/common/scripts/prototype.js b/src/docs/cn/src/documentation/skins/common/scripts/prototype.js new file mode 100644 index 00000000000..ed7d920cb5f --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/scripts/prototype.js @@ -0,0 +1,1257 @@ +/* Prototype JavaScript framework, version 1.4.0_pre4 + * (c) 2005 Sam Stephenson + * + * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff + * against the source tree, available from the Prototype darcs repository. + * + * Prototype is freely distributable under the terms of an MIT-style license. + * + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.4.0_pre4', + + emptyFunction: function() {}, + K: function(x) {return x} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (property in source) { + destination[property] = source[property]; + } + return destination; +} + +Function.prototype.bind = function(object) { + var __method = this; + return function() { + return __method.apply(object, arguments); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + return __method.call(object, event || window.event); + } +} + +Number.prototype.toColorPart = function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; +} + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} + +/*--------------------------------------------------------------------------*/ + +function $() { + var elements = new Array(); + + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + + if (arguments.length == 1) + return element; + + elements.push(element); + } + + return elements; +} + +if (!Array.prototype.push) { + Array.prototype.push = function() { + var startLength = this.length; + for (var i = 0; i < arguments.length; i++) + this[startLength + i] = arguments[i]; + return this.length; + } +} + +if (!Function.prototype.apply) { + // Based on code from http://www.youngpup.net/ + Function.prototype.apply = function(object, parameters) { + var parameterStrings = new Array(); + if (!object) object = window; + if (!parameters) parameters = new Array(); + + for (var i = 0; i < parameters.length; i++) + parameterStrings[i] = 'parameters[' + i + ']'; + + object.__apply__ = this; + var result = eval('object.__apply__(' + + parameterStrings.join(', ') + ')'); + object.__apply__ = null; + + return result; + } +} + +Object.extend(String.prototype, { + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0].nodeValue; + }, + + parseQuery: function() { + var str = this; + if (str.substring(0,1) == '?') { + str = this.substring(1); + } + var result = {}; + var pairs = str.split('&'); + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i].split('='); + result[pair[0]] = pair[1]; + } + return result; + } +}); + + +var _break = new Object(); +var _continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != _continue) throw e; + } + }); + } catch (e) { + if (e != _break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + if (!(result &= (iterator || Prototype.K)(value, index))) + throw _break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result &= (iterator || Prototype.K)(value, index)) + throw _break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw _break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw _break; + } + }); + return found; + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.collect(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value >= (result || value)) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value <= (result || value)) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.collect(Prototype.K); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + iterator(value = collections.pluck(index)); + return value; + }); + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); + +$A = Array.from = function(iterable) { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; +} + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + } +}); + +Object.extend(Array.prototype, Enumerable); + + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')}, + function() {return new XMLHttpRequest()} + ) || false; + } +} + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + parameters: '' + } + Object.extend(this.options, options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + if (this.options.method == 'get') + url += '?' + parameters; + + this.transport.open(this.options.method, url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version]; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', + 'application/x-www-form-urlencoded'); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + + if (event == 'Complete') + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(this.transport); + + (this.options['on' + event] || Prototype.emptyFunction)(this.transport); + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + } +}); + +Ajax.Updater = Class.create(); +Ajax.Updater.ScriptFragment = '(?:)((\n|.)*?)(?:<\/script>)'; + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function() { + this.updateContent(); + onComplete(this.transport); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + + var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); + var response = this.transport.responseText.replace(match, ''); + var scripts = this.transport.responseText.match(match); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + receiver.innerHTML = response; + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout((function() {this.onComplete( + this.transport)}).bind(this), 10); + } + + if (this.options.evalScripts && scripts) { + match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); + setTimeout((function() { + for (var i = 0; i < scripts.length; i++) + eval(scripts[i].match(match)[1]); + }).bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = 1; + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Ajax.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); + +document.getElementsByClassName = function(className) { + var children = document.getElementsByTagName('*') || document.all; + var elements = new Array(); + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + var classNames = child.className.split(' '); + for (var j = 0; j < classNames.length; j++) { + if (classNames[j] == className) { + elements.push(child); + break; + } + } + } + + return elements; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) { + var Element = new Object(); +} + +Object.extend(Element, { + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = + (element.style.display == 'none' ? '' : 'none'); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + hasClassName: function(element, className) { + element = $(element); + if (!element) + return; + var a = element.className.split(' '); + for (var i = 0; i < a.length; i++) { + if (a[i] == className) + return true; + } + return false; + }, + + addClassName: function(element, className) { + element = $(element); + Element.removeClassName(element, className); + element.className += ' ' + className; + }, + + removeClassName: function(element, className) { + element = $(element); + if (!element) + return; + var newClassName = ''; + var a = element.className.split(' '); + for (var i = 0; i < a.length; i++) { + if (a[i] != className) { + if (i > 0) + newClassName += ' '; + newClassName += a[i]; + } + } + element.className = newClassName; + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + var element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + } +}); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content; + + if (this.adjacency && this.element.insertAdjacentHTML) { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.fragment = this.range.createContextualFragment(this.content); + this.insertContent(); + } + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function() { + this.element.parentNode.insertBefore(this.fragment, this.element); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function() { + this.element.insertBefore(this.fragment, this.element.firstChild); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function() { + this.element.appendChild(this.fragment); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function() { + this.element.parentNode.insertBefore(this.fragment, + this.element.nextSibling); + } +}); + +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + $(element).focus(); + $(element).select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + var form = $(form); + var elements = new Array(); + + for (tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + var form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + focusFirstElement: function(form) { + var form = $(form); + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + if (element.type != 'hidden' && !element.disabled) { + Field.activate(element); + break; + } + } + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + var element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return encodeURIComponent(parameter[0]) + '=' + + encodeURIComponent(parameter[1]); + }, + + getValue: function(element) { + var element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + var value = ''; + if (element.type == 'select-one') { + var index = element.selectedIndex; + if (index >= 0) + value = element.options[index].value || element.options[index].text; + } else { + value = new Array(); + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) + value.push(opt.value || opt.text); + } + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + element.target = this; + element.prev_onclick = element.onclick || Prototype.emptyFunction; + element.onclick = function() { + this.prev_onclick(); + this.target.onElementEvent(); + } + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + element.target = this; + element.prev_onchange = element.onchange || Prototype.emptyFunction; + element.onchange = function() { + this.prev_onchange(); + this.target.onElementEvent(); + } + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + + +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +Event.observe(window, 'unload', Event.unloadCache, false); + +var Position = { + + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + } +} diff --git a/src/docs/cn/src/documentation/skins/common/skinconf.xsl b/src/docs/cn/src/documentation/skins/common/skinconf.xsl new file mode 100644 index 00000000000..5d1132086f1 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/skinconf.xsl @@ -0,0 +1,238 @@ + + + + + + + true + + + true + + + true + + + true + + + true + + + false + + + false + + + true + + + .at. + + + true + + + + + + + + + + + + + + + Page 1 + + + + + true + + + + + + + + + + Built with Apache Forrest + http://forrest.apache.org/ + images/built-with-forrest-button.png + 88 + 31 + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_de.xml b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_de.xml new file mode 100644 index 00000000000..bc461196e64 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_de.xml @@ -0,0 +1,23 @@ + + + + Schriftgrsse: + Zuletzt verffentlicht: + Suche: + Suche auf der Seite mit + diff --git a/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_en_US.xml b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_en_US.xml new file mode 100644 index 00000000000..88dfe143118 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_en_US.xml @@ -0,0 +1,23 @@ + + + + Font size: + Last Published: + Search + Search site with + diff --git a/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_es.xml b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_es.xml new file mode 100644 index 00000000000..63be6712b30 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_es.xml @@ -0,0 +1,23 @@ + + + + Tamao del texto: + Fecha de publicacin: + Buscar + Buscar en + diff --git a/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_fr.xml b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_fr.xml new file mode 100644 index 00000000000..622569a4ffa --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/translations/CommonMessages_fr.xml @@ -0,0 +1,23 @@ + + + + Taille : + Dernire publication : + Rechercher + Rechercher sur le site avec + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/fo/d2fo.xml b/src/docs/cn/src/documentation/skins/common/xslt/fo/d2fo.xml new file mode 100644 index 00000000000..7b7a5fe9bfd --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/fo/d2fo.xml @@ -0,0 +1,1015 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 841mm + 594mm + 420mm + 297mm + 210mm + 148mm + 7.25in + 8.5in + 11in + 8.5in + 8.5in + 8.5in + 11in + 8.5in + + + + + 1189mm + 841mm + 594mm + 420mm + 297mm + 210mm + 10.5in + 13in + 17in + 14in + 10.83in + 17in + 11in + + + + + + + + + + 1189mm + 841mm + 594mm + 420mm + 297mm + 210mm + 10.5in + 13in + 17in + 14in + 10.83in + 17in + 11in + + + + + 841mm + 594mm + 420mm + 297mm + 210mm + 148mm + 7.25in + 8.5in + 11in + 8.5in + 8.5in + 8.5in + 11in + 8.5in + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + start + + + + + + + + + + + + + + + end + + + + + + + + + + + + + + start + + + + + + + + + + + + + + + + + + + + + + + + + NOTICE: + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + +. + + + + + +pt + + + + + + +   + + + + + + + + + + + + 0 + + + + + + + + + + + by + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6pt" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Warning: + + + + + + + + + + + + + + + + + Note: + + + + + + + + + + + + + FIXME (): + + + + + + + + + + + + + + + + + + + () + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in + + + + + + + + + + + + + + + + + + + + Table + + +: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Table of contents + + + + page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [] + + + + + + + + + + + + page + + + + page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/fo/document-to-fo.xsl b/src/docs/cn/src/documentation/skins/common/xslt/fo/document-to-fo.xsl new file mode 100644 index 00000000000..bc00518f247 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/fo/document-to-fo.xsl @@ -0,0 +1,1025 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 841mm + 594mm + 420mm + 297mm + 210mm + 148mm + 7.25in + 8.5in + 11in + 8.5in + 8.5in + 8.5in + 11in + 8.5in + + + + + 1189mm + 841mm + 594mm + 420mm + 297mm + 210mm + 10.5in + 13in + 17in + 14in + 10.83in + 17in + 11in + + + + + + + + + + 1189mm + 841mm + 594mm + 420mm + 297mm + 210mm + 10.5in + 13in + 17in + 14in + 10.83in + 17in + 11in + + + + + 841mm + 594mm + 420mm + 297mm + 210mm + 148mm + 7.25in + 8.5in + 11in + 8.5in + 8.5in + 8.5in + 11in + 8.5in + + + + + + kkkkkkkkkkkkkkkkkkkkkk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + start + + + + + + + + + + + + + + + end + + + + + + + + + + + + + + start + + + + + + + + + + + + + + + + + + + + + + + + + NOTICE: + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + +. + + + + + +pt + + + + + + +   + + + + + + + + + + + + 0 + + + + + + + + + + + by + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6pt" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6pt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Warning: + + + + + + + + + + + + + + + + + Note: + + + + + + + + + + + + + FIXME (): + + + + + + + + + + + + + + + + + + + () + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + in + + + + + + + + + + + + + + + + + + + + Table + + +: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +目录 + + + + page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [] + + + + + + + + + + + + page + + + + page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/fo/footerinfo.xsl b/src/docs/cn/src/documentation/skins/common/xslt/fo/footerinfo.xsl new file mode 100644 index 00000000000..c5ba5c9460e --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/fo/footerinfo.xsl @@ -0,0 +1,70 @@ + + + + + + + + + + + + +Copyright © +   + All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/fo/pdfoutline.xsl b/src/docs/cn/src/documentation/skins/common/xslt/fo/pdfoutline.xsl new file mode 100644 index 00000000000..acdc6ac9cf1 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/fo/pdfoutline.xsl @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/book-to-menu.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/book-to-menu.xsl new file mode 100644 index 00000000000..228b57e5c4d --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/book-to-menu.xsl @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • + + + + + + + + +
  • +
    + + + + + +
    diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/document-to-html.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/document-to-html.xsl new file mode 100644 index 00000000000..6e1dcc607e6 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/document-to-html.xsl @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +

    + +

    +
    +
    + +

    + +

    +
    + + + + +
    + + +; + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + ^ + + + + + + + +15 + + +0 + + + +
    + +
    + + + +
    + + + + + +
    + + + + + + Note + Warning + Fixme () + +
    +
    + +
    +
    +
    + +
    + +Notice: + +
    +
    + + + + _top + + + _blank + + + + + + + + + + + + + + +
    + + +
    +
    + + +
    +
    +      
    +      
    +
    +    
    +
    + + + + + + + + + + + + + + codefrag + + + + + + + +
    + + + + +
    +
    + + + + + + + + +
    +
    + + + + + + + +
    + +
    + + + + + + + + + by + , + + + + + + + + + + + + + + version + + + + + + + + + v + + + + + + + + +

    + +Type: + +

    +
    + +

    + +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/dotdots.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/dotdots.xsl new file mode 100644 index 00000000000..2ff9d1f3266 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/dotdots.xsl @@ -0,0 +1,73 @@ + + + + + + + + + + +../ + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/pathutils.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/pathutils.xsl new file mode 100644 index 00000000000..c1639a4b4f2 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/pathutils.xsl @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/renderlogo.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/renderlogo.xsl new file mode 100644 index 00000000000..698156b9ba2 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/renderlogo.xsl @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + {$name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/site-to-xhtml.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/site-to-xhtml.xsl new file mode 100644 index 00000000000..2f8b15cd399 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/site-to-xhtml.xsl @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + > + + + > + + + + + + + + + + + + + + + + + + + + + + + + Valid HTML 4.01! + + + + + + + Valid HTML 4.01! + Valid CSS! + + + + + + + PDF +
    + PDF
    + +
    +
    + + + + + TXT +
    + TXT
    + +
    +
    + + + + + POD +
    + POD
    + +
    +
    + + + + + XML +
    + XML
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + +
    • + + + + + +
    • +
      +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    +
    + + + + + + + + + + 2005 + yyyy + - + + + + + + + + + + + + +
    diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/split.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/split.xsl new file mode 100644 index 00000000000..14c88e72cb4 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/split.xsl @@ -0,0 +1,124 @@ + + + + + + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/strip_namespaces.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/strip_namespaces.xsl new file mode 100644 index 00000000000..47dec6a1788 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/strip_namespaces.xsl @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/tab-to-menu.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/tab-to-menu.xsl new file mode 100644 index 00000000000..257d2919f70 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/tab-to-menu.xsl @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + | + + + + + + + + | + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/src/docs/cn/src/documentation/skins/common/xslt/html/tabutils.xsl b/src/docs/cn/src/documentation/skins/common/xslt/html/tabutils.xsl new file mode 100644 index 00000000000..0caaaf4c81f --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/html/tabutils.xsl @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + +/ + + + +/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/common/xslt/svg/document-to-svg.xsl b/src/docs/cn/src/documentation/skins/common/xslt/svg/document-to-svg.xsl new file mode 100644 index 00000000000..08ca5ba1b7a --- /dev/null +++ b/src/docs/cn/src/documentation/skins/common/xslt/svg/document-to-svg.xsl @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/pelt/css/basic.css b/src/docs/cn/src/documentation/skins/pelt/css/basic.css new file mode 100644 index 00000000000..4ed58b99ae7 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/css/basic.css @@ -0,0 +1,166 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** + * General + */ + +img { border: 0; } + +#content table { + border: 0; + width: 100%; +} +/*Hack to get IE to render the table at 100%*/ +* html #content table { margin-left: -3px; } + +#content th, +#content td { + margin: 0; + padding: 0; + vertical-align: top; +} + +.clearboth { + clear: both; +} + +.note, .warning, .fixme { + border: solid black 1px; + margin: 1em 3em; +} + +.note .label { + background: #369; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.note .content { + background: #F0F0FF; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.warning .label { + background: #C00; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.warning .content { + background: #FFF0F0; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.fixme .label { + background: #C6C600; + color: black; + font-weight: bold; + padding: 5px 10px; +} +.fixme .content { + padding: 5px 10px; +} + +/** + * Typography + */ + +body { + font-family: verdana, "Trebuchet MS", arial, helvetica, sans-serif; + font-size: 100%; +} + +#content { + font-family: Georgia, Palatino, Times, serif; + font-size: 95%; +} +#tabs { + font-size: 70%; +} +#menu { + font-size: 80%; +} +#footer { + font-size: 70%; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", verdana, arial, helvetica, sans-serif; + font-weight: bold; + margin-top: 1em; + margin-bottom: .5em; +} + +h1 { + margin-top: 0; + margin-bottom: 1em; + font-size: 1.4em; +} +#content h1 { + font-size: 160%; + margin-bottom: .5em; +} +#menu h1 { + margin: 0; + padding: 10px; + background: #336699; + color: white; +} +h2 { font-size: 120%; } +h3 { font-size: 100%; } +h4 { font-size: 90%; } +h5 { font-size: 80%; } +h6 { font-size: 75%; } + +p { + line-height: 120%; + text-align: left; + margin-top: .5em; + margin-bottom: 1em; +} + +#content li, +#content th, +#content td, +#content li ul, +#content li ol{ + margin-top: .5em; + margin-bottom: .5em; +} + + +#content li li, +#minitoc-area li{ + margin-top: 0em; + margin-bottom: 0em; +} + +#content .attribution { + text-align: right; + font-style: italic; + font-size: 85%; + margin-top: 1em; +} + +.codefrag { + font-family: "Courier New", Courier, monospace; + font-size: 110%; +} diff --git a/src/docs/cn/src/documentation/skins/pelt/css/print.css b/src/docs/cn/src/documentation/skins/pelt/css/print.css new file mode 100644 index 00000000000..8916b9fc01e --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/css/print.css @@ -0,0 +1,54 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +body { + font-family: Georgia, Palatino, serif; + font-size: 12pt; + background: white; +} + +#tabs, +#menu, +#content .toc { + display: none; +} + +#content { + width: auto; + padding: 0; + float: none !important; + color: black; + background: inherit; +} + +a:link, a:visited { + color: #336699; + background: inherit; + text-decoration: underline; +} + +#top .logo { + padding: 0; + margin: 0 0 2em 0; +} + +#footer { + margin-top: 4em; +} + +acronym { + border: 0; +} diff --git a/src/docs/cn/src/documentation/skins/pelt/css/profile.css.xslt b/src/docs/cn/src/documentation/skins/pelt/css/profile.css.xslt new file mode 100644 index 00000000000..a1dcb23e49c --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/css/profile.css.xslt @@ -0,0 +1,182 @@ + + + + + + + + +#top { background-color: ;} + + +#top .header .current { background-color: ;} +#top .header .current a:link { color: ; } +#top .header .current a:visited { color: ; } +#top .header .current a:hover { color: ; } + + +#tabs li { background-color: ;} +#tabs li a:link { color: ; } +#tabs li a:visited { color: ; } +#tabs li a:hover { color: ; } + + +#level2tabs a.selected { background-color: ;} +#level2tabs a:link { color: ; } +#level2tabs a:visited { color: ; } +#level2tabs a:hover { color: ; } + + +#level2tabs { background-color: ;} +#level2tabs a.unselected:link { color: ; } +#level2tabs a.unselected:visited { color: ; } +#level2tabs a.unselected:hover { color: ; } + + +.heading { background-color: ;} + + +.boxed { background-color: ;} +.underlined_5 {border-bottom: solid 5px ;} +.underlined_10 {border-bottom: solid 10px ;} +table caption { +background-color: ; +color: ; +} + + +#feedback { +color: ; +background: ; +text-align: ; +} +#feedback #feedbackto { +color: ; +} + + +#main .breadtrail { +background: ; +color: ; +} +#main .breadtrail a:link { color: ; } +#main .breadtrail a:visited { color: ; } +#main .breadtrail a:hover { color: ; } +#top .breadtrail { +background: ; +color: ; +} +#top .breadtrail a:link { color: ; } +#top .breadtrail a:visited { color: ; } +#top .breadtrail a:hover { color: ; } + + + +#publishedStrip { +color: ; +background: ; +} + + + +#publishedStrip { +color: ; +background: ; +} + + +#menu .menupagetitle { background-color: ; + color: ;} + + +#menu { border-color: ;} +#menu .menupagetitle { border-color: ;} +#menu .menupageitemgroup { border-color: ;} + + +#menu { background-color: ;} +#menu { color: ;} +#menu a:link { color: ;} +#menu a:visited { color: ;} +#menu a:hover { +background-color: ; +color: ;} + + +#menu .menupageitemgroup { +background-color: ; +} +#menu .menupageitem { +color: ; +} +#menu .menupageitem a:link { color: ;} +#menu .menupageitem a:visited { color: ;} +#menu .menupageitem a:hover { +background-color: ; +color: ; +} + + +#menu h1 { +color: ; +background-color: ; +} + + +#top .searchbox { +background-color: ; +color: ; +} + + +body{ +background-color: ; +color: ; +} +a:link { color:} +a:visited { color:} +a:hover { color:} + + + +#footer { background-color: ;} + + + +.highlight { background-color: ;} + + +.fixme { border-color: ;} + + +.note { border-color: ;} + + +.warning { border-color: ;} + + +.code { border-color: ;} + + +.ForrestTable { background-color: ;} + + +.ForrestTable td { background-color: ;} + + diff --git a/src/docs/cn/src/documentation/skins/pelt/css/screen.css b/src/docs/cn/src/documentation/skins/pelt/css/screen.css new file mode 100644 index 00000000000..aa8c457cb30 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/css/screen.css @@ -0,0 +1,587 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +body { margin: 0px 0px 0px 0px; font-family: Verdana, Helvetica, sans-serif; } + +h1 { font-size : 160%; margin: 0px 0px 0px 0px; padding: 0px; } +h2 { font-size : 140%; margin: 1em 0px 0.8em 0px; padding: 0px; font-weight : bold;} +h3 { font-size : 130%; margin: 0.8em 0px 0px 0px; padding: 0px; font-weight : bold; } +.h3 { margin: 22px 0px 3px 0px; } +h4 { font-size : 120%; margin: 0.7em 0px 0px 0px; padding: 0px; font-weight : normal; text-align: left; } +.h4 { margin: 18px 0px 0px 0px; } +h4.faq { font-size : 120%; margin: 18px 0px 0px 0px; padding: 0px; font-weight : bold; text-align: left; } +h5 { font-size : 100%; margin: 14px 0px 0px 0px; padding: 0px; font-weight : normal; text-align: left; } + +/** +* table +*/ +table .title { background-color: #000000; } +.ForrestTable { + color: #ffffff; + background-color: #7099C5; + width: 100%; + font-size : 100%; + empty-cells: show; +} +table caption { + padding-left: 5px; + color: white; + text-align: left; + font-weight: bold; + background-color: #000000; +} +.ForrestTable td { + color: black; + background-color: #f0f0ff; +} +.ForrestTable th { text-align: center; } +/** + * Page Header + */ + +#top { + position: relative; + float: left; + width: 100%; + background: #294563; /* if you want a background in the header, put it here */ +} + +#top .breadtrail { + background: #CFDCED; + color: black; + border-bottom: solid 1px white; + padding: 3px 10px; + font-size: 75%; +} +#top .breadtrail a { color: black; } + +#top .header { + float: left; + width: 100%; + background: url("images/header_white_line.gif") repeat-x bottom; +} + +#top .grouplogo { + padding: 7px 0 10px 10px; + float: left; + text-align: left; +} +#top .projectlogo { + padding: 7px 0 10px 10px; + float: left; + width: 33%; + text-align: right; +} +#top .projectlogoA1 { + padding: 7px 0 10px 10px; + float: right; +} +html>body #top .searchbox { + bottom: 0px; +} +#top .searchbox { + position: absolute; + right: 10px; + height: 42px; + font-size: 70%; + white-space: nowrap; + text-align: right; + color: white; + background-color: #000000; + z-index:0; + background-image: url(images/rc-t-l-5-1header-2searchbox-3searchbox.png); + background-repeat: no-repeat; + background-position: top left; + bottom: -1px; /* compensate for IE rendering issue */ +} + +#top .searchbox form { + padding: 5px 10px; + margin: 0; +} +#top .searchbox p { + padding: 0 0 2px 0; + margin: 0; +} +#top .searchbox input { + font-size: 100%; +} + +#tabs { + clear: both; + padding-left: 10px; + margin: 0; + list-style: none; +} +/* background: #CFDCED url("images/tab-right.gif") no-repeat right top;*/ +#tabs li { + float: left; + background-image: url(images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png); + background-repeat: no-repeat; + background-position: top right; + background-color: #000000; + margin: 0 3px 0 0; + padding: 0; +} + +/*background: url("images/tab-left.gif") no-repeat left top;*/ +#tabs li a { + float: left; + display: block; + font-family: verdana, arial, sans-serif; + text-decoration: none; + color: black; + white-space: nowrap; + background-image: url(images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png); + background-repeat: no-repeat; + background-position: top left; + padding: 5px 15px 4px; + width: .1em; /* IE/Win fix */ +} + +#tabs li a:hover { + + cursor: pointer; + text-decoration:underline; +} + +#tabs > li a { width: auto; } /* Rest of IE/Win fix */ + +/* Commented Backslash Hack hides rule from IE5-Mac \*/ +#tabs a { float: none; } +/* End IE5-Mac hack */ + +#top .header .current { + background-color: #4C6C8F; + background-image: url(images/rc-t-r-5-1header-2tab-selected-3tab-selected.png); + background-repeat: no-repeat; + background-position: top right; +} +#top .header .current a { + font-weight: bold; + padding-bottom: 5px; + color: white; + background-image: url(images/rc-t-l-5-1header-2tab-selected-3tab-selected.png); + background-repeat: no-repeat; + background-position: top left; +} +#publishedStrip { + padding-right: 10px; + padding-left: 20px; + padding-top: 3px; + padding-bottom:3px; + color: #ffffff; + font-size : 60%; + font-weight: bold; + background-color: #4C6C8F; + text-align:right; +} + +#level2tabs { +margin: 0; +float:left; +position:relative; + +} + + + +#level2tabs a:hover { + + cursor: pointer; + text-decoration:underline; + +} + +#level2tabs a{ + + cursor: pointer; + text-decoration:none; + background-image: url('images/chapter.gif'); + background-repeat: no-repeat; + background-position: center left; + padding-left: 6px; + margin-left: 6px; +} + +/* +* border-top: solid #4C6C8F 15px; +*/ +#main { + position: relative; + background: white; + clear:both; +} +#main .breadtrail { + clear:both; + position: relative; + background: #CFDCED; + color: black; + border-bottom: solid 1px black; + border-top: solid 1px black; + padding: 0px 180px; + font-size: 75%; + z-index:10; +} +/** +* Round corner +*/ +#roundtop { + background-image: url(images/rc-t-r-15-1body-2menu-3menu.png); + background-repeat: no-repeat; + background-position: top right; +} + +#roundbottom { + background-image: url(images/rc-b-r-15-1body-2menu-3menu.png); + background-repeat: no-repeat; + background-position: top right; +} + +img.corner { + width: 15px; + height: 15px; + border: none; + display: block !important; +} + +.roundtopsmall { + background-image: url(images/rc-t-r-5-1header-2searchbox-3searchbox.png); + background-repeat: no-repeat; + background-position: top right; +} + +#roundbottomsmall { + background-image: url(images/rc-b-r-5-1header-2tab-selected-3tab-selected.png); + background-repeat: no-repeat; + background-position: top right; +} + +img.cornersmall { + width: 5px; + height: 5px; + border: none; + display: block !important; +} +/** + * Side menu + */ +#menu a { font-weight: normal; text-decoration: none;} +#menu a:visited { font-weight: normal; } +#menu a:active { font-weight: normal; } +#menu a:hover { font-weight: normal; text-decoration:underline;} + +#menuarea { width:10em;} +#menu { + position: relative; + float: left; + width: 160px; + padding-top: 0px; + top:-18px; + left:10px; + z-index: 20; + background-color: #f90; + font-size : 70%; + +} + +.menutitle { + cursor:pointer; + padding: 3px 12px; + margin-left: 10px; + background-image: url('images/chapter.gif'); + background-repeat: no-repeat; + background-position: center left; + font-weight : bold; + + +} + +.menutitle:hover{text-decoration:underline;cursor: pointer;} + +#menu .menuitemgroup { + margin: 0px 0px 6px 8px; + padding: 0px; + font-weight : bold; } + +#menu .selectedmenuitemgroup{ + margin: 0px 0px 0px 8px; + padding: 0px; + font-weight : normal; + + } + +#menu .menuitem { + padding: 2px 0px 1px 13px; + background-image: url('images/page.gif'); + background-repeat: no-repeat; + background-position: center left; + font-weight : normal; + margin-left: 10px; +} + +#menu .menupage { + margin: 2px 0px 1px 10px; + padding: 0px 3px 0px 12px; + background-image: url('images/page.gif'); + background-repeat: no-repeat; + background-position: center left; + font-style : normal; +} +#menu .menupagetitle { + padding: 0px 0px 0px 1px; + font-style : normal; + border-style: solid; + border-width: 1px; + margin-right: 10px; + +} +#menu .menupageitemgroup { + padding: 3px 0px 4px 6px; + font-style : normal; + border-bottom: 1px solid ; + border-left: 1px solid ; + border-right: 1px solid ; + margin-right: 10px; +} +#menu .menupageitem { + font-style : normal; + font-weight : normal; + border-width: 0px; + font-size : 90%; +} +#menu #credit { + text-align: center; +} +#menu #credit2 { + text-align: center; + padding: 3px 3px 3px 3px; + background-color: #ffffff; +} +#menu .searchbox { + text-align: center; +} +#menu .searchbox form { + padding: 3px 3px; + margin: 0; +} +#menu .searchbox input { + font-size: 100%; +} + +#content { + padding: 20px 20px 20px 180px; + margin: 0; + font : small Verdana, Helvetica, sans-serif; + font-size : 80%; +} + +#content ul { + margin: 0; + padding: 0 25px; +} +#content li { + padding: 0 5px; +} +#feedback { + color: black; + background: #CFDCED; + text-align:center; + margin-top: 5px; +} +#feedback #feedbackto { + font-size: 90%; + color: black; +} +#footer { + clear: both; + position: relative; /* IE bugfix (http://www.dracos.co.uk/web/css/ie6floatbug/) */ + width: 100%; + background: #CFDCED; + border-top: solid 1px #4C6C8F; + color: black; +} +#footer .copyright { + position: relative; /* IE bugfix cont'd */ + padding: 5px; + margin: 0; + width: 45%; +} +#footer .lastmodified { + position: relative; /* IE bugfix cont'd */ + float: right; + width: 45%; + padding: 5px; + margin: 0; + text-align: right; +} +#footer a { color: white; } + +#footer #logos { + text-align: left; +} + + +/** + * Misc Styles + */ + +acronym { cursor: help; } +.boxed { background-color: #a5b6c6;} +.underlined_5 {border-bottom: solid 5px #4C6C8F;} +.underlined_10 {border-bottom: solid 10px #4C6C8F;} +/* ==================== snail trail ============================ */ + +.trail { + position: relative; /* IE bugfix cont'd */ + font-size: 70%; + text-align: right; + float: right; + margin: -10px 5px 0px 5px; + padding: 0; +} + +#motd-area { + position: relative; /* IE bugfix cont'd */ + float: right; + width: 35%; + background-color: #f0f0ff; + border-top: solid 1px #4C6C8F; + border-bottom: solid 1px #4C6C8F; + margin-bottom: 15px; + margin-left: 15px; + margin-right: 10%; + padding-bottom: 5px; + padding-top: 5px; +} + +#minitoc-area { + border-top: solid 1px #4C6C8F; + border-bottom: solid 1px #4C6C8F; + margin: 15px 10% 5px 15px; + /* margin-bottom: 15px; + margin-left: 15px; + margin-right: 10%;*/ + padding-bottom: 7px; + padding-top: 5px; +} +.minitoc { + list-style-image: url('images/current.gif'); + font-weight: normal; +} + +li p { + margin: 0; + padding: 0; +} + +.pdflink { + position: relative; /* IE bugfix cont'd */ + float: right; + margin: 0px 5px; + padding: 0; +} +.pdflink br { + margin-top: -10px; + padding-left: 1px; +} +.pdflink a { + display: block; + font-size: 70%; + text-align: center; + margin: 0; + padding: 0; +} + +.pdflink img { + display: block; + height: 16px; + width: 16px; +} +.xmllink { + position: relative; /* IE bugfix cont'd */ + float: right; + margin: 0px 5px; + padding: 0; +} +.xmllink br { + margin-top: -10px; + padding-left: 1px; +} +.xmllink a { + display: block; + font-size: 70%; + text-align: center; + margin: 0; + padding: 0; +} + +.xmllink img { + display: block; + height: 16px; + width: 16px; +} +.podlink { + position: relative; /* IE bugfix cont'd */ + float: right; + margin: 0px 5px; + padding: 0; +} +.podlink br { + margin-top: -10px; + padding-left: 1px; +} +.podlink a { + display: block; + font-size: 70%; + text-align: center; + margin: 0; + padding: 0; +} + +.podlink img { + display: block; + height: 16px; + width: 16px; +} + +.printlink { + position: relative; /* IE bugfix cont'd */ + float: right; +} +.printlink br { + margin-top: -10px; + padding-left: 1px; +} +.printlink a { + display: block; + font-size: 70%; + text-align: center; + margin: 0; + padding: 0; +} +.printlink img { + display: block; + height: 16px; + width: 16px; +} + +p.instruction { + display: list-item; + list-style-image: url('../images/instruction_arrow.png'); + list-style-position: outside; + margin-left: 2em; +} diff --git a/src/docs/cn/src/documentation/skins/pelt/images/chapter.gif b/src/docs/cn/src/documentation/skins/pelt/images/chapter.gif new file mode 100644 index 00000000000..d3d8245d0c7 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/chapter.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/chapter_open.gif b/src/docs/cn/src/documentation/skins/pelt/images/chapter_open.gif new file mode 100644 index 00000000000..eecce18b50a Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/chapter_open.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/current.gif b/src/docs/cn/src/documentation/skins/pelt/images/current.gif new file mode 100644 index 00000000000..fd82c082012 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/current.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/error.png b/src/docs/cn/src/documentation/skins/pelt/images/error.png new file mode 100644 index 00000000000..b4fe06e3709 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/error.png differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/header_white_line.gif b/src/docs/cn/src/documentation/skins/pelt/images/header_white_line.gif new file mode 100644 index 00000000000..369cae8dcf2 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/header_white_line.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/info.png b/src/docs/cn/src/documentation/skins/pelt/images/info.png new file mode 100644 index 00000000000..2e53447e8c2 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/info.png differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/instruction_arrow.png b/src/docs/cn/src/documentation/skins/pelt/images/instruction_arrow.png new file mode 100644 index 00000000000..0fbc72452bf Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/instruction_arrow.png differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/label.gif b/src/docs/cn/src/documentation/skins/pelt/images/label.gif new file mode 100644 index 00000000000..c83a3893c55 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/label.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/page.gif b/src/docs/cn/src/documentation/skins/pelt/images/page.gif new file mode 100644 index 00000000000..a144d3295be Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/page.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/pdfdoc.gif b/src/docs/cn/src/documentation/skins/pelt/images/pdfdoc.gif new file mode 100644 index 00000000000..ec13eb52fb1 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/pdfdoc.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/printer.gif b/src/docs/cn/src/documentation/skins/pelt/images/printer.gif new file mode 100644 index 00000000000..a8d0d419c90 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/printer.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/success.png b/src/docs/cn/src/documentation/skins/pelt/images/success.png new file mode 100644 index 00000000000..96fcfea3225 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/success.png differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/warning.png b/src/docs/cn/src/documentation/skins/pelt/images/warning.png new file mode 100644 index 00000000000..b81b2ce8a12 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/warning.png differ diff --git a/src/docs/cn/src/documentation/skins/pelt/images/xmldoc.gif b/src/docs/cn/src/documentation/skins/pelt/images/xmldoc.gif new file mode 100644 index 00000000000..c92d9b90574 Binary files /dev/null and b/src/docs/cn/src/documentation/skins/pelt/images/xmldoc.gif differ diff --git a/src/docs/cn/src/documentation/skins/pelt/note.txt b/src/docs/cn/src/documentation/skins/pelt/note.txt new file mode 100644 index 00000000000..d34c8db5ef4 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/note.txt @@ -0,0 +1,50 @@ +Notes for developer: + +--Legend------------------- +TODO -> blocker +DONE -> blocker +ToDo -> enhancement bug +done -> enhancement bug + +--Issues------------------- +- the corner images should be rendered through svg with the header color. +-> DONE +-> ToDo: get rid of the images and use only divs! + +- the menu points should be displayed "better". +-> DONE +-- Use the krysalis-site menu approach for the overall menu display. +-> DONE +-- Use the old lenya innermenu approch to further enhance the menu . +-> DONE + +- the content area needs some attention. +-> DONE +-- introduce the heading scheme from krysalis () +-> DONE +-> ToDo: make box with round corners +-> done: make underlined with variable border height +-> ToDo: make underline with bottom round corner +-- introduce the toc for each html-page +-> DONE +-- introduce the external-link-images. +-> DONE + +- the publish note should be where now only a border is. +Like
    +-> DONE +, but make it configurable. +-> DONE +- footer needs some attention +-> DONE +-- the footer do not have the color profile! Enable it! +-> DONE +-- the footer should as well contain a feedback link. +See http://issues.apache.org/eyebrowse/ReadMsg?listName=forrest-user@xml.apache.org&msgNo=71 +-> DONE + +- introduce credits alternativ location +-> DONE + +- border for published / breadtrail / menu /tab divs +-> ToDo \ No newline at end of file diff --git a/src/docs/cn/src/documentation/skins/pelt/skinconf.xsl b/src/docs/cn/src/documentation/skins/pelt/skinconf.xsl new file mode 100644 index 00000000000..2f8df12903c --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/skinconf.xsl @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/cn/src/documentation/skins/pelt/xslt/fo/document-to-fo.xsl b/src/docs/cn/src/documentation/skins/pelt/xslt/fo/document-to-fo.xsl new file mode 100644 index 00000000000..0fb2a23731b --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/xslt/fo/document-to-fo.xsl @@ -0,0 +1,22 @@ + + + + + diff --git a/src/docs/cn/src/documentation/skins/pelt/xslt/html/book-to-menu.xsl b/src/docs/cn/src/documentation/skins/pelt/xslt/html/book-to-menu.xsl new file mode 100644 index 00000000000..5242fc85152 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/xslt/html/book-to-menu.xsl @@ -0,0 +1,53 @@ + + + + + + + + + +
  • + +

    +
      + +
  • +
    + + +
  • +
    + +
    + +
    +
    + + + + + +
    diff --git a/src/docs/cn/src/documentation/skins/pelt/xslt/html/document-to-html.xsl b/src/docs/cn/src/documentation/skins/pelt/xslt/html/document-to-html.xsl new file mode 100644 index 00000000000..2d337bf0d60 --- /dev/null +++ b/src/docs/cn/src/documentation/skins/pelt/xslt/html/document-to-html.xsl @@ -0,0 +1,154 @@ + + + + + + + + + +
    +