syllabus for the book "Tiếng Anh 6 i-Learn Smart World"
Map reduce hdfs
1. I. MapReduce
1. Java MapReduce
− Ta cần 3 class: một map function, một reduce function và một đoạn code để chạy job.
− Map funtion: nó sẽ implementation Mapper interface, để thực thi hàm map( ) . Source code:
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reporter;
public class MaxTemperatureMapper extends MapReduceBase
implements Mapper<LongWritable, Text, Text, IntWritable> {
private static final int MISSING = 9999;
public void map(LongWritable key, Text value,
OutputCollector<Text, IntWritable> output, Reporter reporter)
throws IOException {
String line = value.toString();
String year = line.substring(15, 19);
int airTemperature;
if (line.charAt(87) == '+') { // parseInt doesn't like leading plus signs
airTemperature = Integer.parseInt(line.substring(88, 92));
} else {
airTemperature = Integer.parseInt(line.substring(87, 92));
}
String quality = line.substring(92, 93);
if (airTemperature != MISSING && quality.matches("[01459]")) {
output.collect(new Text(year), new IntWritable(airTemperature));
}
}
}
• Trong Mapper interface ta cần chỉ định các kiểu của input key, input value, output key, và
output value. Trong ví dụ ở trên:
• Input key là kiểu long integer.
• Input value: kiểu Text.
• Output key: là một year.
• Output value: là kiểu integer.
• Hadoop cung cấp một số kiểu cơ bản, nó được định nghĩa trong gói org.apache.hadoop.io.
Trong ví dụ này:
• LongWritable tương đương kiểu Java Long
2. • Text tương đưong với String
• IntWriteable tương đương với Java Integer.
• Hàm map( ) được đưa vào một key và một value.
• Hàm map( ) cũng được cung cấp một cài đặt của OutputCollector để ghi output vào đó.
− Phần reduce được định nghĩa sử dụng Reducer. Ví dụ:
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
public class MaxTemperatureReducer extends MapReduceBase
implements Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterator<IntWritable> values,
OutputCollector<Text, IntWritable> output, Reporter reporter)
throws IOException {
int maxValue = Integer.MIN_VALUE;
while (values.hasNext()) {
maxValue = Math.max(maxValue, values.next().get());
}
output.collect(key, new IntWritable(maxValue));
}
}
• Cũng tương tự, ta có 4 kiểm tham số được dùng để chỉ định cho các kiểu input và output. Trong
đó các kiểu input của hàm reduce phải khớp tuyệt đối với ouput type của map function. Trong
ví dụ này, output type của reduce function là Text và IntWriteable.
− Cuối cùng là đoạn code để chạy MapReduce job:
import java.io.IOException;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
public class MaxTemperature {
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.err.println("Usage: MaxTemperature <input path> <output path>");
System.exit(-1);
}
JobConf conf = new JobConf(MaxTemperature.class);
3. conf.setJobName("Max temperature");
FileInputFormat.addInputPath(conf, new Path(args[0]));
FileOutputFormat.setOutputPath(conf, new Path(args[1]));
conf.setMapperClass(MaxTemperatureMapper.class);
conf.setReducerClass(MaxTemperatureReducer.class);
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(IntWritable.class);
JobClient.runJob(conf);
}
}
• JobConf: chỉ định chi tiết job, nó cho phép ta điều khiển cách mà job làm việc.
• Trong JobConf ta cần chỉ định input path và output path.
• Một input path được chỉ định bằng cách gọi phương thức addInputPath( ) trong
FileInputFormat, nó có thể là một file đơn, một thư mục hay một file pattern. Một khuyến
cáo là addInputPath( ) có thể được gọi nhiều hơn một lần để sử dụng input từ nhiều paths.
• Output path được chỉ định bởi phươn thức setOutPutPath( ) của FileOutputFormat. Nó
chỉ định một thư mục nơi và các output files từ reducer functions được viết ra. Thư mục này
có thể chưa có trước khi chạy job.
• Tiếp theo ta cần chỉ định các kiểu map và reduce thông qua setMapperClass( ) và
setReduceClass( )
• các phương thức setOutPutKeyClass( ) và setOutPutValueClass( ) điều khiểu kiểu output cho
các chức năng map và reduce trong trường hợp chúng giống nhau, còn nếu khác nhau, kiểu map
ouput có thể được thiết lập sử dụng các phương thức setMapOutPutKeyClass( ) và
setMapOutPutValueClass( ).
• Các kiểu input được điêu khiển thông qua input format, ta chỉ định thôgn qua
TextInputFormat.
• Phương thức runJob( ) trong JobClient thực thi job và đợi cho nó kết thúc, ghi các thông tin ra
console.
2. Data Flow
− Một MapReduce job là một đơn vị của work mà client muốn thực thi, nó bao gồm: input data,
MapReduce program, và configuratioin information.
− Hadoop chạy job bằng việc phân chia nó vào trong các tasks, có 2 loại: map tasks và reduce
tasks.
− Có 2 kiểu nodes điều khiển quá trình thực thi job: một jobtracker và một số tasktrackers.
• jobtracker kết hợp tất cả job chạy trên hệ thống bằng việc lập ra các tasks để chạy
trên tasktrackers.
• Tasktrackers chạy các tasks và gửi báo cáo tiến tình sử lý tới jobtracker,
jobtracker này sẽ dữ một record của tấ cả các progress của mỗi job.
• Nếu một jobtracker fails, jobtracker có thẻ dãn nó trên một tasktracker khác.
− Hadoop phân chia input của một MapReduce job thành các fixed-size pieces gọi là input splits
hay just splits. Hadoop tạo một map task cho mỗi split, nó chạy useer-defined map function cho
mỗi record trong split.
− Cho phần lớn các job, một good split size là kich cỡ của HDFS block, mặc định là 64MB. Tuy
nhiên nó có thể thay đổi cho nhóm hay được chỉ định khi mỗi file được tạo ra.
4. 3. Hadoop Streaming
− Hadoop cung cấp một API cho MapReduce cho phép bạn việt các chức năng map và reduce
trêng nhiêu ngôn ngữ lập trình chứ ko chỉ có JAVA.
− Hadoop sử dụng Unix standard streams như interface giữa Hadoop và chươn trình → có thể sử
dụng bất cứ ngôn ngữ nào mà có thể đọc standard input và viết với standard output để ghi vào
MapReduce program.
− Streaming thường phù hợp với quá trình sử lý text và khi được sử dụng trong text mode, nó
hướng vào từng dòng của dữ liệu. Map input data được gửi vào trên standard I nput vào map
function, nó sử lý từng dòng một và ghi các dòng này vào standard output. Reduce functioin
đọc các dòng từ standard input, sẵp xếp theo key, và ghi kết quả của nó vào standard output.
II. HDFS (Hadoop Distributed File System)
_ HDFS (Hadoop Distributed Files System) là mô hình lưu trữ trên nhiều máy được sử dụng bởi
các ứng dụng của Hadoop. Các cụm máy HDFS bao gồm một NameNode có trách nhiệm quản lí các
metadata của hệ thống và các DataNode được lưu trữ.
− Khi dữ liệu trên một máy đơn tăng lên, nó cần phải chia ra một số máy riêng lẻ. Filesystem mà
quản lý việc lưu trữ thông qua mạng các máy được gọi là Distributed filesystems.
5. 1. Thiết kế của HDFS
Sơ đồ HDFS mô tả hoạt động của HDFS.
/***** Quoc
• Các đặc điểm:
• Hadoop, bao gồm có HDFS, thích hợp với lưu trữ và xử lí phân tán trên nhiều máy tính.
• HDFS có thể cấu hình dễ dàng, cùng với chế độ cấu hình mặc định phù hợp với nhiều hệ thống.
Trong gần như tất cả các trường hợp, cấu hình của HDFS chỉ bị thay đổi khi cluster quá lớn.
• Hadoop được viết bằng Java.
• Hadoop có hỗ trợ tương tác bằng dòng lệnh với HDFS.
• HDFS được thiết kế để lưu trữ các file rất lớn với streaming data acces patterns, hoạt động
trong một nhóm các hardware. Cụ thể:
• Very large files: kích cỡ file lưu trữ có thể đến hàng trăm MB, GB, hoặc tera bytes thậm chí
là petabytes.
• Streaming data access: HDFS được xây dựng trên ý tưởng mô hình truy cập dữ liêu: write-
one, read-many-times. Một dataset được sinh ra hay copy tu nguồn,sau đó các fân tích được
6. thực thi trên dataset đó.
• Commodity hardware: Hadoop không yêu cầu cao về hardware, nó đươc thiết kế để họat
động trong một nhóm các hardware mà có nguy cơ fail cao. Nó được thiết kế để làm việc
mà ko làm dán đoạn user khi xẩy ra lỗi.
− Một vài vấn đề mà HDFS không làm việc tốt:
• Low-latency data access: một ứng dụng mà yêu cầu độ trễ thấp khi truy cập data, ví dụ: trong
phạm vi 10ms (tens of milliseconds), nó sẽ không thể làm việc tốt trên HDFS. HDFS được tối
ưu hóa để cung cấp một dữ liệu lớn
• Quá nhiều file nhỏ (lost of small file): namenode nắm dữ filesystem metadata trong memory,
giới hạn về số lượng các file trong filesystem được quy định bởi số lượng memory trong
namenode. Mỗi file, thu mục, block khảng 150bytes.Ví dụ, nếu ta có 1 triệu files, mỗi file tương
ứng với một block, ta sẽ cần ít nhất 300MB bộ nhớ. Nó vẫn có thể lưu trữ vài triệ file, nhưng
đến hàng tỉ thì có thể vượt quá khả năng của hardware.
• Multiple writers, tùy tiệ chỉnh sửa file: các file trong HDFS chỉ ghi với một single writer. Việc
ghi file luôn ghi vào cuối của file. Không hỗ trợ nhiều wirter và chỉnh sửa nội dung tùy tiện
trong file.
•
2. Các khái niệm của HDFS
2.1 Block
− 1 blok trong HDFS có dung lượng mặc định là 64MBWeb Interface:
3. Web Interface của HDFS:
DataNode và NameNode đều duy trì một web server bên trong để hiện thị các thông tin cơ bản
về status hiện tại của cluster (hệ thống các máy tham gia lưu trữ và xử lí). Với cấu hình mặc
định, địa chỉ frontpage của NameNode là http://namenode-name:50070/. Địa chỉ này có chứa
danh sách các DataNode và những thống kê cơ bản của cả hệ thống. Giao diện web này còn có
thể được sử dụng để mở hệ thống file ( Sử dụng “Browse the File System” ở trang đầu).
4. Shell Command
Hadoop có hệ thống lệnh để tương tác trực tiếp với HDFS và các hệ thống file (file system)
khác mà Hadoop hỗ trợ. Lệnh bin/hadoop fs -help sẽ cho hiển thị các lệnh cho phép bởi Hadoop
shell. Ngoài ra, lệnh bin/hadoop fs -help command-name sẽ hiển thị chi tiết về một lệnh. Các lệnh
trên hỗ trợ hầu hết các hoạt động của hệ thống file thông thường ví dụ như copy file, thay đổi
các quyền... Thậm chí các lệnh này còn hỗ trợ một vài hoạt động đặc biệt của HDFS như thay
đổi số bản copy của files.
Giới thiệu về các lệnh trong hadoop:
Các lệnh sẽ được gõ từ thư mục {HADOOP_HOME}/hadoop-{HADOOP_VERSION}
Lệnh DFSAdmin:
7. 1. Lệnh bin/hadoop dfsadmin hỗ trợ một vài hoạt động quản trị HDFS
2. Lệnh bin/hadoop dfsadmin -help đưa ra danh sách các lệnh được hỗ trợ hiện thời.
3. Lệnh hiển thị các thống kê của HDFS. Một vài thông tin được hiển thị ở đây cũng xuất hiện
ở trang chủ của NameNode.
4. safemode Mặc dù không phải lúc nào cũng cần thiết nhưng một admin có thể cấu hình bật
hoặc tắt safe mode.
5. Lệnh finalizeUpgrade xóa back up của hệ thống máy trong lần upgrade trước.
6. refreshNodes :Cập nhật tập hợp các host được phép kết nối vào NameNode. Đọc lại các cấu
hình file để cập nhật các giá trị được thiết lập bởi dfs.hosts, dfs.host.exclude và đọc lại nội dung
(entry - hostname) trong các file đó. Các nội dung không được định nghĩa trong dfs.hots và
trong dfs.hót.exclude bị dừng hoạt động. Các nội dung được định nghĩa trong dfs.hots và trong
dfs.hot.exclude sẽ được đưa ra khỏi danh sách ngừng hoạt động nếu đã bị cho ngừng hoạt động
trước đó.
5. NameNode thứ cấp (Secondary NameNode)
NameNode lưu trữ các thay đổi vào hệ thống các file logs (edits) . Khi NameNode được khởi
động, nó đọc các thông số của HDFS từ một file image (fsimage) và áp dụng các thay đổi từ
các file logs vào file image . Sau đó, NameNode viết các thông số mới của HDFS vào fsimage
và hoạt động bình thường với một file edits trắng. Vì NameNode chỉ trộn (merge) fsimage và
edits vào lúc được khởi động, các file log dần dần trở nên rất lớn ở một hệ thống hoạt động liên
tục. Một tác động khác của file edits kích thước lớn là làm cho quá trình khởi động trở nên
chậm hơn.
NameNode thứ cấp trộn file fsimage và các file logs theo định kỳ và giữ cho kích thước các
file logs trong một giới hạn. NameNode thứ cấp được chạy ở một máy khác máy có NameNode
sơ cấp hay NameNode chính (primary NameNode). NameNode thứ cấp được khởi động bằng
lệnh bin/start-dfs.sh trên một máy mà được xác định trong file conf/masters
Việc khởi động quá trình làm việc của NameNode thứ cấp (checkpoint process) được kiểm soát
bởi hai tham số:
. Tham số fs.checkpoint.period, sẽ khởi động quá trình trộn các thông số của file log vào file
image theo mặc định là mỗi tiếng một lần.
. Tham số fs.checkpoint.size, khởi động quá trình trộn khi các file log đạt được kích thước 64Mb
theo mặc định.
8. Checkpoint gần nhất có thể được copy sang NameNode chính nếu cả file image và file log bị
mất. Để làm được việc này, thực hiện các bước sau:
. Tạo một thư mục trống với tên gọi được cập nhật vào tham số dfs.name.dir
. Cập nhật địa chỉ của thư mục vào fs.checkpoint.dir
. Khởi động NameNode với option -importCheckpoint
NameNode sẽ upload checkpoint từ thư mục fs.checkpoint.dir và lưu nó vào thư mục được vừa
được mặc định trong dfs.name.dir.
6. Rebalancer
Dữ liệu HDFS có thể không được lưu một cách đồng bộ trên các DataNode, lí do là có thể có
DataNode mới được thêm vào hệ thống. Không chỉ có vậy, khi lưu các block (Dữ liệu của các
file được lưu dưới dạng chuỗi các blocks), NameNode sẽ xem xét nhiều yếu tố để quyết định
DataNode được lưu. Một vài yếu tố được xem xét ở đây là:
. Yêu cầu giữ một bản sao của block trên chính node đã viết ra các block.
. Nhu cầu phân phối các bản sao của một block ra các rack khác nhau để hệ thống có thể duy trì
khi dữ liệu của cả một rack bị mất.
. Một bản sao được phân phối cho DataNode trên cùng rack với node đã viết ra block để tiết
kiệm đường truyền giữa các rack.
. Phân phối đồng đều dữ liệu ra các node trong HDFS.
Do có rất nhiều yếu tố được xem xét như vậy, tình trạng mất cân bằng trong lưu trữ giữa các
node là khó tránh khỏi. Vì vậy, HDFS cung cấp công cụ giúp phân tích tình trạng mất cân bằng
và giúp cân bằng lại các node.
Lệnh balancer:
hadoop balancer [-threshold <threshold>]
Để tìm hiểu rõ hơn về các công cụ này, theo đường dẫn:
http://issues.apache.org/jira/secure/attachment/12368261/RebalanceDesign6.pdf
7. Nhận biết rack (Rack Awareness)
Thông thường, một cụm máy Hadoop lớn được đặt ở nhiều rack khác nhau và lưu thông
giữa các máy cùng rack dễ dàng hơn nhiều so với giữa các máy ở khác rack. Hadoop cho phép
người quản trị lựa chọn rack cho các node thông qua tham số . Khi script này được cấu hình,
mỗi node sẽ chạy script để quyết định rack ID của nó. Khi được cài đặt theo mặc định, các node
sẽ được thiết lập ở cùng một rack. Xem thêm ở:
9. http://issues.apache.org/jira/secure/attachment/12345251/Rack_aware_HDFS_proposal.pdf
8. Safe Mode
Khi khởi động NameNode load hệ thống file từ file fsimage và các file edits log. Sau đó,
NameNode đợi cho các DataNode báo cáo về các blocks của nó để NameNode không copy lại
các block đã có đủ bản sao. Trong khoảng thời gian này, NameNode được đặt vào chế độ an
toàn (Safe Mode). Về cơ bản, Safe Mode là chế độ read-only cho hệ thống HDFS, trong đó nó
không cho phép bất kì một thay đổi nào lên hệ thông file hoặc blocks. Thông thường,
NameNode tự động rời khỏi Safe Mode sau khi các DataNode gửi thông báo là các blocks đã
sẵn sàng. Nếu được yêu cầu, HDFS có thể đặt Safe Mode công khai bằng cách sử dụng lệnh
'bin/hadoop dfsadmin -safemode . Trang chủ của NameNode có hiển thị chế độ của Safe Mode
(bật hoặc tắt).
9. fsck
HDFS hộ trợ lệnh để kiểm tra những bất nhất trong hệ thống. Nó được thiết kế để báo
cáo các vấn đề về nhiều file, ví dụ như thiếu blocks của một file hoặc các bản sao không hoàn
thiện của block. Không giống như công cụ fsck truyền thống của các hệ thống file khác, lệnh
fsck này không sửa chữa những lỗi mà nó phát hiện được. Thông thường, NameNode sẽ tự
động sửa chữa những lỗi có thể hồi phục được. HDFS fsck có thể được chạy bằng cách gõ
bin/hadoop fsck'. Lệnh này có thể được chạy trên cả hệ thống hoặc một phần của hệ thống. Chi
tiết về lệnh fsck:
Usage: hadoop fsck [GENERIC_OPTIONS] <path> [-move | -delete | -openforwrite] [-files [-blocks [-
locations | -racks]]]
COMMAND_OPTION Description
<path> Bắt đầu kiểm tra từ đường dẫn này
-move Chuyển các file bị hỏng vào /lost+found
-delete Xóa các file bị hỏng
-openforwrite Hiển thị các file được mở để viết
-files Hiển thị ra các file đang được kiểm tra.
-blocks Hiển thị báo cáo block
-locations Hiển thị vị trí của tất cả các block
-racks
Hiển thị network topology cho các vị trí của
DataNode
Các lựa chọn của lệnh fsck
10. Nâng cấp và bỏ nâng cấp:
10. Cũng giống như các phần mềm khác, trong bản nâng cấp của hadoop cũng có thể xuất
hiện bug hoặc xung đột với hệ thống đang chạy. HDFS cho phép quản trị viên có thể gỡ bỏ nâng
cấp và quay trở về phiên bản trước. HDFS có một bản back up. Trước khi nâng cấp, quản trị
viên cần gỡ bỏ các back up đang có bằng lệnh bin/hadoop dfsadmin -finalizeUpgrade. Sau đây là các
bước nâng cấp cơ bản:
Trược khi nâng cấp, gỡ bỏ back up. Để biết hệ thống có cần gỡ bỏ back up không, gõ
lệnh dfsadmin -upgradeProgress status.
Dừng hệ thống Hadoop và phân phát phiên bản mới của Hadoop
Chạy phiên bản mới bằng lệnh bin/start-dfs.sh -upgrade).
Nếu cần phải gỡ bỏ bản nâng cấp, cần phải dừng hệ thống Hadoop và phân phối phiên bản cũ
của Hadoop. Sau đó chạy lệnh Hadoop với option rollback: bin/start-dfs.h -rollback .
11. Phân quyền:
Người sử dụng máy NameNode sẽ được gán là superuser.
Quoc
*********************************************************/
12.1 Đọc data thông qua Hadoop URL
− Ví dụ:
package MyExample;
import java.io.InputStream;
import java.net.URL;
import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils;
public class URLCat {
static {
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
}
public static void main(String[] args) throws Exception {
InputStream in = null;
try {
in = new URL("hdfs://localhost/user/maivanha/urllist.txt").openStream();
IOUtils.copyBytes(in, System.out, 4096, false);
} finally {
IOUtils.closeStream(in);
}
}
}
11. 12.2 Đọc data sử dụng FileSystem API
− Ví dụ:
package MyExample;
import java.io.InputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class FileSystemCat {
public static void main(String[] args) throws Exception {
String uri = "hdfs://localhost/user/maivanha/urllist.txt";
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri), conf);
InputStream in = null;
try {
in = fs.open(new Path(uri));
IOUtils.copyBytes(in, System.out, 4096, false);
} finally {
IOUtils.closeStream(in);
}
}
}
13.3 Sử dụng FSDataInputStream
− Hàm open( ) của FileSystem trả về một FSDataInputStream, nó hỗ trợ việc truy cập ngẫu nhiên
và ta có thể đọc bất cứ phần nào của stream đó.
− Ví dụ:
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class FileSystemDoubleCat {
public static void main(String[] args) throws Exception {
String uri = MyStaticValues.uri;
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri), conf);
FSDataInputStream in = null;
try {
in = fs.open(new Path(uri));
12. IOUtils.copyBytes(in, System.out, 4096, false);
in.seek(0); // go back to the start of the file
IOUtils.copyBytes(in, System.out, 4096, false);
} finally {
IOUtils.closeStream(in);
}
}
}
− Chạy ví dụng này bằng lệnh:
% hadoop FileSystemDoubleCat hdfs://localhost/user/tom/quangle.txt
− FSDataIputStream kế thừa PositionedReadable interface cho việc đọc từng phần của một file:
public interface PositionedReadable {
public int read(long position, byte[] buffer, int offset, int length)
throws IOException;
public void readFully(long position, byte[] buffer, int offset, int length)
throws IOException;
public void readFully(long position, byte[] buffer) throws IOException;
}
13.4 Writing Data
− Lớp FileSystem có một số phương thức cho việc tạo một file Trong đó đơn giản nhất là:
public FSDataOutputStream create(Path f) throws IOException
− Một ví dụ về việc copy file từ local lên một Hadoop Filesystem:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
public class FileCopyWithProgress {
public static void main(String[] args) throws Exception {
String localSrc = "/home/maivanha/urllist.txt";
String dst = "hdfs://192.168.14.15
/user/home/maivanha/MyVanHaUrlListTestWrite.txt";
InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(dst), conf);
OutputStream out = fs.create(new Path(dst), new Progressable() {
public void progress() {
13. System.out.print(".");
}
});
IOUtils.copyBytes(in, out, 4096, true);
}
}
− Đểy chạy chương trình này ta dùng lệnh:
% hadoop FileCopyWithProgress input/docs/1400-8.txt
hdfs://localhost/user/tom/1400-8.txt
13.4.2 FSDataOutputStream
− Phương thức create( ) trong FileSystem trả về một FSDataOutputStream, tương tự như
FSDataInputStream, có một hàm để lấy về vị trí hiện thời trong file:
package org.apache.hadoop.fs;
public class FSDataOutputStream extends DataOutputStream implements Syncable {
public long getPos() throws IOException {
// implementation elided
}
// implementation elided
}
− Tuy nhiên hàm FSDataOuputStream không hỗ trợ việc ghi dữ liệu ở bất cứ vị trí nào trong file
mà chỉ có thể ghi tiếp vào cuối file.
13.4.3 Directories
− FileSystem cugn cập một phương thức để tạo một thư mục:
public boolean mkdirs(Path f) throws IOException
− Hàm này có thể tạo cả các thư mục cha nếu nó chưa tồn tại. Nó trả về true nếu thư mục đó đựoc
tạo thành công.
− Ngoài ra hàm create( ) cũng có khả năng tạo các thư mục cha của file cần ghi nếu chúng chưa
tồn tại.
13.5 Querying the Filesystem
13.5.1 File metadata: FileStatus
− Lớp FileStatus chứa metadata cho các file và các thư mục, bao gồm: chiều dài file, kích cỡ
block, thời gian chỉnh sửa, quyền sở hữu, các thông tin về permission, replication.
− Phương thức getFileStatus( ) trong FileSystem là cách để lấy ra một đối tượng FileStatus của
một file hay một thư mục.
− Ví dụ:
package MyExample.HadoopJavaInterface;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
14. import junit.framework.Assert;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class ShowFileStatusTest {
private MiniDFSCluster cluster; // use an in-process HDFS cluster for testing
private FileSystem fs;
private final String path = "/home/maivanha/dic.txt";
@Before
public void setUp() throws IOException {
Configuration conf = new Configuration();
if (System.getProperty("test.build.data") == null) {
System.setProperty("test.build.data", "/tmp");
}
cluster = new MiniDFSCluster(conf, 1, true, null);
fs = cluster.getFileSystem();
OutputStream out = fs.create(new Path(path));
out.write("content".getBytes("UTF-8"));
out.close();
}
@After
public void tearDown() throws IOException {
if (fs != null) { fs.close(); }
if (cluster != null) { cluster.shutdown(); }
}
@Test(expected = FileNotFoundException.class)
public void throwsFileNotFoundForNonExistentFile() throws IOException {
fs.getFileStatus(new Path("no-such-file"));
}
@Test
public void fileStatusForFile() throws IOException {
Path file = new Path(path);
FileStatus stat = fs.getFileStatus(file);
Assert.assertEquals(stat.getPath().toUri().getPath(), path);
Assert.assertEquals(stat.isDir(), false);
Assert.assertEquals(stat.getLen(), 7L);
Assert.assertTrue(stat.getModificationTime() <= System.currentTimeMillis());
Assert.assertEquals(stat.getReplication(), (short) 1);
15. Assert.assertEquals(stat.getBlockSize(), 64 * 1024 * 1024L);
Assert.assertEquals(stat.getOwner(), "tom");
Assert.assertEquals(stat.getGroup(), "supergroup");
Assert.assertEquals(stat.getPermission().toString(), "rw-r--r--");
}
@Test
public void fileStatusForDirectory() throws IOException {
Path dir = new Path("/home/maivanha/build/test");
FileStatus stat = fs.getFileStatus(dir);
Assert.assertEquals(stat.getPath().toUri().getPath(), "/home/maivanha/build/test");
Assert.assertEquals(stat.isDir(), true);
Assert.assertEquals(stat.getLen(), 0L);
Assert.assertTrue(stat.getModificationTime() <= System.currentTimeMillis());
Assert.assertEquals(stat.getReplication(), (short) 0);
Assert.assertEquals(stat.getBlockSize(), 0L);
Assert.assertEquals(stat.getOwner(), "tom");
Assert.assertEquals(stat.getGroup(), "supergroup");
Assert.assertEquals(stat.getPermission().toString(), "rwxr-xr-x");
}
}
III. Xây dựng một MapReduce Application
1. Configuration API
− Các thành phần trong Hadoop được cấu hình bằng việc sử dụng configuration API của Hadoop.
Mỗi cài đặt của lớp Configuration đại diệ cho một tập hợp các properties và giá trị của chúng.
Mỗi property được đặt tên bởi String, có kiểu là một trong cá kiểu như: kiểu java nguyên
thủy(như boolean, int, long, float), các kiểu String, class, java.io.File và các tập hợp của Strings.
− Configuration đọ các properties của nó từ một XML file với một cấu trúc đơn giản gồm name-
value. Ví dụ:
<?xml version="1.0"?>
<configuration>
<property>
<name>color</name>
<value>yellow</value>
<description>Color</description>
</property>
<property>
<name>size</name>
<value>10</value>
<description>Size</description>
</property>
<property>
<name>weight</name>
<value>heavy</value>
16. <final>true</final>
<description>Weight</description>
</property>
<property>
<name>size-weight</name>
<value>${size},${weight}</value>
<description>Size and weight</description>
</property>
</configuration>
− Ví dụ về đọc file cấu hình:
Configuration conf = new Configuration();
conf.addResource("configuration-1.xml");
assertThat(conf.get("color"), is("yellow"));
assertThat(conf.getInt("size", 0), is(10));
assertThat(conf.get("breadth", "wide"), is("wide"));
− Ngoài ra hàm get( ) còn cho phép chỉ định giá trịn mặc định, nó sẽ được sử dụng khi property ta
đưa vào không được định nghĩa trong file XML.
1.2 Kết hợp nhiều nguồn cấu hình với nhau
− Khi ta chỉ định sẽ load nhiều file xml, thì các properties được định nghĩa trong file mà được add
vào sau sẽ đè lên file thêm vào trước nó. Ví dụ, ta chỉ định thêm một file cấu hình nữa là file
configuration-2.xml có nội dung:
<?xml version="1.0"?>
<configuration>
<property>
<name>size</name>
<value>12</value>
</property>
<property>
<name>weight</name>
<value>light</value>
</property>
</configuration>
− Code đọc cấu hình từ 2 file XML:
Configuration conf = new Configuration();
conf.addResource("configuration-1.xml");
conf.addResource("configuration-2.xml");
assertThat(conf.getInt("size", 0), is(12));
assertThat(conf.get("weight"), is("heavy"));
1.3 Khả năng mở rộng
− Các thuôc tính cấu hình được định nghĩa trong các properties hay trong system properties.
− Ví dụ: trong file cấu hình xml đầu tiên, thuộc tính size-weight đuợc định nghĩa như ${size},$
{weight} và nó được mở rộng để sử dụng:
assertThat(conf.get("size-weight"), is("12,heavy"));
17. − Các system properties được ưu tiên hơn các properties được định nghĩa trong file xml. Ví dụ:
System.setProperty("size", "14");
assertThat(conf.get("size-weight"), is("14,heavy"));
− Đặc điểm này hữu dụng cho việc ghi đè lên các properties khi ta chạy command line bằng các
tham số: -Dproperty=value
− Chú ý rằng: ta chỉ có thể định nghĩa các system properties khi cac properties đó được định
nghĩa trong các file xml, nếu ko, nó chỉ có thể là null. Ví dụ:
System.setProperty("length", "2");
assertThat(conf.get("length"), is((String) null));
2. Cấu hình và chạy hadoop
2.1 yêu cầu
− Java 1.6.x
− ssh và sshd
− với windows cần cài đặt thêm Cygwin
2.2 Cài đặt phần mềm
− trong môi trường Ubuntu có thể cài ssh bằng dòng lệnh:
$ sudo apt-get install ssh
$ sudo apt-get install rsync
− Trên môi trường window, khởi động cygwin và chọn packages:
openssh – the Net category
2.3 Start Hadoop Cluster
− Sau khi download và giải nén gói hadoop, vào file conf/hadoop-env.sh và thiết lập giá trị cho
biếtn JAVA_HOME, ví dụ: export JAVA_HOME=/usr/lib/jvm/java-6-openjdk
− Chạy thử lệnh: bin/hadoop nếu thấy hiển thị danh sách các hadoop script là được.
2.4 Standalone Operation
The following example copies the unpacked conf directory to use as input and then finds and
displays every match of the given regular expression. Output is written to the given output directory.
− Mặc định hadoop được cấu hình để chạy trong chế độ non-distributed.
− Thực hiện các lệnh:
$ mkdir input
$ cp conf/*.xml input
$ bin/hadoop jar hadoop-*-examples.jar grep input output 'dfs[a-z.]+'
$ cat output/*
2.5 Pseudo-Dirstributed Operation
2.5.1 Cấu hình
− File conf/core-site.xml
<configuration>
18. <property>
<name>fs.default.name</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>
− File conf/hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
− File conf/hdfs-site.xml
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>localhost:9001</value>
</property>
</configuration>
2.5.2 Cấu hình SSH
− Kiểm tra kết nối SSH
$ ssh localhost
− Nếu khi kết nối ssh tới localhost mà vẫn bị yêu cầu nhập password thì chạy các lệnh sau:
$ ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys
2.5.3 Thực thi chương trình
− Định dạng một distributed-filesystem:
$ bin/hadoop namenode -format
− Start hadoop daemons:
$ bin/start-all.sh
− Sử dụng trình duyệt web để quan sát NameNode và JobTracker thông qua 2 địa chỉ:
• NameNode: http://localhost:50070
• JobTracker: http://localhost:50030
− Copy input files vào distributed filesystem:
$ bin/hadoop fs -put conf input
− Chạy các ví dụ:
$ bin/hadoop jar hadoop-*-example.jar grep iput output ‘dfs[a-z.]+’
− Xem xét out files:
• Copy output files từ distributed filesystem tới local filesystem để xem:
$ bin/hadoop fs -get output output
$ cat output/*
Hoặc
• hiển thị output filé trên distributed filesystem:
$ bin/hadoop fs – cat output/*
19. − Sau khi thực hiện song, stop daemons:
$ bin/stop-all.sh
3. Cấu hình Cluster Setup
3.1 Cài đặt
− Lựa chọn một máy trong cluster làm NameNode và các máy khác sẽ là JobTracker
− tất cả các máy trong cluster có cùng một HADOOP_HOME path.
3.2 Cấu hình
3.2.1 Các file cấu hình
Hadoop có 2 loại file cấu hình quan trọng:
− Các file cấu hình mặc định chỉ đọc: src/core/core-default.xml, src/hdfs/hdfs-default.xml và
src/mapred/mapred-default.xml
− các file cấu hình chỉ đinh: conf/core-site.xml, conf/hdfs-site.xml và conf/mapred-site.xml.
− Ngoài ra ta có thể điểu khiển khác scripts của hadoop dựa vào thư mục bin/ thông qua việc thiết
lập giá trị site-specific thôgn qua conf/hadoop-env.sh
3.2.2 Starting HDFS
− Tiến hành format file system:
$ bin/hadoop namenode -format
− Start distributed file system:
$ bin/start-dfs.sh
− Start Map-reduce với lệnh:
$ bin/start-mapred.sh
− Tạo thư mục home của bạn nếu nó chưa tồn tại:
$ bin/hadoop dfs -mkdir /user
Sau đó thêm vào thư mục của bạn:
$ bin/hadoop dfs -mkdir /user/maivanha
− Upload file: đưa một file vào trong HDFS, ta có thể dùng lệnh put:
$ bin/hadoop dfs -put /home/someone/interestingFile.txt /user/maivanha/
Xác định file đó trong HDFS:
$ bin/hadoop dfs -ls /user/maivanha
$ bin/hadoop dfs -ls
− Lấy dữ liệu từ HDFS:
• Hiển thị dữ liệu với lệnh cat:
$ bin/hadoop dfs -cat foo
• Copy file từ HDFS vào local:
$ bin/hadoop dfs -get foo localFoo
$ cat localFoo
− Hadoop Shutdown
• Stop hadoop:
$ bin/stop-dfs.sh
• Stop Map reduce
20. $ bin/stop-mapred.sh
4. Ví dụ
4.1 Mô tả bài tóan
− Viết một chương trình tiến hành đếm số lần xuất hiện của một từ trong một tập các văn bản.
Cho chương trình này chạy trong một hệ thống gồm 2 máy, 1 máy làm master và một máy là
slave
4.2 Source Code
package WordCount;
import java.io.*;
import java.util.*;
21. import org.apache.hadoop.fs.Path;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.util.*;
import com.sun.corba.se.impl.oa.poa.AOMEntry;
public class WordCount extends Configured implements Tool{
private static final String inputPath = "/user/hadoop/wordcount/input";
private static final String outputPath = "/user/hadoop/wordcount/output";
public static class Map extends MapReduceBase implements Mapper<LongWritable, Text,
Text, IntWritable> {
static enum Counters { INPUT_WORDS }
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
private boolean caseSensitive = true;
private Set<String> patternsToSkip = new HashSet<String>();
private long numRecords = 0;
private String inputFile;
public void configure(JobConf job) {
caseSensitive = job.getBoolean("wordcount.case.sensitive", true);
inputFile = job.get("map.input.file");
if (job.getBoolean("wordcount.skip.patterns", false)) {
Path[] patternsFiles = new Path[0];
try {
patternsFiles = DistributedCache.getLocalCacheFiles(job);
} catch (IOException ioe) {
System.err.println("Caught exception while getting cached files: "
+ StringUtils.stringifyException(ioe));
}
for (Path patternsFile : patternsFiles) {
parseSkipFile(patternsFile);
}
}
}
private void parseSkipFile(Path patternsFile) {
try {
BufferedReader fis = new BufferedReader(new
22. FileReader(patternsFile.toString()));
String pattern = null;
while ((pattern = fis.readLine()) != null) {
patternsToSkip.add(pattern);
}
} catch (IOException ioe) {
System.err.println("Caught exception while parsing the cached file '" +
patternsFile + "' : " + StringUtils.stringifyException(ioe));
}
}
public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable>
output, Reporter reporter) throws IOException {
String line = (caseSensitive) ? value.toString() : value.toString().toLowerCase();
for (String pattern : patternsToSkip) {
line = line.replaceAll(pattern, "");
}
StringTokenizer tokenizer = new StringTokenizer(line);
while (tokenizer.hasMoreTokens()) {
word.set(tokenizer.nextToken());
output.collect(word, one);
reporter.incrCounter(Counters.INPUT_WORDS, 1);
}
if ((++numRecords % 100) == 0) {
reporter.setStatus("Finished processing " + numRecords + " records " +
"from the input file: " + inputFile);
}
}
}
public static class Reduce extends MapReduceBase implements Reducer<Text, IntWritable,
Text, IntWritable> {
public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text,
IntWritable> output, Reporter reporter) throws IOException {
int sum = 0;
while (values.hasNext()) {
sum += values.next().get();
}
output.collect(key, new IntWritable(sum));
}
}
public int run(String[] args) throws Exception {
JobConf conf = new JobConf(getConf(), WordCount.class);
conf.setJobName("wordcount");
24. − Cấu hình SSH:
$ su – hadoop
$ ssh-keygen -t rsa -P “”
enable SSH acces cho máy local:
$ cat $HOME/.ssh/id_rsa.pub >> $HOME/.ssh/authorized_keys
− Disable IPv6 cho hadoop bằng cách thêm vào fiel conf/hadoop-env.sh đoạn sau:
export HADOOP_OPTS=-Djava.net.preferIPv4Stack=true
− Cài đặt Hadoop vào máy:
• Tải bản Hadoop về máy và đặt vào thư mục /usr/local sau đó chạy các lệnh:
$ cd /usr/local
$ sudo tar xzf hadoop-0.20.2.tar.gz
$ sudo mv hadoop-0.20.2 hadoop
$ sudo chown -R hadoop:hadoop hadoop
− Cấu hình cho file conf/hadoop-env.sh : thêm vào cuối file này biến môi trường cho JAVA:
export JAVA_HOME=/usr/lib/jvm/java-6-sun
− Cấu hình file conf/core-site.xml ta thêm vào đoạn cấu hình sau:
<!-- In: conf/core-site.xml -->
<property>
<name>hadoop.tmp.dir</name>
<value>/your/path/to/hadoop/tmp/dir/hadoop-${user.name}</value>
<description>A base for other temporary directories.</description>
</property>
<property>
<name>fs.default.name</name>
<value>hdfs://localhost:54310</value>
<description>The name of the default file system. A URI whose
scheme and authority determine the FileSystem implementation. The
uri's scheme determines the config property (fs.SCHEME.impl) naming
the FileSystem implementation class. The uri's authority is used to
determine the host, port, etc. for a filesystem.</description>
</property>
− Cấu hình file conf/mapred-site.xml thêm vào:
<!-- In: conf/mapred-site.xml -->
<property>
<name>mapred.job.tracker</name>
<value>localhost:54311</value>
<description>The host and port that the MapReduce job tracker runs
at. If "local", then jobs are run in-process as a single map
and reduce task.
</description>
</property>
− Cấu hình file conf/hdfs-site.xml :
<!-- In: conf/hdfs-site.xml -->
<property>
25. <name>dfs.replication</name>
<value>2</value>
<description>Default block replication.
The actual number of replications can be specified when the file is created.
The default is used if replication is not specified in create time.
</description>
</property>
Vì ở đây hệ thống hoạt động trên 2 máy,nên giá trị của dfs.replication sẽ là 2
− Format namenode: $ bin/hadoop namenode -format
− Start hadoop: $ bin/start-all.sh
− Upload file: đưa các file dữ liệu vào trong HDFS:
$ bin/hadoop dfs -copyFromLocal /tmp/gutenberg gutenberg
4.3.3 Cấu hình master và slave
− Trước tiên cần phải stop toàn bộ hệ thôgns hadoop: $ bin/stop-all.sh
− Ta copy toàn bộ nội dung trong /usr/local/hadoop sang các máy slave.
− Giả sử ta sử dụng 2 máy và có ip lần lượt là:
• master: 192.168.1.14
• slave: 192.168.1.15
− Trên máy master:
• thêm vào file conf/masters ip của máy: 192.168.1.14
• Thêm vào file conf/slave địa chỉ ip của cả 2 máy:
192.16.1.14
192.16.1.15
− Trên máy slave: chỉ cần thêm vào file cấu hình conf/slave ip của máy slave: 192.168.1.15
− Tiến hành format lại namenode vì ta đã thay đổi khá nhiều cấu hình:
$ bin/hadoop namenode -format
4.3.4 Chạy ví dụ
− Từ máy master, sử dụng lênh:
$ bin/hadoop jar hadoop-0.20.2-examples.jar wordcount gutenberg gutenberg-
output
− Trong đó chương trình đọc đầu vào từ gutenberg và dữ liệu đầu ra sẽ nằm ở gutenberg-output
− Kiểm tra kết quả chương trình, sử dung hệ thống web của hadoop hoặc đọc các file log trên cả 2
máy để thấy quá trình họat động của hệ thống này.