hadoop – hdfs base operation (Java api )

package com.billstudy.hdfs.test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

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.FsUrlStreamHandlerFactory;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.Before;
import org.junit.Test;

/**
 * Test hdfs operation (CRUD)
 * @author Bill
 * @since V1.0 2015年3月22日 - 上午9:45:40
 */
public class HadoopJunitTest {
	
	
	private final String HDFS_BASE_PATH = "hdfs://h21:9000/";
	
	private FileSystem fs = null;
	
	private final Configuration configuration = new Configuration();
	
	@Before
	public void before() throws Exception{
		// register supports hdfs protocol 
		URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());

		// create file system by uri and conf
		URI uri = new URI(HDFS_BASE_PATH);
		fs = FileSystem.get(uri, configuration);
	}
	
	
	@Test
	public void testMkdir() throws IOException, URISyntaxException{
		final Path path = new Path("/hey");
		
		// if not exists create 
		boolean exists = fs.exists(path);
		System.out.println(path.getName() + "-exists:" + exists);
		if (!exists) {
			fs.mkdirs(path); 
		}
		fs.close();
	}
	
	@Test
	public void testPut() throws Exception{
		FSDataOutputStream outSteam = fs.create(new Path("/hey/hi-hadoop.txt"), true);
		
		// inputSteam , outputStream, configuration , copy success is close
		IOUtils.copyBytes(
					new ByteArrayInputStream("hello hadoop ~".getBytes()),
					outSteam, 
					configuration, 
					true);
		fs.close();
	}
	
	@Test
	public void testGet() throws Exception{
		FSDataInputStream inSteam = fs.open(new Path("/hey/hi-hadoop.txt"));
		// read file print to console
		IOUtils.copyBytes(inSteam, System.out, configuration, true);
		fs.close();
	}
	
	@Test
	public void testListFile() throws Exception{
		FileStatus[] listStatus = fs.listStatus((new Path("/")));
		for (FileStatus f : listStatus) {
			System.out.println(
						  ( f.isDir() ? "dir" : "file" ) + "\t"
						+ ( f.getAccessTime() )			 + "\t"
						+ ( f.getBlockSize() ) 			 + "\t"
						+ ( f.getGroup() ) 			 	 + "\t"
						+ ( f.getLen() ) 				 + "\t"
						+ ( f.getModificationTime() ) 	 + "\t"
						+ ( f.getReplication() ) 		 + "\t"
						+ ( f.getPermission() ) 		 + "\t"
						+ ( f.getPath().getName() )		 + "\t"
					);

		}
	}
	
	
	@Test
	public void testDelete() throws IOException{
		Path path = new Path("/hey");
		// recursive delete , likeness shell rmr 
		//fs.delete(path, true);
		
		fs.deleteOnExit(path);
		
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

hadoop设计基础和目标-笔记01

  1. 硬件错误是常态,因此需要冗余.
  2. 流式数据访问,即数据批量读写而非随机读写,Hadoop擅长做的数据分析而不是事务处理
  3. 大规模数据集
  4. 简单一致性模型。为了降低系统复杂度,对文件采用一次性写多次读的逻辑设计,也就是说:文件一经过写入,关闭就再也不能修改!
  5. 程序采用“数据就近”原则分配节点执行.
NameNode:
1.管理文件系统的命名空间
2.记录每个文件数据块在各个DateNode上的位置和副本信息
3.协调客户端对文件的访问
4.记录命名空间内的改动和空间本身属性的改动
5.NameNode使用事务日志记录HDFS元数据的变化。使用映像文件的命名空间,包括文件映射,文件属性
            
DataNode:
1.负责所在物理节点的存储管理
2.一次写入,多次读取(不修改,也是就不需要处理读写一致性的问题)
3.文件由数据块组成,典型的块大小是64MB。
4.数据块尽量散布到各个节点,实现副本冗余的效果。

读取数据流程:
1.客户端要访问hdfs文件
2.首先从NameNode获取组成这个文件的数据块位置列表
3.根据列表知道存储数据块的DataNode
4.​访问DataNode获取数据
5.NameNode不参与实际数据传输,只是起到查询作用。

HDFS的可靠性:
1.冗余副本策略
    hdfs-site.xml设置复制因子指定副本数量,所有数据块都有副本,DataNode启动时,遍历本地文件系统,产生一份HDFS数据块和本地文件的对应关系列表(BlockReport)汇报给NameNode.
    复制因子越大越安全,但是空间利用率低。
2.机架策略
    集群一般放在不同机架上,机架间带宽要比机架内带宽要小。HDFS的“机架感知”,一般在本机架存放一个副本,在其他机架再存放别的副本,这样可以防止机架失效丢失数据,也可以提高带宽利用率。

3.心跳机制
    NameNode周期性从DataNode接收心跳信号和块报告(BlockReport),NameNode根据块报告验证元数据。
    没有按时发送心跳的DataNode会被标记为宕机,不会再给它任何I/O请求.如果DataNode失效造成副本数量下降,并且低于预先设置的阀值,NameNode会检测出这些数据块,并在合适的时间进行重新复制,引发重新复制的原因还包括数据副本本身损坏、磁盘错误、复制因子备增大等。
4.安全模式
    NameNode在启动时会经过一个”安全模式”(SafeMode)阶段,此阶段不会产生数据读写.在此阶段NameNode收集各个DataNode的报告,当数据块达到最小副本数以上时,会备认为是“安全”的.
    在一定比例(可设置)的数据库被确定为“安全”后,再过若干时间,安全模式结束。当检测到副本数不足的数据块时,该块会被复制直到达到最小副本数。
5.校验和
    在文件创建时,每个数据块都会产生校验和, 校验和会作为一个隐藏文件保存在命名空间下,客户端获取数据时可以检查校验和是否相同,从而发现数据库是否损坏。如果正在读取的数据块损坏,则可以继续读取其他副本。
6.回收站
    删除文件时,其实是放入回收站(Trash).回收站里的文件可以快速恢复,可以设置一个时间阀值,当回收站里文件的存放时间超过这个阀值,就会彻底删除并释放占用的数据块。
7.元数据保护
    映像文件和事务日志是NameNode的核心数据,可以配置为拥有多个副本。副本会降低NameNode的处理速度,但增加安全性,NameNode依然是单点,如果发生故障要手工切换.
8.快照机制(0.20.2还没实现)
    支持存储摸个时间点的映象,需要时可以使数据重返时间点的状态

版权声明:本文为博主原创文章,未经博主允许不得转载。

HDFS客户端的权限错误:Permission denied

搭建了一个Hadoop的环境,Hadoop集群环境部署在几个Linux服务器上,现在想使用windows上的Java客户端来操作集群中的HDFS文件,但是在客户端运行时出现了如下的认证错误,被折磨了几天,问题终得以解决。以此文记录问题的解决过程。

如果想看最终解决问题的方法拉到最后,如果想看我的问题解决思路请从上向下看

问题描述

上传文件的代码:

  1.     private static void uploadToHdfs() throws FileNotFoundException,IOException {
  2.         //我的文件地址
  3.         String localSrc = “E:\\快盘\\技术文档\\hadoop\\HDFS初步研究.pdf”;
  4.         //存放在云端的目的地址
  5.         String dest = “hdfs://192.168.2.156:9000/user/HDFS初步研究.pdf”;
  6.         InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
  7.         //得到配置对象
  8.         Configuration conf = new Configuration();
  9. //        conf.set(“fs.default.name”,”hdfs://192.168.2.156:9000″);
  10.         //文件系统
  11.         FileSystem fs = FileSystem.get(URI.create(dest), conf);
  12.         //输出流
  13.         OutputStream out = fs.create(new Path(dest), new Progressable() {
  14.             @Override
  15.             public void progress() {
  16.                 System.out.println(“上传完一个设定缓存区大小容量的文件!”);
  17.             }
  18.         });
  19.         //连接两个流,形成通道,使输入流向输出流传输数据
  20.         IOUtils.copyBytes(in, out, 4096true);
  21.     }

错误的详细描述如下:

org.apache.hadoop.security.AccessControlException: org.apache.hadoop.security .AccessControlException: Permission denied: user=Administrator, access=WRITE, inode=”hadoop”: hadoop:supergroup:rwxr-xr-x

其实这个错误的原因很容易看出来,用户Administator在hadoop上执行写操作时被权限系统拒绝.

解决问题的过程

看到这个错误的,第一步就是将这个错误直接入放到百度google里面进行搜索。找到了N多篇文章,但是主要的思路就如此篇文章所写的两个解决办法:http://www.cnblogs.com/acmy/archive/2011/10/28/2227901.html

1、在hdfs的配置文件中,将dfs.permissions修改为False

2、执行这样的操作 hadoop fs -chmod 777 /user/hadoop

对于上面的第一个方法,我试了行不通,不知道是自己设置错误还是其他原因,对我此法不可行,第二个方法可行。第二个方法是让我们来修改HDFS中相应文件夹的权限,后面的/user/hadoop这个路径为HDFS中的文件路径,这样修改之后就让我们的administrator有在HDFS的相应目录下有写文件的权限(所有的用户都是写权限)。

虽然上面的第二步可以解决问题了,上传之后的文件所有者为Administrator,但是总感觉这样的方法不够优雅,而且这样修改权限会有一定的安全问题,总之就是看着不爽,就在想有没有其他的办法?

问题分析

开始仔细的观察了这个错误的详细信息,看到user=Administrator, access=WRITE。这里的user其实是我当前系统(运行客户端的计算机的操作系统)的用户名,实际期望这里的user=hadoop(hadoop是我的HADOOP上面的用户名),但是它取的是当前的系统的用户名,很明显,如果我将当前系统的用户名改为hadoop,这个肯定也是可以行得通的,但是如果后期将开发的代码部署到服务器上之后,就不能方便的修改用户,此方法明显也不够方便。

现在就想着Configuration这个是一个配置类,有没有一个参数是可以在某个地方设置以哪个用户运行呢?搜索了半天,无果。没有找到相关的配置参数。

最终只有继续分析代码, FileSystem fs = FileSystem.get(URI.create(dest), conf);代码是在此处开始对HDFS进行调用,所以就想着将HADOOP的源码下下来,debug整个调用过程,这个user=Administator是在什么时间赋予的值。理解了调用过程,还怕找不到解决问题的办法么?

跟踪代码进入 FileSystem.get–>CACHE.get()–>Key key = new Key(uri, conf);到这里的时候发现key值里面已经有Administrator了,所以关键肯定是在new key的过程。继续跟踪UserGroupInformation.getCurrentUser()–>getLoginUser()–>login.login()到这一步的时候发现用户名已经确定了,但是这个方法是Java的核心源码,是一个通用的安全认证,但对这一块不熟悉,但是debug时看到subject里面有NTUserPrincipal:Administator,所以就想着搜索一下这个东西是啥,结果就找到了下面这一篇关键的文章:

http://www.udpwork.com/item/7047.html

在此篇文章里面作者分析了hadoop的整个登录过程,对于我有用的是其中的这一段:

2.login.login();
这个会调用HadoopLoginModule的login()和commit()方法。
HadoopLoginModule的login()方法是一个空函数,只打印了一行调试日志 LOG.debug(“hadoop login”);
commit()方法负责把Principal添加到Subject中。
此时一个首要问题是username是什么?
在使用了kerberos的情况下,从javax.security.auth.kerberos.KerberosPrincipal的实例获取username。
在未使用kerberos的情况下,优先读取HADOOP_USER_NAME这个系统环境变量,如果不为空,那么拿它作username。否则,读取HADOOP_USER_NAME这个java环境变量。否则,从com.sun.security.auth.NTUserPrincipal或者com.sun.security.auth.UnixPrincipal的实例获取username。
如果以上尝试都失败,那么抛出异常LoginException(“Can’t find user name”)。
最终拿username构造org.apache.hadoop.security.User的实例添加到Subject中。

看完这一段,我明白了执行login.login的时候调用了hadoop里面的HadoopLoginModule方法,而关键是在commit方法里面,在这里优先读取HADOOP_USER_NAME系统环境变量,然后是java环境变量,如果再没有就从NTUserPrincipal等里面取。关键代码为:

  1. if (!isSecurityEnabled() && (user == null)) {
  2.   String envUser = System.getenv(HADOOP_USER_NAME);
  3.   if (envUser == null) {
  4.     envUser = System.getProperty(HADOOP_USER_NAME);
  5.   }
  6.   user = envUser == null ? null : new User(envUser);
  7. }

OK,看到这里我的需求也就解决了,只要在系统的环境变量里面添加HADOOP_USER_NAME=hadoop(HDFS上的有权限的用户,具体看自己的情况),或者在当前JDK的变量参数里面添加HADOOP_USER_NAME这个Java变量即可。我的情况添加系统环境变量更方法。

如果是在Eclipse里面运行,修改完环境变量后,记得重启一下eclipse,不然可能不会生效。

解决办法

最终,总结下来解决办法大概有三种:

1、在系统的环境变量或java JVM变量里面添加HADOOP_USER_NAME,这个值具体等于多少看自己的情况,以后会运行HADOOP上的Linux的用户名。(修改完重启eclipse,不然可能不生效)

2、将当前系统的帐号修改为hadoop

3、使用HDFS的命令行接口修改相应目录的权限,hadoop fs -chmod 777 /user,后面的/user是要上传文件的路径,不同的情况可能不一样,比如要上传的文件路径为hdfs://namenode/user/xxx.doc,则这样的修改可以,如果要上传的文件路径为hdfs://namenode/java/xxx.doc,则要修改的为hadoop fs -chmod 777 /java或者hadoop fs -chmod 777 /,java的那个需要先在HDFS里面建立Java目录,后面的这个是为根目录调整权限。

我喜欢第一个方法。http://www.huqiwen.com/2013/07/18/hdfs-permission-denied/

hdfs.DFSClient: DataStreamer Exception: org.apache.hadoop.ipc.RemoteException: java.io.IOException:

情况:master(h21) JobTracker,SecondaryNameNode,NameNode都启动了。  

slave(h22,h23)的TaskTracker,DataNode也都启动了。 但是在master(h21)机器上看,却没有可用的DateNode节点。 hdfs(put 命令)上传文件的时候也会报错

 网上说清理data ,tmp文件夹,检查硬盘可用空间,重新格式化,以及退出safemode 都试过了。 错误还是没一点变化

拿着权威指南又看了一遍,发现是自己的core-site.xml配置错了。  我以为是每台机器都要配置自己的hostname. 原来是要all slaves都要配置master 的ip:port ..  解决了。。 下楼吃东西去 

解决方案:core-site.xml里面配置master的ip和port

版权声明:本文为博主原创文章,未经博主允许不得转载。

Centos 6.4 修改HOSTNAME

由于需要搭建多台机器集群,就把机器名按照统一命名规则修改了下。机器(centos 6.4)下面是修改步骤

1.hostname 修改

执行:hostname newHostName

2.network文件

执行:vi /etc/sysconfig/network

将内部:HASTNAME=newHostName

3.hosts文件

若是静态IP,则添加个ip对应newHostName即可。

版权声明:本文为博主原创文章,未经博主允许不得转载。

Error: No suitable device found: no device found for connection ‘System eth0’

在redhat和centos6.0以后的版本,如果我们用虚拟机装过一个系统后想克隆一个虚拟机用的话,我们会发现clone虚拟机网卡起不来,报一下错误:

Error: No suitable device found: no device found for connection ‘System eth0’.或者

Bringing up interface eth0:  Device eth0 does not seem to be present, delaying initialization.

同时我们执行ifconfig命令的时候,会发现显示eth1网卡网卡配置文件却为/etc/sysconfig/network-scripts/ifcfg-eth0。

解决方法:

centos—>删除这个 /etc/udev/rules.d/70-persistent-net.rules,重启后把这个文件里NAME=”eth1″的eth1改成eth0,同时也要修改ATTR{address}里的mac地址和/etc/sysconfig/network-scripts/ifcfg-eth0文件mac地址一致。
redhat—>redhat有点麻烦,redhat需要把/etc/sysconfig/network-scripts/ifcfg-eth0这个文件重命名为/etc/sysconfig/network-scripts/ifcfg-eth1,同时修改文件里面的DEVICE=eth0为DEVICE=eth1,然后把/etc/udev/rules.d/70-persistent-net.rules

文件中NAME=”eth0″的删掉,重启网络服务器正常,只不过没有eth0,而是eth1.如果想改成eth0需要重启后把上述三个位置都修改后再重启。

本文出自 “面对自己” 博客,请务必保留此出处http://angus717.blog.51cto.com/1593644/829007

/user/libexec/gconf-sanity-check-2 exited with status 256

把虚拟机(centos 6.4)克隆了下,结果发现起不来了。

错误提示:

There is a problem with the configuration server(/usr/libexec/gconf-sanity-check-2 exited with status 256)  

于是,关机重启进入单用户模式,将/tmp/ 清干净,chmod 777 .  后能正常启动了。   

版权声明:本文为博主原创文章,未经博主允许不得转载。

eclipse 启动一闪而过启动不起来,Job found still running after platform shutd

eclipse 一闪而过解决方案:

 

 
【我用的是黄色高亮方法】
原帖地址 : http://www.metod.si/job-found-still-running-after-platform-shutdown-eclipse/

Job found still running after platform shutdown eclipse

Posted March 15th, 2012 in Razno by Metod

If your eclipse crashed and refuses to start, saying something along the lines of: Job found still running after platform shutdown. Jobs should be canceled by the plugin that scheduled them during shutdown, then
try the following.

  1. rm-rf YOUR_PROJECT_DIR/.metadata/.plugins/org.eclipse.core.resources

Start eclipse. However, all your projects will be gone.

You can import them via File ->Import ->Existing Projects into Workspace;then select the directory where you had your projects.

EDIT: I just found a better way. (source)

  1. rm YOUR_PROJECT_DIR/.metadata/.plugins/org.eclipse.core.resources/.snap

This way you do not need to reimport your projects.

ANOTHER WAY:

As IGx89 suggested
in the comments
, you can also try the following:

  1. rm YOUR_PROJECT_DIR/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi

http://stackoverflow.com/questions/11310970/eclipse-juno-doesnt-start/12045547#12045547