DataNode内幕(一):数据存储和它的小伙伴们

 大家好,今天我们开始学习DataNode,一个相对于分布式客户端,较为复杂的一个hadoop集群的VIP会员
DataNode:数据节点,在分布式集群中和真正和数据打交道的一个对象,它的职责就是存储数据和读取数据,维护它所管理的数据
节点:简单的说,一个网络拓扑节点,在一个集群内,我们将一台计算机或者一台虚拟机称之为一个节点,同时一台机器(计算机和虚拟机的统称)可以属于多个集群,对于交叉集群部署(一台机器上被部署了多个集群)的情况,等会我会详细介绍如何区分 这个节点到底属于哪个集群。而且hadoop是如何解决交叉部署中出现"争夺资源"的情况
首先大致了解下在DataNode工作过程中,与之相关的一些成员。
这些成员从作用来分大致分为三类:
A:与DataNode存储有关
B:与文件系统数据集的工作机制有关
C:与DataNode工作线程有关

设计背景:
根据软件开发的原则,数据节点的业务逻辑不会在数据节点的文件结构上直接操作,当需要对磁盘上的数据进行操作时,业务逻辑只需要调用管理这些文件结构的对象提供的服务即可,数据节点的文件结构管理包括两部分内容:数据存储DataStorage和文件系统数据集FSDataset。


A:成员介绍:
a:DataStorage:其父是抽象类Storage,其祖父是StorageInfo

-----------------------------------------------------------------------------------------------------------
StorageInfo
位于 org.apache.hadoop.hdfs.server.common 包  
/**存储系统信息结构的版本号**/
  public int   layoutVersion;  
  
  /**存储系统的唯一标识符**/
  public int   namespaceID;    
  
  /**该存储系统信息的创建时间**/
  public long  cTime;    
     
//构造器
public StorageInfo () {
    this(0, 0, 0L);
  }
---------------------------------------------------------------------------------------------------------
Storage
位于 org.apache.hadoop.hdfs.server.common 包  
较为重要的成员介绍 :

 /**用此后缀名 表示独占该文件**/
  private   static final String STORAGE_FILE_LOCK     = "in_use.lock";

 protected static final String STORAGE_FILE_VERSION  = "VERSION";
  public static final String STORAGE_DIR_CURRENT   = "current";
  private   static final String STORAGE_DIR_PREVIOUS  = "previous";
  private   static final String STORAGE_TMP_REMOVED   = "removed.tmp";
  private   static final String STORAGE_TMP_PREVIOUS  = "previous.tmp";
  private   static final String STORAGE_TMP_FINALIZED = "finalized.tmp";
  private   static final String STORAGE_TMP_LAST_CKPT = "lastcheckpoint.tmp";
  private   static final String STORAGE_PREVIOUS_CKPT = "previous.checkpoint";  
 
 
/**标识是NameNode还是DataNode**/
  private NodeType storageType;    // Type of the node using this storage 
  
  protected List<StorageDirectory> storageDirs = new ArrayList<StorageDirectory>();
-------------------------------------------------
内部类 
StorageDirectory 
/**
   * 提供存储目录上的一些通用操作
   * @author Administrator
   *
   */
  public class StorageDirectory {
/**保存着存储目录的根**/  
    File              root; 
    
     /**文件锁,这里使用的是 独占文件锁**/
    FileLock          lock;
    
     /**保存目录对应的类型**/
    StorageDirType dirType; 
    ----------------
两个构造器 :
    public StorageDirectory(File dir) {
      // default dirType is null
      this(dir, null);
    }
    
    public StorageDirectory(File dir, StorageDirType dirType) {
      this.root = dir;
      this.lock = null;
      this.dirType = dirType;
    }  
-------------------
    
 public void lock() throws IOException {
      this.lock = tryLock();
      if (lock == null) {
        String msg = "Cannot lock storage " + this.root 
          + ". The directory is already locked.";
        LOG.info(msg);
        throw new IOException(msg);
      }
    }
----------------------
    /**
     * 对文件的加锁,防止出现交叉部署中 争夺资源的情况
     * @return
     * @throws IOException
     */
    FileLock tryLock() throws IOException {
    // 在存储目录的根目录下 ,新建一个 锁文件,注意,这里的根与linux系统里面的根 含义不同,这里指存储目录的根,由配置文件指定
      File lockF = new File(root, STORAGE_FILE_LOCK);
      
      /**独占该文件的数据节点停止时,删除此"in_use.lock"文件**/
      lockF.deleteOnExit();
      
      /**可以访问任意文件的任意地方,提供文件的随机读写功能**/
      RandomAccessFile file = new RandomAccessFile(lockF, "rws");
      FileLock res = null;
      try {
      //锁住文件,独占访问,
      //采用file.getChannel().tryLock()可以避免删除文件或者移动文件造成的"in_use.lock"文件丢失后,tryLocK()程序判断逻辑失效
        res = file.getChannel().tryLock();
      } catch(OverlappingFileLockException oe) {
        file.close();
        return null;
      } catch(IOException e) {
        LOG.info(StringUtils.stringifyException(e));
        file.close();
        throw e;
      }
      return res;
    }
-----------------
    /**
     * Unlock storage.
     * 
     * @throws IOException
     */
    public void unlock() throws IOException {
      if (this.lock == null)
        return;
      this.lock.release();
      lock.channel().close();
      lock = null;
    }
  }  
 ------------------------------------------------------------------------------------------------------
 DataStorage
位于
org.apache.hadoop.hdfs.server.datanode 包下  
重要成员介绍:
 final static String BLOCK_SUBDIR_PREFIX = "subdir";
  final static String BLOCK_FILE_PREFIX = "blk_";
  final static String COPY_FILE_PREFIX = "dncp_";
--------------
三个构造器
  DataStorage() {
    super(NodeType.DATA_NODE);
    storageID = "";
  }
  
  DataStorage(int nsID, long cT, String strgID) {
    super(NodeType.DATA_NODE, nsID, cT);
    this.storageID = strgID;
  }
  
  public DataStorage(StorageInfo storageInfo, String strgID) {
    super(NodeType.DATA_NODE, storageInfo);
    this.storageID = strgID;
  }  
----------------------------------
 
 /**
   * 数据节点第一次启动时,会调用DataStorage.format()创建存储目录结构,
   * ,如果数据节点管理多个目录,这个方法会调用多次,在不同的目录下 创建节点文件结构
   * @param sd
   * @param nsInfo 从名字节点返回的NamespaceInfo,携带了存储系统标识namespaceID等信息,该标识最终放在VERSION文件中
   * @throws IOException
   */
  void format(StorageDirectory sd, NamespaceInfo nsInfo) throws IOException {
 
//删除原有目录及数据,并重新创建目录  ,
    sd.clearDirectory();
     //然后为VERSION文件中的属性赋值并将其 持久化到磁盘
    this.layoutVersion = FSConstants.LAYOUT_VERSION;
    this.namespaceID = nsInfo.getNamespaceID();
    this.cTime = 0;
     // store storageID as it currently is
    sd.write();
  }
--------------------------------------------------
/**
   * 升级
   * Move current storage into a backup directory,
   * and hardlink all its blocks into the new current directory.
   * 
   * @param sd  storage directory
   * @throws IOException
   */
  void doUpgrade(StorageDirectory sd,
                 NamespaceInfo nsInfo
                 ) throws IOException {
    LOG.info("Upgrading storage directory " + sd.getRoot()
             + ".\n   old LV = " + this.getLayoutVersion()
             + "; old CTime = " + this.getCTime()
             + ".\n   new LV = " + nsInfo.getLayoutVersion()
             + "; new CTime = " + nsInfo.getCTime());
    
     //获得当前版本目录
    File curDir = sd.getCurrentDir();
     //获得上一个版本目录
    File prevDir = sd.getPreviousDir();
    
     //当前目录,必须存在,否则无法升级
    assert curDir.exists() : "Current directory must exist.";
     /**
     * 如果prevDir目录存在,则需要删除该目录,注意,该删除操作相当于提交了上一次升级,
     * 同时保证了HDFS最多保留前一版本数据的要求(删除上一个版本)
     */
    
    if (prevDir.exists())
      deleteDir(prevDir);
    
     //获得上一个版本的临时目录
    File tmpDir = sd.getPreviousTmp();
     //保存 tmpDir 不存在,tmpDir目录只能在升级过程中出现
    assert !tmpDir.exists() : "previous.tmp directory must not exist.";
     //目录改名: current ———> tmp 首先将"current"目录改变成"previous.tmp
    rename(curDir, tmpDir);
     /**
     * 支持升级回滚,就必须保留升级前的数据,在数据节点,就是保存数据块以及数据块的校验信息文件,
     * 在doUpgrade()中,保留升级前数据是通过建立文件硬链接实现的
     */
    
    /***
     * 硬链接:是一种特殊的文件系统机制,它允许一个文件可以有多个名称,
     * 当一个文件有多个名称时,删除其中的一个名称,并不会删除文件数据,
     * 只有所有的文件名都被删除后,文件系统才会真正删除文件数据
     */
    linkBlocks(tmpDir, curDir, this.getLayoutVersion());
     // 写新版本的version文件
    this.layoutVersion = FSConstants.LAYOUT_VERSION;
    assert this.namespaceID == nsInfo.getNamespaceID() :
      "Data-node and name-node layout versions must be the same.";
    this.cTime = nsInfo.getCTime();
    sd.write();
     // 最后将 "previous.tmp" 改名为 "previous"完成升级
    rename(tmpDir, prevDir);
     /**
     * 最后数据节点的存储空间会有"previous"和"current"两个目录,
     * 而且,"previous"和 "current"包含了同样的数据块和数据块校验信息文件,
     * 但它们有各自的version文件,升级过程中需要的"privious.tmp"目录已经消失 
     */
    LOG.info("Upgrade of " + sd.getRoot()+ " is complete.");
  } 
  





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值