View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.replication;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.concurrent.CopyOnWriteArrayList;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.classification.InterfaceAudience;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.hbase.Abortable;
30  import org.apache.hadoop.hbase.Stoppable;
31  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
32  import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener;
33  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
34  import org.apache.zookeeper.KeeperException;
35  
36  /**
37   * This class is a Zookeeper implementation of the ReplicationTracker interface. This class is
38   * responsible for handling replication events that are defined in the ReplicationListener
39   * interface.
40   */
41  @InterfaceAudience.Private
42  public class ReplicationTrackerZKImpl extends ReplicationStateZKBase implements ReplicationTracker {
43  
44    private static final Log LOG = LogFactory.getLog(ReplicationTrackerZKImpl.class);
45    // All about stopping
46    private final Stoppable stopper;
47    // listeners to be notified
48    private final List<ReplicationListener> listeners =
49        new CopyOnWriteArrayList<ReplicationListener>();
50    // List of all the other region servers in this cluster
51    private final ArrayList<String> otherRegionServers = new ArrayList<String>();
52    private final ReplicationPeers replicationPeers;
53  
54    public ReplicationTrackerZKImpl(ZooKeeperWatcher zookeeper,
55        final ReplicationPeers replicationPeers, Configuration conf, Abortable abortable,
56        Stoppable stopper) {
57      super(zookeeper, conf, abortable);
58      this.replicationPeers = replicationPeers;
59      this.stopper = stopper;
60      this.zookeeper.registerListener(new OtherRegionServerWatcher(this.zookeeper));
61      this.zookeeper.registerListener(new PeersWatcher(this.zookeeper));
62    }
63  
64    @Override
65    public void registerListener(ReplicationListener listener) {
66      listeners.add(listener);
67    }
68  
69    @Override
70    public void removeListener(ReplicationListener listener) {
71      listeners.remove(listener);
72    }
73  
74    /**
75     * Return a snapshot of the current region servers.
76     */
77    @Override
78    public List<String> getListOfRegionServers() {
79      refreshOtherRegionServersList();
80  
81      List<String> list = null;
82      synchronized (otherRegionServers) {
83        list = new ArrayList<String>(otherRegionServers);
84      }
85      return list;
86    }
87  
88    /**
89     * Watcher used to be notified of the other region server's death in the local cluster. It
90     * initiates the process to transfer the queues if it is able to grab the lock.
91     */
92    public class OtherRegionServerWatcher extends ZooKeeperListener {
93  
94      /**
95       * Construct a ZooKeeper event listener.
96       */
97      public OtherRegionServerWatcher(ZooKeeperWatcher watcher) {
98        super(watcher);
99      }
100 
101     /**
102      * Called when a new node has been created.
103      * @param path full path of the new node
104      */
105     @Override
106     public void nodeCreated(String path) {
107       refreshListIfRightPath(path);
108     }
109 
110     /**
111      * Called when a node has been deleted
112      * @param path full path of the deleted node
113      */
114     @Override
115     public void nodeDeleted(String path) {
116       if (stopper.isStopped()) {
117         return;
118       }
119       boolean cont = refreshListIfRightPath(path);
120       if (!cont) {
121         return;
122       }
123       LOG.info(path + " znode expired, triggering replicatorRemoved event");
124       for (ReplicationListener rl : listeners) {
125         rl.regionServerRemoved(getZNodeName(path));
126       }
127     }
128 
129     /**
130      * Called when an existing node has a child node added or removed.
131      * @param path full path of the node whose children have changed
132      */
133     @Override
134     public void nodeChildrenChanged(String path) {
135       if (stopper.isStopped()) {
136         return;
137       }
138       refreshListIfRightPath(path);
139     }
140 
141     private boolean refreshListIfRightPath(String path) {
142       if (!path.startsWith(this.watcher.rsZNode)) {
143         return false;
144       }
145       return refreshOtherRegionServersList();
146     }
147   }
148 
149   /**
150    * Watcher used to follow the creation and deletion of peer clusters.
151    */
152   public class PeersWatcher extends ZooKeeperListener {
153 
154     /**
155      * Construct a ZooKeeper event listener.
156      */
157     public PeersWatcher(ZooKeeperWatcher watcher) {
158       super(watcher);
159     }
160 
161     /**
162      * Called when a node has been deleted
163      * @param path full path of the deleted node
164      */
165     @Override
166     public void nodeDeleted(String path) {
167       List<String> peers = refreshPeersList(path);
168       if (peers == null) {
169         return;
170       }
171       if (isPeerPath(path)) {
172         String id = getZNodeName(path);
173         LOG.info(path + " znode expired, triggering peerRemoved event");
174         for (ReplicationListener rl : listeners) {
175           rl.peerRemoved(id);
176         }
177       }
178     }
179 
180     /**
181      * Called when an existing node has a child node added or removed.
182      * @param path full path of the node whose children have changed
183      */
184     @Override
185     public void nodeChildrenChanged(String path) {
186       List<String> peers = refreshPeersList(path);
187       if (peers == null) {
188         return;
189       }
190       LOG.info(path + " znode expired, triggering peerListChanged event");
191       for (ReplicationListener rl : listeners) {
192         rl.peerListChanged(peers);
193       }
194     }
195   }
196 
197   /**
198    * Verify if this event is meant for us, and if so then get the latest peers' list from ZK. Also
199    * reset the watches.
200    * @param path path to check against
201    * @return A list of peers' identifiers if the event concerns this watcher, else null.
202    */
203   private List<String> refreshPeersList(String path) {
204     if (!path.startsWith(getPeersZNode())) {
205       return null;
206     }
207     return this.replicationPeers.getAllPeerIds();
208   }
209 
210   private String getPeersZNode() {
211     return this.peersZNode;
212   }
213 
214   /**
215    * Extracts the znode name of a peer cluster from a ZK path
216    * @param fullPath Path to extract the id from
217    * @return the id or an empty string if path is invalid
218    */
219   private String getZNodeName(String fullPath) {
220     String[] parts = fullPath.split("/");
221     return parts.length > 0 ? parts[parts.length - 1] : "";
222   }
223 
224   /**
225    * Reads the list of region servers from ZK and atomically clears our local view of it and
226    * replaces it with the updated list.
227    * @return true if the local list of the other region servers was updated with the ZK data (even
228    *         if it was empty), false if the data was missing in ZK
229    */
230   private boolean refreshOtherRegionServersList() {
231     List<String> newRsList = getRegisteredRegionServers();
232     if (newRsList == null) {
233       return false;
234     } else {
235       synchronized (otherRegionServers) {
236         otherRegionServers.clear();
237         otherRegionServers.addAll(newRsList);
238       }
239     }
240     return true;
241   }
242 
243   /**
244    * Get a list of all the other region servers in this cluster and set a watch
245    * @return a list of server nanes
246    */
247   private List<String> getRegisteredRegionServers() {
248     List<String> result = null;
249     try {
250       result = ZKUtil.listChildrenAndWatchThem(this.zookeeper, this.zookeeper.rsZNode);
251     } catch (KeeperException e) {
252       this.abortable.abort("Get list of registered region servers", e);
253     }
254     return result;
255   }
256 }