linux用户身份与群组记录的文件
cat /etc/group 查看用户组
cat /etc/shadow 查看个人密码
cat /etc/passwd 查看用户相关信息
这三个文件可以说是:账号,密码,群组 信息的的集中地!
w或who 查看用户在线信息和登录信息
eclipse 创建方法快捷键shift + alt + M
eclipse 创建局部变量快捷键 shift + alt + L
1.对hdfs的操作方式:hadoop fs xxx
hadoop fs -ls / 查看hdfs的根目录下的内容的
hadoop fs -lsr / 递归查看hdfs的根目录下的内容的
hadoop fs -mkdir /d1 在hdfs上创建文件夹d1
hadoop fs -put <linux source> <hdfs destination> 把数据从linux上传到hdfs的特定路径中
hadoop fs -get <hdfs source> <linux destination> 把数据从hdfs下载到linux的特定路径下
hadoop fs -text <hdfs文件> 查看hdfs中的文件
hadoop fs -rm 删除hdfs中文件
hadoop fs -rmr 删除hdfs中的文件夹
hadoop fs -ls hdfs://zebra:9000/
2.HDFS的datanode在存储数据时,如果原始文件大小>64MB,按照64MB大小切分;如果<64MB,只有一个block,占用磁盘空间是源文件实际大小。
下面学习搭建Hadoop开发环境(伪分布模式),练习HDFS 的java访问方法,具体搭建方法网上很多,这里不是多说,有一点需要注意,就是我们本地跑hadoop项目需要将它的权限注释掉,避免权限错误,报权限异常,做法就是对包core里的org.apache.hadoop.fs.FileUtil 类的 checkReturnValue 方法内容注释,如下:
private static void checkReturnValue(boolean rv, File p,
FsPermission permission
) throws IOException {
// //权限屏蔽,zebra
// if (!rv) {
// throw new IOException("Failed to set permissions of path: " + p +
// " to " +
// String.format("%04o", permission.toShort()));
// }
}
接着我们讲HDFS的java操作,这就需要使用FileSystem api读写数据,它是org.apache.hadoop.fs.FileSystem,这是我们用户代码操作 HDFS 的直接入口,该类含有操作 HDFS 的各种方法,类似于 jdbc 中操作数据库的直接入口是Connection 类。
详细的读写操作,我在下面的代码中逐个说明。这里附上自己的操作代码:
package com.zeb;
import java.io.ByteArrayInputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class fileSystemApi {
private static final String uri = "hdfs://zebra:9000/";
//org.apache.hadoop.fs.FileSystem是一个重要的api,他是用户操作hdfs的直接入口,类似与jdbc操作数据库的Connection类。
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
//fileSystem的静态方法get传递两个值给形式参数,第一个是访问HDFS的地址,该地址的协议是hdfs,
//ip是zebra:9000。,这个地址的完整信息在配置文件core-site.xml中指定,第二个参数是一个配置对象
FileSystem fs = FileSystem.get(URI.create(uri), conf);
//eclipse 创建方法快捷键shift + alt + M
//eclipse 创建局部变量快捷键 shift + alt + L
// //创建一个文件夹
// final String pathString ="/zhang1";
// Path path = new Path(pathString);
// boolean exits = fs.exists(path);
// if(!exits){//判断文件夹是否存在
// boolean result = fs.mkdirs(path);
// System.out.println(result);//返回布尔值,true or false.
// }
// //创建一个文件,并写入一些信息
// final String pathString = "/zhang/zebraText";
// Path path = new Path(pathString);
// //FSDataOutputStream调用 create 方法创建一个通向 HDFS 的输出流;
// final FSDataOutputStream fdos = fs.create(path);
// //通过调用 hadoop 的一个工具类 IOUtils 的静态方法 copyBytes 把一个字符串发
// //送给输出流中。该静态方法有四个参数,第一个参数输入流,第二个参数是输出流,第三个
// //参数是配置对象,第四个参数是布尔值,如果是 true 表示数据传输完毕后关闭流。
// IOUtils.copyBytes(new ByteArrayInputStream("This is the first try!".getBytes()), fdos, conf, true);
// //读取刚才写入的文件信息
// final String pathString = "/zhang/zebraText";
// //FSDataInputStream调用方法 open 打开一个指定的文件,返回值是一个通向该文件的输入流;
// final FSDataInputStream fdis = fs.open(new Path(pathString));
// //通过调用 IOUtils.copyBytes 方法,输出的目的地是控制台。
// IOUtils.copyBytes(fdis, System.out, conf, true);
//查看目录文件列表和文件详细信息
// final String pathString = "/";
// Path path = new Path(pathString);
// //调用listStatus方法会得到一个指定路径下的所有文件和文件夹,每一个用FileStatus表示。我
// //们使用for循环显示每一个FileStatus对象。FileStatus对象表示文件的详细信息,里面含有类型、
// //副本数、权限、长度、路径等很多信息
// final FileStatus[] listStatus = fs.listStatus(path);
// for(FileStatus fstatus : listStatus){
// final String type = fstatus.isDir()?"目录":"文件";
// final short replication = fstatus.getReplication();
// final String permission = fstatus.getPermission().toString();
// final Long len = fstatus.getLen();
// final Path p = fstatus.getPath();
// System.out.println(type+"\t"+permission+"\t"+replication+"\t"+len+"\t"+p);
// }
// //删除文件
// final String pathString = "/zhang/zebraText";
// Path path =new Path(pathString);
// //递归删除目录“/zhang”及下面的所有内容
// fs.delete(new Path("/zhang"), true);
// //删除文件/zhang/zebraText
fs.deleteOnExit(path);
}
}
HDFS的写数据过程分析:
我们通过 FileSystem 类可以操控 HDFS,那我们就从这里开始分析写数据到 HDFS 的过程。在我们向 HDFS 写文件的时候,调用的是 FileSystem.create(Path path)方法,我们查看这个方法的源码,通过跟踪内部的重载方法,可以找到它最终调用的抽象方法,如下源码。
/**
* Opens an FSDataOutputStream at the indicated Path with write-progress
* reporting.
* @param f the file name to open
* @param permission
* @param overwrite if a file with this name already exists, then if true,
* the file will be overwritten, and if false an error will be thrown.
* @param bufferSize the size of the buffer to be used.
* @param replication required block replication for the file.
* @param blockSize
* @param progress
* @throws IOException
* @see #setPermission(Path, FsPermission)
*/
public abstract FSDataOutputStream create(Path f,
FsPermission permission,
boolean overwrite,
int bufferSize,
short replication,
long blockSize,
Progressable progress) throws IOException;
这个方法是抽象类,没有实现。那么我们只能向他的子类寻找实现。FileSystem 有个子类是 DistributedFileSystem,在 我们的 伪分布环 境下使 用的就 是这个 类。我 们可以看 到DistributedFileSystem 的这个方法的实现(ctrl + T),如图。
public FSDataOutputStream create(Path f, FsPermission permission,
boolean overwrite,
int bufferSize, short replication, long blockSize,
Progressable progress) throws IOException {
statistics.incrementWriteOps(1);
return new FSDataOutputStream
(dfs.create(getPathName(f), permission,
overwrite, true, replication, blockSize, progress, bufferSize),
statistics);
}
注意它的返回值 FSDataOutputStream。这个返回值对象调用了自己的构造方法,构造方法的第一个参数是 dfs.create()方法。我们关注一下这里的 dfs 对象是谁,create 方法做了什么事情。现在进入这个方法的实现,如图。
/**
* Create a new dfs file with the specified block replication
* with write-progress reporting and return an output stream for writing
* into the file.
*
* @param src stream name
* @param permission The permission of the directory being created.
* If permission == null, use {@link FsPermission#getDefault()}.
* @param overwrite do not check for file existence if true
* @param createParent create missing parent directory if true
* @param replication block replication
* @return output stream
* @throws IOException
* @see ClientProtocol#create(String, FsPermission, String, boolean, short, long)
*/
public OutputStream create(String src,
FsPermission permission,
boolean overwrite,
boolean createParent,
short replication,
long blockSize,
Progressable progress,
int buffersize
) throws IOException {
checkOpen();
if (permission == null) {
permission = FsPermission.getDefault();
}
FsPermission masked = permission.applyUMask(FsPermission.getUMask(conf));
LOG.debug(src + ": masked=" + masked);
final DFSOutputStream result = new DFSOutputStream(src, masked,
overwrite, createParent, replication, blockSize, progress, buffersize,
conf.getInt("io.bytes.per.checksum", 512));
beginFileLease(src, result);
return result;
}
返回值正是创建的对象。这个类有什么神奇的地方吗?我们看一下他的源码,如图。
/**
* Create a new output stream to the given DataNode.
* @see ClientProtocol#create(String, FsPermission, String, boolean, short, long)
*/
DFSOutputStream(String src, FsPermission masked, boolean overwrite,
boolean createParent, short replication, long blockSize, Progressable progress,
int buffersize, int bytesPerChecksum) throws IOException {
this(src, blockSize, progress, bytesPerChecksum, replication);
computePacketChunkSize(writePacketSize, bytesPerChecksum);
try {
// Make sure the regular create() is done through the old create().
// This is done to ensure that newer clients (post-1.0) can talk to
// older clusters (pre-1.0). Older clusters lack the new create()
// method accepting createParent as one of the arguments.
if (createParent) {
namenode.create(
src, masked, clientName, overwrite, replication, blockSize);
} else {
namenode.create(
src, masked, clientName, overwrite, false, replication, blockSize);
}
} catch(RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileAlreadyExistsException.class,
FileNotFoundException.class,
NSQuotaExceededException.class,
DSQuotaExceededException.class);
}
streamer.start();
}
可 以 看 到 , 这 个 类 是 DFSClient 的 内 部 类 。 在 类 内 部 通 过 调 用namenode.create()方法创建了一个输出流。我们再看一下 namenode 对象是什么类型,发现是 public final ClientProtocol namenode;是 ClientProtocal 接口。那么,这个对象是什么时候创建的那?如图
namenode 对象是在 DFSClient 的构造函数调用时创建的,即当 DFSClient 对象存在的时候,namenode 对象已经存在了。
至此,我们可以看到,使用 FileSystem 对象的 api 操纵 HDFS,其实是通过 DFSClient 对象访问 NameNode 中的方法操纵 HDFS 的。这里的 DFSClient 是 RPC 机制的客户端, NameNode是 RPC 机制的服务端的调用对象,整个调用过程如图。
在整个过程中,DFSClient 是个很重要的类,从名称就可以看出,他表示 HDFS 的 Client,是整个 HDFS 的 RPC 机制的客户端部分。我们对 HDFS 的操作,是通过 FileSsytem 调用的DFSClient 里面的方法。FileSystem 是封装了对 DFSClient 的操作,提供给用户使用的。HDFS的读操作也是这样,调用的open(path)方法,这里不多分析,今天就总结到这里。