zookeeper(2) 文件系統

  這一節咱們主要來看一下zookeeper文件系統的實現。java

樹結構apache

  爲了提升對指定節點的操做,zookeeper使用一個HashMap來存儲樹結構數據,key爲數據路徑,value爲節點數據。session

樹節點(DataNode)app

 1 public class DataNode implements Record {
 2     //父節點
 3     DataNode parent;
 4     //節點數據
 5     byte data[];
 6     //節點權限
 7     Long acl;
 8     //節點狀態信息
 9     public StatPersisted stat;
10     //子節點名
11     private Set<String> children = null;
12 
13 }
View Code

數節點狀態(StatPersisted)ide

 1 public class StatPersisted implements Record {
 2   //該節點建立是的事務xid
 3   private long czxid;
 4   //該節點最後一次修改的事務id
 5   private long mzxid;
 6   //建立時間
 7   private long ctime;
 8   //最後一次修改時間
 9   private long mtime;
10   //節點版本號
11   private int version;
12   //子節點版本號
13   private int cversion;
14   //acl版本號
15   private int aversion;
16   //是否爲零時節點
17   private long ephemeralOwner;
18   //子列表被修改的zxid
19   private long pzxid;
20 }
View Code

配額管理ui

  zookeeper在建立、修改節點時能夠設置特定路徑上的配額。在實現上,配額也存儲在文件系統中,而且還存儲了節點當前的信息。配額的控制在特定的路徑下:/zookeeper/quota/{節點路徑}/zookeeper_limits 節點的限制數據節點;/zookeeper/quota/{節點路徑}/zookeeper_stats  節點的當前量節點。一個節點路徑中只能有一個配額限制。this

  當在對某個節點進行操做時,咱們須要知道該路徑下的哪一個節點設置了配額,由於樹結構使用hashmap來存儲,因此不便於經過路徑查找,因此使用了一個樹結構來表示那個節點上配置了限額。spa

配額路徑(PathTrie)3d

  1 public class PathTrie {
  2     //根節點
  3     private final TrieNode rootNode ;
  4     //節點
  5     static class TrieNode {
  6         //是否設置配額,false沒有,true有
  7         boolean property = false;
  8         //子節點
  9         final HashMap<String, TrieNode> children;
 10         //父節點
 11         TrieNode parent = null;
 12         
 13      
 14         //刪除子節點配額
 15         void deleteChild(String childName) {
 16             synchronized(children) {
 17                 if (!children.containsKey(childName)) {
 18                     return;
 19                 }
 20                 TrieNode childNode = children.get(childName);
 21                 //若是子節點沒有本身點直接刪除,不然設置property爲false
 22                 if (childNode.getChildren().length == 1) { 
 23                     childNode.setParent(null);
 24                     children.remove(childName);
 25                 }
 26                 else {
 27                     childNode.setProperty(false);
 28                 }
 29             }
 30         }
 31     }
 32     //新增配額節點
 33     public void addPath(String path) {
 34         if (path == null) {
 35             return;
 36         }
 37         String[] pathComponents = path.split("/");
 38         TrieNode parent = rootNode;
 39         String part = null;
 40         if (pathComponents.length <= 1) {
 41             throw new IllegalArgumentException("Invalid path " + path);
 42         }
 43         for (int i=1; i<pathComponents.length; i++) {
 44             part = pathComponents[i];
 45             if (parent.getChild(part) == null) {
 46                 parent.addChild(part, new TrieNode(parent));
 47             }
 48             parent = parent.getChild(part);
 49         }
 50         parent.setProperty(true);
 51     }
 52     
 53     //刪除配額節點
 54     public void deletePath(String path) {
 55         if (path == null) {
 56             return;
 57         }
 58         String[] pathComponents = path.split("/");
 59         TrieNode parent = rootNode;
 60         String part = null;
 61         if (pathComponents.length <= 1) { 
 62             throw new IllegalArgumentException("Invalid path " + path);
 63         }
 64         for (int i=1; i<pathComponents.length; i++) {
 65             part = pathComponents[i];
 66             if (parent.getChild(part) == null) {
 67                 return;
 68             }
 69             parent = parent.getChild(part);
 70         }
 71         TrieNode realParent  = parent.getParent();
 72         realParent.deleteChild(part);
 73     }
 74     
 75     //獲取指定路徑上配額節點最大路徑
 76     public String findMaxPrefix(String path) {
 77         if (path == null) {
 78             return null;
 79         }
 80         if ("/".equals(path)) {
 81             return path;
 82         }
 83         String[] pathComponents = path.split("/");
 84         TrieNode parent = rootNode;
 85         List<String> components = new ArrayList<String>();
 86         if (pathComponents.length <= 1) {
 87             throw new IllegalArgumentException("Invalid path " + path);
 88         }
 89         int i = 1;
 90         String part = null;
 91         StringBuilder sb = new StringBuilder();
 92         //最大路徑的index
 93         int lastindex = -1;
 94         while((i < pathComponents.length)) {
 95             if (parent.getChild(pathComponents[i]) != null) {
 96                 part = pathComponents[i];
 97                 parent = parent.getChild(part);
 98                 components.add(part);
 99                 if (parent.getProperty()) {
100                     lastindex = i-1;
101                 }
102             }
103             else {
104                 break;
105             }
106             i++;
107         }
108         for (int j=0; j< (lastindex+1); j++) {
109             sb.append("/" + components.get(j));
110         }
111         return sb.toString();
112     }
113 }
View Code

監聽器管理code

  zookeeper能夠對指定路徑進行監聽,當指定路徑發生變化時,監聽器會執行響應的動做。主要是經過將path和watcher創建關聯關係,在對指定路徑進行操做是調用相應監聽器方法。

監聽管理器(WatchManager)

 1 public class WatchManager {
 2     //key爲path value爲該path對應的watcher集合
 3     private final HashMap<String, HashSet<Watcher>> watchTable =
 4         new HashMap<String, HashSet<Watcher>>();
 5     //key爲watcher value爲該watcher對應的path集合,使用兩個hashmap來維護路徑和監聽器是由於watcher和路徑是多對多關係,這樣不管經過watcher仍是路徑均可以很快找到對應的路徑和watcher。
 6     private final HashMap<Watcher, HashSet<String>> watch2Paths =
 7         new HashMap<Watcher, HashSet<String>>();
 8     
 9     public synchronized void addWatch(String path, Watcher watcher) {
10         //新增watcher到watchTable
11         HashSet<Watcher> list = watchTable.get(path);
12         if (list == null) {
13             list = new HashSet<Watcher>(4);
14             watchTable.put(path, list);
15         }
16         list.add(watcher);
17         //新增watcher到watch2Paths
18         HashSet<String> paths = watch2Paths.get(watcher);
19         if (paths == null) {
20             paths = new HashSet<String>();
21             watch2Paths.put(watcher, paths);
22         }
23         paths.add(path);
24     }
25     
26     public synchronized void removeWatcher(Watcher watcher) {
27         //從watch2Paths和watchTable刪除watcher
28         HashSet<String> paths = watch2Paths.remove(watcher);
29         if (paths == null) {
30             return;
31         }
32         for (String p : paths) {
33             HashSet<Watcher> list = watchTable.get(p);
34             if (list != null) {
35                 list.remove(watcher);
36                 if (list.size() == 0) {
37                     watchTable.remove(p);
38                 }
39             }
40         }
41     }
42     //觸發watcher
43     public Set<Watcher> triggerWatch(String path, EventType type, Set<Watcher> supress) {
44         WatchedEvent e = new WatchedEvent(type,
45                 KeeperState.SyncConnected, path);
46         HashSet<Watcher> watchers;
47         synchronized (this) {
48             //zookeeper的通知是一次性的,也就是說若是一個路徑觸發通知後,相應的watcher會從這兩個hashmap中刪除。
49             watchers = watchTable.remove(path);
50             for (Watcher w : watchers) {
51                 HashSet<String> paths = watch2Paths.get(w);
52                 if (paths != null) {
53                     paths.remove(path);
54                 }
55             }
56         }
57         for (Watcher w : watchers) {
58             if (supress != null && supress.contains(w)) {
59                 continue;
60             }
61             w.process(e);
62         }
63         return watchers;
64     }
65 }
View Code

臨時節點

  zookeeper中有一類節點在建立的session結束後會被清除掉,zookeeper在建立這些節點時會記錄節點和session 的對應關係,到session結束是,刪除這些節點。

結束session(DataTree.killSession)

 1 //session與零時節點對應關係
 2 private final Map<Long, HashSet<String>> ephemerals =
 3         new ConcurrentHashMap<Long, HashSet<String>>();
 4 //關閉session
 5 void killSession(long session, long zxid) {
 6         //session結束後,刪除零時節點
 7         HashSet<String> list = ephemerals.remove(session);
 8         if (list != null) {
 9             for (String path : list) {
10                 try {
11                     deleteNode(path, zxid);
12                 } catch (NoNodeException e) {
13                     LOG.warn("Ignoring NoNodeException for path " + path
14                             + " while removing ephemeral for dead session 0x"
15                             + Long.toHexString(session));
16                 }
17             }
18         }
19     }
View Code

權限管理

  zookeeper的每一個節點都會存儲該節點能夠訪問的用戶已經能夠執行的操做。

 權限(ACL)

 1 public class ACL implements Record {
 2   //perms即權限,有五種權限:READ(可讀);WRITE(可寫);CREATE(可建立子節點);DELETE(可刪除子節點);ADMIN(管理權限);perms的每一位表明一種權限。
 3   private int perms;
 4   //id是受權的對象。
 5   private org.apache.zookeeper.data.Id id;
 6 }
 7 public class Id implements Record {
 8   //scheme是權限模式,有五種模式:digest(經過用戶名密碼,id爲user:password);auth();ip(經過ip,id爲ip地址);world(固定用戶爲anyone,爲全部Client端開放權限 );super(對應的id擁有超級權限)。
 9   private String scheme;
10   private String id;
11 }
View Code

每一個節點能夠設置多個權限,實際節點權限只存儲一個整數,對應的acl信息保存在兩個hashmap中。(DataTree.java)

1 public final Map<Long, List<ACL>> longKeyMap = new HashMap<Long, List<ACL>>();
2 public final Map<List<ACL>, Long> aclKeyMap = new HashMap<List<ACL>, Long>();
View Code

節點操做

View Code
相關文章
相關標籤/搜索