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        return parse(this.getData(false)).getInfoPort();
88      } catch (DeserializationException e) {
89        LOG.warn("Failed parse master zk node data", e);
90        return 0;
91      }
92    }
93    /**
94     * Get the info port of the backup master if it is available.
95     * Return 0 if no current master or zookeeper is unavailable
96     * @param sn server name of backup master
97     * @return info port or 0 if timed out or exceptions
98     */
99    public int getBackupMasterInfoPort(final ServerName sn) {
100     String backupZNode = ZKUtil.joinZNode(watcher.backupMasterAddressesZNode, sn.toString());
101     try {
102       byte[] data = ZKUtil.getData(watcher, backupZNode);
103       return parse(data).getInfoPort();
104     } catch (Exception e) {
105       LOG.warn("Failed to get backup master: " + sn + "'s info port.", e);
106       return 0;
107     }
108   }
109 
110   /**
111    * Get the address of the current master if one is available.  Returns null
112    * if no current master. If refresh is set, try to load the data from ZK again,
113    * otherwise, cached data will be used.
114    *
115    * @param refresh whether to refresh the data by calling ZK directly.
116    * @return Server name or null if timed out.
117    */
118   public ServerName getMasterAddress(final boolean refresh) {
119     try {
120       return ServerName.parseFrom(super.getData(refresh));
121     } catch (DeserializationException e) {
122       LOG.warn("Failed parse", e);
123       return null;
124     }
125   }
126 
127   /**
128    * Get master address.
129    * Use this instead of {@link #getMasterAddress()} if you do not have an
130    * instance of this tracker in your context.
131    * @param zkw ZooKeeperWatcher to use
132    * @return ServerName stored in the the master address znode or null if no
133    * znode present.
134    * @throws KeeperException 
135    * @throws IOException 
136    */
137   public static ServerName getMasterAddress(final ZooKeeperWatcher zkw)
138   throws KeeperException, IOException {
139     byte [] data;
140     try {
141       data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
142     } catch (InterruptedException e) {
143       throw new InterruptedIOException();
144     }
145     if (data == null){
146       throw new IOException("Can't get master address from ZooKeeper; znode data == null");
147     }
148     try {
149       return ServerName.parseFrom(data);
150     } catch (DeserializationException e) {
151       KeeperException ke = new KeeperException.DataInconsistencyException();
152       ke.initCause(e);
153       throw ke;
154     }
155   }
156 
157   /**
158    * Get master info port.
159    * Use this instead of {@link #getMasterInfoPort()} if you do not have an
160    * instance of this tracker in your context.
161    * @param zkw ZooKeeperWatcher to use
162    * @return master info port in the the master address znode or null if no
163    * znode present.
164    * @throws KeeperException
165    * @throws IOException
166    */
167   public static int getMasterInfoPort(final ZooKeeperWatcher zkw) throws KeeperException,
168       IOException {
169     byte[] data;
170     try {
171       data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
172     } catch (InterruptedException e) {
173       throw new InterruptedIOException();
174     }
175     if (data == null) {
176       throw new IOException("Can't get master address from ZooKeeper; znode data == null");
177     }
178     try {
179       return parse(data).getInfoPort();
180     } catch (DeserializationException e) {
181       KeeperException ke = new KeeperException.DataInconsistencyException();
182       ke.initCause(e);
183       throw ke;
184     }
185   }
186 
187   /**
188    * Set master address into the <code>master</code> znode or into the backup
189    * subdirectory of backup masters; switch off the passed in <code>znode</code>
190    * path.
191    * @param zkw The ZooKeeperWatcher to use.
192    * @param znode Where to create the znode; could be at the top level or it
193    * could be under backup masters
194    * @param master ServerName of the current master
195    * @return true if node created, false if not; a watch is set in both cases
196    * @throws KeeperException
197    */
198   public static boolean setMasterAddress(final ZooKeeperWatcher zkw,
199       final String znode, final ServerName master, int infoPort)
200   throws KeeperException {
201     return ZKUtil.createEphemeralNodeAndWatch(zkw, znode, toByteArray(master, infoPort));
202   }
203 
204   /**
205    * Check if there is a master available.
206    * @return true if there is a master set, false if not.
207    */
208   public boolean hasMaster() {
209     return super.getData(false) != null;
210   }
211 
212   /**
213    * @param sn
214    * @return Content of the master znode as a serialized pb with the pb
215    * magic as prefix.
216    */
217   static byte[] toByteArray(final ServerName sn, int infoPort) {
218     ZooKeeperProtos.Master.Builder mbuilder = ZooKeeperProtos.Master.newBuilder();
219     HBaseProtos.ServerName.Builder snbuilder = HBaseProtos.ServerName.newBuilder();
220     snbuilder.setHostName(sn.getHostname());
221     snbuilder.setPort(sn.getPort());
222     snbuilder.setStartCode(sn.getStartcode());
223     mbuilder.setMaster(snbuilder.build());
224     mbuilder.setRpcVersion(HConstants.RPC_CURRENT_VERSION);
225     mbuilder.setInfoPort(infoPort);
226     return ProtobufUtil.prependPBMagic(mbuilder.build().toByteArray());
227   }
228 
229   /**
230    * @param data zookeeper data
231    * @return pb object of master
232    * @throws DeserializationException
233    */
234   public static ZooKeeperProtos.Master parse(byte[] data) throws DeserializationException {
235     int prefixLen = ProtobufUtil.lengthOfPBMagic();
236     try {
237       return ZooKeeperProtos.Master.PARSER.parseFrom(data, prefixLen, data.length - prefixLen);
238     } catch (InvalidProtocolBufferException e) {
239       throw new DeserializationException(e);
240     }
241   }
242   /**
243    * delete the master znode if its content is same as the parameter
244    */
245   public static boolean deleteIfEquals(ZooKeeperWatcher zkw, final String content) {
246     if (content == null){
247       throw new IllegalArgumentException("Content must not be null");
248     }
249 
250     try {
251       Stat stat = new Stat();
252       byte[] data = ZKUtil.getDataNoWatch(zkw, zkw.getMasterAddressZNode(), stat);
253       ServerName sn = ServerName.parseFrom(data);
254       if (sn != null && content.equals(sn.toString())) {
255         return (ZKUtil.deleteNode(zkw, zkw.getMasterAddressZNode(), stat.getVersion()));
256       }
257     } catch (KeeperException e) {
258       LOG.warn("Can't get or delete the master znode", e);
259     } catch (DeserializationException e) {
260       LOG.warn("Can't get or delete the master znode", e);
261     }
262 
263     return false;
264   }
265 }