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