您的位置:首页 > 运维架构

Hadoop快速实战(三)——HDFS原理

2017-02-13 17:16 239 查看

一、HDFS架构

主要包括:

NameNode

DataNode

SecondaryNameNode

NameNode工作原理

存储流程

1、客户端发送存储请求

2、服务端NameNode接收请求后,返回分配的DataNode节点

3、客户端收到信息后,将文件切分为多个block写入到DataNode

4、DataNode将得到的blk同步到其他DataNode形成备份,一般三份

5、如果DataNode备份失败,会将信息返回给NameNode,NameNode将重新分配备份服务器,确保备份三份



每一个block的基本信息(例如:存储路径),以元数据的形式存储在NameNode上。元数据相当于是所有文件的目录,既小又多。如何保证元数据的快速、稳定显得非常重要!


NameNode管理元数据原理

1、客户端发送存储请求

2、NameNode往edits log中记录元数据操作日志

3、NameNode返回分配的DataNode节点

4、客户端向DataNode写文件

5、写完之后,发送信息通知NameNode

6、NameNode将edits log中的元数据更新到内存中。

7、每当editslog写满时,需要将新的元数据刷到fsimage文件中。

*edits log非常小,只有几十兆,每次存满后都会flush到fsimage中永久保存

*fsimage中的元数据和NameNode内存中的元数据是基本保持一致的,如果不一致,查看edits log



edits log如何刷到fsimage中,原理解析:

1、当NameNode发现edits log已经写满,通知SN(SecondaryNameNode),执行checkpoint(合并)操作。

2、SN通知NameNode,停止往edits文件中写数据。新的元数据写到edits.new中。

3、SN下载NameNode中的edits文件和fsimage文件(http get)

4、SN合并edits和fsimage,形成新文件fsimage.checkpoint

5、上传新的fsimage.checkpoint给NameNode

6、fsimage.checkpoint替换原来的fsimage,edits.new替换原来的edits

*checkpoint触发条件,既可以是大小,也可以是时间,默认edits达到64M时执行,默认间隔时间3600秒。



上述的HDFS存储流程,以及元数据的管理机制,可以说是比较稳定的了。但是整个架构NameNode只有一台,一旦宕机,则导致整个服务无法继续对外提供。

DataNode工作原理

DataNode提供真实文件数据的存储服务。

基本概念:

block:基本存储单位(文件块)。如果一个文件小于一个block大小,并不占用整个文件块的存储空间。一个block的默认大小是128M,可以通过配置文件修改。

可以动手上传一个文件试试看,如果大于128M,一定会被切分的,切分后的文件可以手动合并回来

$ cat blk2 >blk1


二、linux下安装eclipse并配置环境

导入hdfs的jar包,导入依赖,导入common的jar包,导入common的依赖









三、HDFS操作实例

写一个HdfsUtil的Class文件,编写一个下载的方法



fs就是HDFS的客户端

// org.apache.hadoop.configuration
// 用来读配置文件的,把key-value读到对象中方便获取
// 也可以直接设置参数conf.set(name,value);
Configuration conf = new Configuration();
// core-site.xml中配置过
conf.set("fs.defaultFS","hdfs://服务明名称:9000/")
// fs就是hdfs的客户端
FileSystem fs = FileSystem.get(conf);
Path src = new Path("hdfs://服务名称:9000/文件名称")
FSDataInputStream in = fs.open(src);

FileOutputStream os = new fileOutputStream("/本地目
4000
录/文件命名")
IOUtils.copy(in,os);


如果不在conf中设置fs.defaultFS,则拷贝core-site.xml到项目根目录下

在windows项目下用户不是linux下的用户,设置VM arguments

-DHADOOP_USER_NAME=hadoop





其中hadoop是用户名,我的是fangxin

可以通过交互界面查看hadoop文件内容



之前的实现太繁琐

上传直接使用fs.copyFromLocalFile(src,dst)实现

而身份认证直接使用

// 模拟用户,除了上述的配置方式,也可以通过代码解决
FileSystem.get(new URI("hdfs://默认地址"),conf,"身份")


代码如下:

before

FileSystem fs = null;

@Before
public void init() throws IOException, URISyntaxException, InterruptedException {
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://192.168.49.31:9000/");
// 根据配置信息,去获取一个具体文件系统的客户端操作实例对象
fs = FileSystem.get(new URI("hdfs://192.168.49.31:9000/"),conf,"fangxin");
}


这样就以hadoop的身份访问了。

上传

@Test
public void uploda() throws IOException {
fs.copyFromLocalFile(new Path("F:\\BaiduYunDownload\\aa.txt"),new Path("hdfs://192.168.49.31:9000/"));
}


下载

@Test
public void download() throws IOException {
fs.copyToLocalFile(new Path("/aa.txt"),new Path("f:/BaiduYunDownload/"));
}


创建文件夹

@Test
public void mkdir() throws IOException {
fs.mkdirs(new Path("/aaa/bbb/ccc"));
}


删除文件夹或文件

@Test
public void rmdir() throws IOException {
fs.delete(new Path("/aaa"),true);
}


迭代遍历文件

@Test
public void listFiles() throws IOException {
RemoteIterator<LocatedFileStatus> files = fs.listFiles(new Path("/"), true);
while(files.hasNext()){
LocatedFileStatus file = files.next();
Path filePath = file.getPath();
String fileName = filePath.getName();
System.out.println(fileName);
}
}


遍历当前目录下所有内容,不迭代

@Test
public void listStatus() throws IOException {
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for(FileStatus status: listStatus){
String name = status.getPath().getName();
System.out.println(name);
}
}


总结:listFiles列出的是文件信息,而且提供递归遍历

listStatus 可以列出文件和文件夹信息,但是不提供自带的递归遍历

FileSystem设计原理

FileSystem抽象类解耦了具体文件系统的实现



四、RPC远程过程调用

简单说:两个进程间的调用。

传统的controller和service是在一套程序中的,或者说一个项目中的,这样我们直接用service的实例完成我们的业务。但是考虑到如果controller和service分离到两台服务器上,怎么办呢?这时候我们是不可能直接在controller层创建service的实例的。

这时候我们只能通过网络通信。网络通信需要相关的网络编程的知识。

socket就是一种网络通信协议。

controller调用socket client,

socket client和socket server通信,

socket server反射实例,调用目标方法,获取结果

序列化结果,服务端再将结果通过socket传递给客户端

客户端的socket将结果返回给controller



这就是网络通信的基本思路。协议是tcp。

不同的框架对网络通信的处理不同,协议不同。但是其实都是网络通信的一种

webservice的协议是soap

jdk中的RMI

hadoop中的叫RPC。节点间的通信基本都使用RPC

hadoop中的rpc的调用非常的好,给人一种感觉就像在本地一样。

1、代理对象proxy和socket实现相同的接口

2、调用proxy的方法,例如登录。实际是走的socket通信的方法

3、服务端socket获取到信息,调用服务端目标方法

4、结果返回给客户端。



通过一个中间接口,加代理方式,实现类似于本地要用的风格。

RPC实例

hadoop的RPC框架存在于common包中,你可以不用hadoop但使用RPC

测试文件准备



一个实现,一个接口,一个启动,共三个文件。服务应该部署在服务端,我这里写在了linux虚拟服务器上。

接口

package com.rpc;

/**
* @author fangxin
* @description ${END}
* @date 2017/2/13 0013.
*/
public interface LoginService{
// 如果没有版本号,运行报错
static final long versionID = 1L;

String login(String username);
}


如果不写版本号,会报如下错误



实现

package com.rpc;

/**
* @author fangxin
* @description ${END}
* @date 2017/2/13 0013.
*/
public class LoginServiceImpl implements LoginService{

@Override
public String login(String username) {
return "welcome "+username;
}

}


启动类

package com.rpc;

import java.io.IOException;

import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.RPC.Server;

/**
* @author fangxin
* @description ${END}
* @date 2017/2/13 0013.
*/
public class Starter {
public static void main(String[] args) throws HadoopIllegalArgumentException, IOException {
RPC.Builder builder = new RPC.Builder(new Configuration());
builder.setBindAddress("192.168.49.31").setPort(10000).setProtocol(LoginService.class).setInstance(new LoginServiceImpl());
Server server = builder.build();
server.start();
}
}


准备好后,运行Starter中的main方法,login方法就支持调用了。

服务端准备好后,开始准备客户端

package com.rpc;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;

import java.io.IOException;
import java.net.InetSocketAddress;

/**
* @author fangxin
* @description ${END}
* @date 2017/2/13 0013.
*/
public class LoginController {
public static void main(String[] args) throws IOException {
LoginService proxy = RPC.getProxy(LoginService.class,1L,new InetSocketAddress("192.168.49.31",10000),new Configuration());
String result = proxy.login("angelababy");
System.out.println(result);
}
}


除了LoginController.java,客户端还要准备一个接口文件,这个文件应该和服务端完全一致,目录都要一样

RPC优点

有一些服务是长期对外提供服务的,传统的项目,一旦service改变,引用他的jar包的所有服务都要更新才行。而如果采用RPC则不需要了。

另外,可以将service单独抽离出来,这样如果service承载能力不够了,可以有针对性的扩大承载能力。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息