View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.zookeeper;
19  
20  import java.io.IOException;
21  import java.io.InterruptedIOException;
22  
23  import org.apache.hadoop.hbase.Abortable;
24  import org.apache.hadoop.hbase.HConstants;
25  import org.apache.hadoop.hbase.ServerName;
26  import org.apache.hadoop.hbase.classification.InterfaceAudience;
27  import org.apache.hadoop.hbase.exceptions.DeserializationException;
28  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
29  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
30  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
31  import org.apache.zookeeper.KeeperException;
32  import org.apache.zookeeper.data.Stat;
33  
34  import com.google.protobuf.InvalidProtocolBufferException;
35  
36  /**
37   * Manages the location of the current active Master for the RegionServer.
38   * <p>
39   * Listens for ZooKeeper events related to the master address. The node
40   * <code>/master</code> will contain the address of the current master.
41   * This listener is interested in
42   * <code>NodeDeleted</code> and <code>NodeCreated</code> events on
43   * <code>/master</code>.
44   * <p>
45   * Utilizes {@link ZooKeeperNodeTracker} for zk interactions.
46   * <p>
47   * You can get the current master via {@link #getMasterAddress()} or via
48   * {@link #getMasterAddress(ZooKeeperWatcher)} if you do not have a running
49   * instance of this Tracker in your context.
50   * <p>
51   * This class also includes utility for interacting with the master znode, for
52   * writing and reading the znode content.
53   */
54  @InterfaceAudience.Private
55  public class MasterAddressTracker extends ZooKeeperNodeTracker {
56    /**
57     * Construct a master address listener with the specified
58     * <code>zookeeper</code> reference.
59     * <p>
60     * This constructor does not trigger any actions, you must call methods
61     * explicitly.  Normally you will just want to execute {@link #start()} to
62     * begin tracking of the master address.
63     *
64     * @param watcher zk reference and watcher
65     * @param abortable abortable in case of fatal error
66     */
67    public MasterAddressTracker(ZooKeeperWatcher watcher, Abortable abortable) {
68      super(watcher, watcher.getMasterAddressZNode(), abortable);
69    }
70  
71    /**
72     * Get the address of the current master if one is available.  Returns null
73     * if no current master.
74     * @return Server name or null if timed out.
75     */
76    public ServerName getMasterAddress() {
77      return getMasterAddress(false);
78    }
79  
80    /**
81     * Get the info port of the current master of one is available.
82     * Return 0 if no current master or zookeeper is unavailable
83     * @return info port or 0 if timed out
84     */
85    public int getMasterInfoPort() {
86      try {
87        final ZooKeeperProtos.Master master = parse(this.getData(false));
88        if (master == null) {
89          return 0;
90        }
91        return master.getInfoPort();
92      } catch (DeserializationException e) {
93        LOG.warn("Failed parse master zk node data", e);
94        return 0;
95      }
96    }
97    /**
98     * Get the info port of the backup master if it is available.
99     * Return 0 if no backup master or zookeeper is unavailable
100    * @param sn server name of backup master
101    * @return info port or 0 if timed out or exceptions
102    */
103   public int getBackupMasterInfoPort(final ServerName sn) {
104     String backupZNode = ZKUtil.joinZNode(watcher.backupMasterAddressesZNode, sn.toString());
105     try {
106       byte[] data = ZKUtil.getData(watcher, backupZNode);
107       final ZooKeeperProtos.Master backup = parse(data);
108       if (backup == null) {
109         return 0;
110       }
111       return backup.getInfoPort();
112     } catch (Exception e) {
113       LOG.warn("Failed to get backup master: " + sn + "'s info port.", e);
114       return 0;
115     }
116   }
117 
118   /**
119    * Get the address of the current master if one is available.  Returns null
120    * if no current master. If refresh is set, try to load the data from ZK again,
121    * otherwise, cached data will be used.
122    *
123    * @param refresh whether to refresh the data by calling ZK directly.
124    * @return Server name or null if timed out.
125    */
126   public ServerName getMasterAddress(final boolean refresh) {
127     try {
128       return ServerName.parseFrom(super.getData(refresh));
129     } catch (DeserializationException e) {
130       LOG.warn("Failed parse", e);
131       return null;
132     }
133   }
134 
135   /**
136    * Get master address.
137    * Use this instead of {@link #getMasterAddress()} if you do not have an
138    * instance of this tracker in your context.
139    * @param zkw ZooKeeperWatcher to use
140    * @return ServerName stored in the the master address znode or null if no
141    * znode present.
142    * @throws KeeperException 
143    * @throws IOException 
144    */
145   public static ServerName getMasterAddress(final ZooKeeperWatcher zkw)
146   throws KeeperException, IOException {
147     byte [] data;
148     try {
149       data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
150     } catch (InterruptedException e) {
151       throw new InterruptedIOException();
152     }
153     // TODO javadoc claims we return null in this case. :/
154     if (data == null){
155       throw new IOException("Can't get master address from ZooKeeper; znode data == null");
156     }
157     try {
158       return ServerName.parseFrom(data);
159     } catch (DeserializationException e) {
160       KeeperException ke = new KeeperException.DataInconsistencyException();
161       ke.initCause(e);
162       throw ke;
163     }
164   }
165 
166   /**
167    * Get master info port.
168    * Use this instead of {@link #getMasterInfoPort()} if you do not have an
169    * instance of this tracker in your context.
170    * @param zkw ZooKeeperWatcher to use
171    * @return master info port in the the master address znode or null if no
172    * znode present.
173    * // TODO can't return null for 'int' return type. non-static verison returns 0
174    * @throws KeeperException
175    * @throws IOException
176    */
177   public static int getMasterInfoPort(final ZooKeeperWatcher zkw) throws KeeperException,
178       IOException {
179     byte[] data;
180     try {
181       data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
182     } catch (InterruptedException e) {
183       throw new InterruptedIOException();
184     }
185     // TODO javadoc claims we return null in this case. :/
186     if (data == null) {
187       throw new IOException("Can't get master address from ZooKeeper; znode data == null");
188     }
189     try {
190       return parse(data).getInfoPort();
191     } catch (DeserializationException e) {
192       KeeperException ke = new KeeperException.DataInconsistencyException();
193       ke.initCause(e);
194       throw ke;
195     }
196   }
197 
198   /**
199    * Set master address into the <code>master</code> znode or into the backup
200    * subdirectory of backup masters; switch off the passed in <code>znode</code>
201    * path.
202    * @param zkw The ZooKeeperWatcher to use.
203    * @param znode Where to create the znode; could be at the top level or it
204    * could be under backup masters
205    * @param master ServerName of the current master must not be null.
206    * @return true if node created, false if not; a watch is set in both cases
207    * @throws KeeperException
208    */
209   public static boolean setMasterAddress(final ZooKeeperWatcher zkw,
210       final String znode, final ServerName master, int infoPort)
211   throws KeeperException {
212     return ZKUtil.createEphemeralNodeAndWatch(zkw, znode, toByteArray(master, infoPort));
213   }
214 
215   /**
216    * Check if there is a master available.
217    * @return true if there is a master set, false if not.
218    */
219   public boolean hasMaster() {
220     return super.getData(false) != null;
221   }
222 
223   /**
224    * @param sn must not be null
225    * @return Content of the master znode as a serialized pb with the pb
226    * magic as prefix.
227    */
228   static byte[] toByteArray(final ServerName sn, int infoPort) {
229     ZooKeeperProtos.Master.Builder mbuilder = ZooKeeperProtos.Master.newBuilder();
230     HBaseProtos.ServerName.Builder snbuilder = HBaseProtos.ServerName.newBuilder();
231     snbuilder.setHostName(sn.getHostname());
232     snbuilder.setPort(sn.getPort());
233     snbuilder.setStartCode(sn.getStartcode());
234     mbuilder.setMaster(snbuilder.build());
235     mbuilder.setRpcVersion(HConstants.RPC_CURRENT_VERSION);
236     mbuilder.setInfoPort(infoPort);
237     return ProtobufUtil.prependPBMagic(mbuilder.build().toByteArray());
238   }
239 
240   /**
241    * @param data zookeeper data. may be null
242    * @return pb object of master, null if no active master
243    * @throws DeserializationException
244    */
245   public static ZooKeeperProtos.Master parse(byte[] data) throws DeserializationException {
246     if (data == null) {
247       return null;
248     }
249     int prefixLen = ProtobufUtil.lengthOfPBMagic();
250     try {
251       return ZooKeeperProtos.Master.PARSER.parseFrom(data, prefixLen, data.length - prefixLen);
252     } catch (InvalidProtocolBufferException e) {
253       throw new DeserializationException(e);
254     }
255   }
256   /**
257    * delete the master znode if its content is same as the parameter
258    * @param zkw must not be null
259    * @param content must not be null
260    */
261   public static boolean deleteIfEquals(ZooKeeperWatcher zkw, final String content) {
262     if (content == null){
263       throw new IllegalArgumentException("Content must not be null");
264     }
265 
266     try {
267       Stat stat = new Stat();
268       byte[] data = ZKUtil.getDataNoWatch(zkw, zkw.getMasterAddressZNode(), stat);
269       ServerName sn = ServerName.parseFrom(data);
270       if (sn != null && content.equals(sn.toString())) {
271         return (ZKUtil.deleteNode(zkw, zkw.getMasterAddressZNode(), stat.getVersion()));
272       }
273     } catch (KeeperException e) {
274       LOG.warn("Can't get or delete the master znode", e);
275     } catch (DeserializationException e) {
276       LOG.warn("Can't get or delete the master znode", e);
277     }
278 
279     return false;
280   }
281 }