目录
引言
目前项目中部分数据接入了Hadoop中的hdfs文件系统。对hdfs提出了以下几个需求,首先用户需要能够正常地访问hdfs文件系统,这便需要对hdfs文件系统中关于用户权限进行处理。其次是由于可能存在并发地提交任务,所以需要在客户端设置一个线程池应对高并发的情况。数据文件提交后,当然需要能够查看文件的结构以及各个节点的状态。
权限管理
hdfs文件系统采用与linux相似的文件系统权限模型,也就是UGO模型。当接收一个用户时,会通过namenode调用shell命令,查看namenode服务器用户组系统。根据组列表判断该用户所拥有的权限。
上图中我们新创建一个testLimit目录, 默认情况下,新创建目录会赋权为755,对于新创建的文件则会赋权为644,可以看到该目录的所有者是master用户,并且属于supergroup。
shell命令
新创建一个用户dell,尝试对hdfs文件系统进行创建目录操作,可以看到我们被拒绝了,为了解决这个问题,考虑将dell用户加入到supergroup组中。
# 在linux本机创建supergroup用户组,在root用户下执行
usermod -a -G supergroup dell
# 同步本机用户组信息到hdfs
hdfs dfsadmin -refreshUserToGroupsMappings
# 切换到dell用户
su - dell
# 尝试在hdfs中创建目录
hdfs dfs -mkdir /testLimist2
# 查看hdfs
hdfs dfs -ls /
上面的操作我们也可以使用java api来实现,使用jSch连接到linux服务器,然后通过channel来执行shell命令。
/*
连接linux服务器
*/
public Session getConnect () {
// 初始化jsch
JSch jSch = new JSch();
// 初始化session
Session session = null;
try {
// 建立连接
session = jSch.getSession("root", "192.168.139.100", 22);
session.setPassword("123456");
session.setConfig("StrictHostKeyChecking", "no");
// 启用连接
session.connect();
System.out.println("服务器连接成功");
} catch (JSchException e) {
System.out.println("服务器连接失败");
throw new RuntimeException(e);
}
return session;
}
/*
关闭连接
*/
public void closeConnection(Session session) {
try {
session.disconnect();
System.out.println("关闭连接成功");
}catch (Exception e){
e.printStackTrace();
System.out.println("关闭连接失败");
}
}
/*
/*
使用脚本语言将用户添加到superGroup
*/
public List<String> setGroup(Session session, List<String > stringList) {
// 存放命令返回值
List<String> outputList = new ArrayList<>();
for (String command: stringList) {
// 创建通道
Channel channel = null;
try {
// 创建执行通道
channel = session.openChannel("exec");
// 设置命令
((ChannelExec) channel).setCommand(command);
// 连接通道
channel.connect();
// 获取输出
InputStream in = channel.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// 存放命令的结果
String line;
// 用lines拼接line结果
StringBuffer lines = new StringBuffer();
while((line = reader.readLine()) != null) {
line.trim();
lines = lines.append(lines);
}
// 如果没有返回值,则输出没有返回值
if (String.valueOf(lines).equals("")){
outputList.add("命令["+command+"]执行成功,但没有返回值");
}else {
outputList.add(String.valueOf(lines));
}
reader.close();
channel.disconnect();
} catch (JSchException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return outputList;
}
hdfs API
使用hadoop java api对待操作目录的权限进行修改,该操作需要在管理员用户情况下才能使用。
/*
修改目录权限
*/
public void modifyDir(String aimDir) {
// 配置类
Configuration conf = new Configuration();
// 设master用户的ugi
conf.set("hadoop.job.ugi", "dell");
// 设置远程连接的用户
UserGroupInformation ugi = UserGroupInformation.createRemoteUser("dell");
try {
// 使用doAs执行操作
ugi.doAs((PrivilegedExceptionAction<Void>)() ->{
// 创建文件对象
FileSystem fs = FileSystem.get(conf);
// 对testLimit目录更新权限
fs.setPermission(new Path(aimDir), new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL));
System.out.printf("%s 权限修改成功\n", aimDir);
return null;
});
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
线程池管理
在实际应用中,常常会存在大量客户端访问服务端的情况,如果每有一个客户端请求就创建一个线程,不仅会造成系统资源的消耗,还极易造成系统的不稳定。而线程池能够重复利用已创建线程,并且在任务达到时不需要等待线程创建便能够执行,因此考虑使用线程池解决多个客户端并发访问Hadoop集群。
public void ThreadPool() {
// 初始线程池 设置线程数
ExecutorService executorService = Executors.newFixedThreadPool(20);
long begin = System.currentTimeMillis();
// 将线程池中提交100次查看文件的任务
for (int i = 1; i <= 100; i++) {
executorService.submit(new Request("/test/input/a.txt"));
}
long end = System.currentTimeMillis();
executorService.shutdown();
System.out.printf("花费时间为 %d", end - begin);
}
创建一个用于操作hdfs的Request类
public class Request implements Runnable{
private String filepath;
public Request(String filepath) {
this.filepath = filepath;
}
public void readFile(String filePath){
// 实例化HdfsFs
HdfsFS hdfsFS = new HdfsFS();
// 调用方法连接到hdfs文件系统
FileSystem fs = hdfsFS.getFileSystem();
// 初始化FsDataInputStream
FSDataInputStream in = null;
try {
// 打开a.txt文件按
in = fs.open(new Path(filePath));
// 输出文件中的内容
IOUtils.copyBytes(in, System.out, 2048, false);
// 关闭文件FsDataInputStream
IOUtils.closeStream(in);
// 关闭文件系统
fs.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
this.readFile(this.filepath);
}
public String getFilepath() {
return filepath;
}
public void setFilepath(String filepath) {
this.filepath = filepath;
}
}
文件系统管理
在linux系统中,我们可以通过hdfs提供一些命令来查看文件系统,如`-ls`、`-cat`等。但是我们想要通过前端界面中一个交互式按钮来实现该功能,那么就需要使用到Hadoop提供的Java API。主要使用到FileSystem中的listStatus函数。
package testJavaAPI;
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 java.io.IOException;
public class FSOperation {
// 查看指定文件目录下的文件结构
public FileStatus[] getFileStructure() {
// 待查看的路径
String pathName = "hdfs://192.168.139.100:9000/";
// 配置类
Configuration conf = new Configuration();
// 初始化文件系统
FileSystem fs = null;
// 初始化路径系统
Path path = new Path(pathName);
// 通过文件系统查看路径下的内容
try {
// 为文件系统传入conf
fs = FileSystem.get(conf);
// 调用status查看目标路径下的文件系统情况
FileStatus[] fileStatuses = fs.listStatus(path);
// 遍历查看目录下文件的组织情况
for (FileStatus fileStatus: fileStatuses) {
// 取出path属性
String[] filePathList = fileStatus.getPath().toString().split("/");
String filePath = "/" + filePathList[filePathList.length - 1];
// 设置path需要改为的值
Path pathTemp = new Path(filePath);
// 修改path的内容 path=hdfs://192.168.139.100:9000/home => path=/home;
fileStatus.setPath(pathTemp);
// 查看输出 FileStatus{path=/home; isDirectory=true; modification_time=1698108051829; access_time=0; owner=master; group=supergroup; permission=rwxr-xr-x; isSymlink=false}
//System.out.println(fileStatus);
}
return fileStatuses;
} catch (IOException e) {
System.out.println(e);
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
FSOperation fsOperation = new FSOperation();
fsOperation.getFileStructure();
}
}
集群管理
虽然hdfs为我们提供了web可以查看各个节点的状态,但是项目需要装这些状态展示到前端,能够实时显示各个节点的健康状态、使用容量、剩余容量等。所以使用到FileSystem与DistributedFileSystem来获取到各个节点的详细状态,但是要注意的是想要使用该功能需要使用管理员用户登录到hdfs。
public void getDataNodeInformation(){
// 配置类
Configuration conf = new Configuration();
//
try {
// 初始化文件系统
FileSystem fs = null;
try {
fs = FileSystem.get(new URI("hdfs://192.168.139.100:9000"), conf, "master");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
// 初始化分布式文件系统
DistributedFileSystem hdfs = (DistributedFileSystem)fs;
// 查看datanode情况
DatanodeInfo[] datanodeInfos = hdfs.getDataNodeStats();
for (DatanodeInfo datanodeInfo: datanodeInfos) {
// 获取节点名称
System.out.println("Name:" + datanodeInfo.getHostName());
System.out.println("IP:" + datanodeInfo.getIpAddr());
System.out.println("Status:" + datanodeInfo.getAdminState());
System.out.println("Capacity:" + datanodeInfo.getDfsUsed());
System.out.println("Used:" + datanodeInfo.getDfsUsed());
System.out.println("Remaining:" + datanodeInfo.getRemaining());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}