利用zookeeper模擬實現HA高可用

                           利用zookeeper模擬實現HA高可用

 

1、需求

在分佈式場景中,對於主從架構來說,最大的問題就是單點故障。當學過zookeeper之後,我們都知道,可以利用zookeeper集羣來幫助實現Hadoop的HA,那到底Hadoop的HA是如何實現的呢?

 

2、實現思路

zookeeper給我們提供了兩個非常重要的組件:

1、znode系統:提供了存儲關鍵數據的能力

2、監聽機制:提供了監聽感興趣數據變化的能力

利用zookeeper的這兩點能力,我們實現HA

 

3、具體實現功能

1、當開始啓動namenode的時候,所有剛啓動的namenode都需要去爭搶成爲active的namenode,沒有爭搶成功的則成爲standby的狀態

2、當active的namenode死掉之後,需要剩下的所有的stanby都需要去爭搶成爲active的狀態

 

4、具體代碼實現

package com.ghgj.zookeeper.zkapp1903;

import org.apache.zookeeper.*;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;

import java.util.List;

/**
 * 需求:
 * 模擬實現HA
 * <p>
 * 具體的功能:
 * 1、當開始啓動namenode的時候,所有剛啓動的namenode都需要去爭搶成爲active的namenode
 * 沒有爭搶成功的則成爲standby的狀態
 * 2、當active的namenode死掉之後,需要剩下的所有的stanby都需要去爭搶成爲active的狀態
 * <p>
 * 在這個模擬實現中,假定所有的namenode之間的數據狀態都是同步的。沒有數據狀態差別
 * <p>
 * 分析實現思路:
 * 見代碼註釋
 */
public class NameNodeHA {

  // 連接信息
  private static final String CONNECT_STR = "hadoop02:2181,hadoop03:2181";

  // 會話超時時長   會話建立成功最長的等待時間
  private static final int TIME_OUT = 5000;

  // 存儲active namenode的父級znode節點
  private static final String ACTIVE_PARENT = "/namenode_active";
  // 存儲standby namenode的父級znode節點
  private static final String STANBY_PARENT = "/namenode_standbys";
  // 鎖節點
  private static final String LOCK_ZNODE = "/namenode_lock";

  // 當前上線的節點名稱
  private static final String NAMENODE_HOST = "hadoop02";

  static ZooKeeper zookeeper = null;

  public static void main(String[] args) throws Exception {

    /**
     * 獲取連接
     *
     * 關於監聽器的知識:
     * 有兩種添加監聽的方式:
     * 1、通過會話對象添加,這個會話對象中的所有的相應都能接收到,都在這個監聽器對象中的process
     * 方法中執行業務邏輯的回調
     * 	在獲取會話的時候添加的監聽是屬於全局監聽,當前這個會話中的任何事件響應,都會回調這個監聽器對象中的
     * process方法
     *
     * 2、在對應的三種添加監聽的方式中,注入自定義的監聽對象,那麼注入的監聽器對象是誰,
     * 當事件響應的時候,就回調這個監聽器對象中的process方法
     */
    zookeeper = new ZooKeeper(CONNECT_STR, TIME_OUT, new Watcher() {

      @Override
      public void process(WatchedEvent event) {
        // 哪個znode節點
        String path = event.getPath();
        // 事件的類型
        EventType type = event.getType();

        // 如果是 ACTIVE_PARENT 的  NodeChildrenChanged 事件
        // 當active namenode死掉或者增加都會觸發process回調
        if (path.equals(ACTIVE_PARENT) && type == EventType.NodeChildrenChanged) {

          try {

            // 爭搶成爲active namenode
            List<String> onlyAtiveNM = zookeeper.getChildren(ACTIVE_PARENT, null);
            if (onlyAtiveNM.size() == 0) {
              // 原來的active namenode死掉了
              // 正式實現:爭搶成爲active namenode
              // 搶鎖: 使用創建一個znode來模式實現搶鎖,誰創建成功就是誰獲取到了這把鎖

              // 註冊監聽
              // 關注 LOCK_ZNODE 的  NodeCreated 事件
              zookeeper.exists(LOCK_ZNODE, true);

              // 創建鎖節點
              // 觸發了 LOCK_ZNODE 的  NodeCreated 事件
              if (zookeeper.exists(LOCK_ZNODE, false) == null) {
                zookeeper.create(LOCK_ZNODE, NAMENODE_HOST.getBytes(),
                        Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
              }

            } else if (onlyAtiveNM.size() == 1) {
              // 相當於已經有一個anmenode把自己切換成爲active了
              // 所以不需要做什麼操作
            }
          } catch (Exception e) {
            e.printStackTrace();
          }

        } else if (path.equals(LOCK_ZNODE) && type == EventType.NodeCreated) {
          String namenode_lock_znode = null;
          try {
            // 真正的來判斷,誰創建成功的鎖節點,如果是自己創建成功的,則切換自己的狀態成爲 active
            byte[] data = zookeeper.getData(LOCK_ZNODE, false, null);
            // namenode_lock_znode
            // 這個對象 namenode_lock_znode 就是誰創建成功的那個namenode的節點名稱
            namenode_lock_znode = new String(data, "UTF-8");
          } catch (Exception e) {
            e.printStackTrace();
          }

          // 需要判斷,是否是自己創建成功的鎖節點
          if (NAMENODE_HOST.equals(namenode_lock_znode)) {
            // 是自己創建成功的鎖節點
            // 切換自己的狀態

            try {
              // 首先刪除自己在  STANDBY_PERENT節點下的該表自己的znode
              String deletePath = STANBY_PARENT + "/" + NAMENODE_HOST;
              if (zookeeper.exists(deletePath, false) != null) {
                zookeeper.delete(deletePath, -1);
              }

              // 再創建一個znode節點在ACTIVE_PARNET下面
              String createPath = ACTIVE_PARENT + "/" + NAMENODE_HOST;
              zookeeper.create(createPath, NAMENODE_HOST.getBytes(),
                      Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);

              System.out.println(NAMENODE_HOST + " 註冊成爲active角色");

            } catch (InterruptedException e) {
              e.printStackTrace();
            } catch (KeeperException e) {
              e.printStackTrace();
            }
          } else {

            // 判斷得出結果 鎖節點不是自己創建成功,不要成爲active
            // 什麼都不做
          }
        }
      }
    });


    /**
     * 執行各種操作
     */

    // 確保兩個父節點存在
    if (zookeeper.exists(ACTIVE_PARENT, null) == null) {
      zookeeper.create(ACTIVE_PARENT, "storage active namenode data".getBytes(),
              Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }
    if (zookeeper.exists(STANBY_PARENT, null) == null) {
      zookeeper.create(STANBY_PARENT, "storage standby namenode data".getBytes(),
              Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    // 先來判斷是否有active的namenode
    List<String> activeNM = zookeeper.getChildren(ACTIVE_PARENT, null);
    if (activeNM.size() == 1) {

      System.out.println(activeNM.get(0) + " 節點是active角色, 自己 " + NAMENODE_HOST + "成爲standby角色");

      // 如果有active的namenode, 則自動成爲 standby的namenode
      // 到 STANBY_PARENT 這個znode節點下,創建一個子節點代表當前這個standby namenode
      String standByPath = STANBY_PARENT + "/" + NAMENODE_HOST;
      zookeeper.create(standByPath, NAMENODE_HOST.getBytes(),
              Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);

      // 註冊監聽:關注現在的active namenode 是否死掉
      // ACTIVE_PARENT 的  NodeChildrenChanged 事件
      // 關心是否 active 的namenode 死掉
      zookeeper.getChildren(ACTIVE_PARENT, true);
    } else {

      // 當發現沒有active的namenode的時候:
      // 先爭搶鎖
      // 爭搶鎖爭搶到了的話,就切換自己的狀態
      // 註冊監聽
      // 關注 LOCK_ZNODE 的  NodeCreated 事件
      zookeeper.exists(LOCK_ZNODE, true);

      // 創建鎖節點
      // 觸發了 LOCK_ZNODE 的  NodeCreated 事件
      zookeeper.create(LOCK_ZNODE, NAMENODE_HOST.getBytes(),
              Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
      System.out.println("發現沒有active,去爭搶成爲" + NAMENODE_HOST + " 節點成爲standby節點");
    }

    /**
     * 關閉連接
     */
    while (true) {
      try {
        Thread.sleep(10000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

 

5、執行效果

當hadoop02節點啓動:

當hadoop03啓動的時候:

當hadoop04啓動的時候:

當現在爲active的hadoop02宕機的時候:

當hadoop02被宕機的時候,發現最終,hadoop03和hadoop04爭搶成爲active角色,最後發現hadoop04競爭成功成爲active角色

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章