1.windows mapreduce开发环境
linux下进行hadoop应用的开发,不会存在兼容性问题。如在window上做客户端应用开发,需要设置以下环境:
A、在windows的某个目录下解压一个hadoop的安装包
B、将安装包下的lib和bin目录用对应windows版本平台编译的本地库替换
C、在window系统中配置HADOOP_HOME指向你解压的安装包
D、在windows系统的path变量中加入hadoop的bin目录
java对hdfs相关的操作
package com.lei.bigdata.hdfs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.junit.Before;
import org.junit.Test;
public class HdfsClient {
FileSystem fs = null;
@Before
public void init() throws Exception {
// 构造一个配置参数对象,设置一个参数:我们要访问的hdfs的URI
// 从而FileSystem.get()方法就知道应该是去构造一个访问hdfs文件系统的客户端,以及hdfs的访问地址
// new Configuration();的时候,它就会去加载jar包中的hdfs-default.xml
// 然后再加载classpath下的hdfs-site.xml
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://bigdata-sg-a-05:9000");
/**
* 参数优先级: 1、客户端代码中设置的值 2、classpath下的用户自定义配置文件 3、然后是服务器的默认配置
*/
conf.set("dfs.replication", "2");
conf.set("dfs.block.size","64m");
// 获取一个hdfs的访问客户端,根据参数,这个实例应该是DistributedFileSystem的实例
// fs = FileSystem.get(conf);
// 如果这样去获取,那conf里面就可以不要配"fs.defaultFS"参数,而且,这个客户端的身份标识已经是hadoop用户
fs = FileSystem.get(new URI("hdfs://bigdata-sg-a-05:9000"), conf, "hadoop");
// 获取文件系统相关信息
DatanodeInfo[] dataNodeStats = ((DistributedFileSystem) fs).getDataNodeStats();
for(DatanodeInfo dinfo: dataNodeStats){
System.out.println(dinfo.getHostName());
}
}
/**
* 往hdfs上传文件
*
* @throws Exception
*/
@Test
public void testAddFileToHdfs() throws Exception {
// 要上传的文件所在的本地路径
Path src = new Path("g:/apache-flume-1.6.0-bin.tar.gz");
// 要上传到hdfs的目标路径
Path dst = new Path("/");
fs.copyFromLocalFile(src, dst);
fs.close();
}
/**
* 从hdfs中复制文件到本地文件系统
*
* @throws IOException
* @throws IllegalArgumentException
*/
@Test
public void testDownloadFileToLocal() throws IllegalArgumentException, IOException {
// fs.copyToLocalFile(new Path("/apache-flume-1.6.0-bin.tar.gz"), new Path("d:/"));
fs.copyToLocalFile(false,new Path("/apache-flume-1.6.0-bin.tar.gz"), new Path("d:/"),true);
fs.close();
}
/**
* 目录操作
* @throws IllegalArgumentException
* @throws IOException
*/
@Test
public void testMkdirAndDeleteAndRename() throws IllegalArgumentException, IOException {
// 创建目录
fs.mkdirs(new Path("/a1/b1/c1"));
// 删除文件夹 ,如果是非空文件夹,参数2必须给值true
fs.delete(new Path("/aaa"), true);
// 重命名文件或文件夹
fs.rename(new Path("/a1"), new Path("/a2"));
}
/**
* 查看目录信息,只显示文件
*
* @throws IOException
* @throws IllegalArgumentException
* @throws FileNotFoundException
*/
@Test
public void testListFiles() throws FileNotFoundException, IllegalArgumentException, IOException {
// 思考:为什么返回迭代器,而不是List之类的容器, 如果文件特大, 那不就崩啦! 迭代器是每迭代一次都向服务器取一次
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
while (listFiles.hasNext()) {
LocatedFileStatus fileStatus = listFiles.next();
System.out.println(fileStatus.getPath().getName());//文件名
System.out.println(fileStatus.getBlockSize());//block块的大小
System.out.println(fileStatus.getPermission());//文件的权限
System.out.println(fileStatus.getLen());//字节数
BlockLocation[] blockLocations = fileStatus.getBlockLocations();//获取block块
for (BlockLocation bl : blockLocations) {
System.out.println("block-length:" + bl.getLength() + "--" + "block-offset:" + bl.getOffset());
String[] hosts = bl.getHosts(); //主机名
for (String host : hosts) {
System.out.println(host);
}
}
System.out.println("--------------为angelababy打印的分割线--------------");
}
}
/**
* 查看文件及文件夹信息
*
* @throws IOException
* @throws IllegalArgumentException
* @throws FileNotFoundException
*/
@Test
public void testListAll() throws FileNotFoundException, IllegalArgumentException, IOException {
FileStatus[] listStatus = fs.listStatus(new Path("/"));
String flag = "d-- ";
for (FileStatus fstatus : listStatus) {
if (fstatus.isFile()) flag = "f-- ";
System.out.println(flag + fstatus.getPath().getName());
System.out.println(fstatus.getPermission());
}
}
}
写MapReduce程序时,最后加一个判断当前输出路径是否存在的代码,如果输出路径存在则删除。这样可以避免出现如下错误:
Output directory hdfs://bigdata-sg-a-05:9000/output already exists
具体代码如下:
final static String OUTPUT_PATH = "hdfs://bigdata-sg-a-05:9000/output";
//输出路径用字符串表示,在主类中定义,或者由主方法参数给出
Path path = new Path(OUTPUT_PATH);
FileSystem fileSystem = path.getFileSystem(conf);
//getFileSystem()函数功能 Return the FileSystem that owns this Path.
if (fileSystem.exists(new Path(OUTPUT_PATH))) {
fileSystem.delete(new Path(OUTPUT_PATH),true);
在FileSystem 类下的delete()函数源码如下:
public boolean delete(Path f) throws IOException {
return delete(f, true);
}
/** Delete a file.
*
* @param f the path to delete.
* @param recursive if path is a directory and set to
* true, the directory is deleted else throws an exception. In
* case of a file the recursive can be set to either true or false.
* @return true if delete is successful else false.
* @throws IOException
*/
public abstract boolean delete(Path f, boolean recursive) throws IOException;
函数由两个参数,第一个为要删除文件的路径(类型Path),第二个函数为是否递归删除文件夹及其字目录(类型boolean),一般为true。删除失败抛出IOException异常。