001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.zookeeper; 019 020import java.io.IOException; 021import java.io.InterruptedIOException; 022 023import org.apache.hadoop.hbase.Abortable; 024import org.apache.hadoop.hbase.HConstants; 025import org.apache.hadoop.hbase.ServerName; 026import org.apache.hadoop.hbase.exceptions.DeserializationException; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.apache.zookeeper.KeeperException; 029import org.apache.zookeeper.data.Stat; 030 031import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException; 032import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 033import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 034import org.apache.hadoop.hbase.shaded.protobuf.generated.ZooKeeperProtos; 035 036/** 037 * Manages the location of the current active Master for the RegionServer. 038 * <p> 039 * Listens for ZooKeeper events related to the master address. The node 040 * <code>/master</code> will contain the address of the current master. 041 * This listener is interested in 042 * <code>NodeDeleted</code> and <code>NodeCreated</code> events on 043 * <code>/master</code>. 044 * <p> 045 * Utilizes {@link ZKNodeTracker} for zk interactions. 046 * <p> 047 * You can get the current master via {@link #getMasterAddress()} or via 048 * {@link #getMasterAddress(ZKWatcher)} if you do not have a running 049 * instance of this Tracker in your context. 050 * <p> 051 * This class also includes utility for interacting with the master znode, for 052 * writing and reading the znode content. 053 */ 054@InterfaceAudience.Private 055public class MasterAddressTracker extends ZKNodeTracker { 056 /** 057 * Construct a master address listener with the specified 058 * <code>zookeeper</code> reference. 059 * <p> 060 * This constructor does not trigger any actions, you must call methods 061 * explicitly. Normally you will just want to execute {@link #start()} to 062 * begin tracking of the master address. 063 * 064 * @param watcher zk reference and watcher 065 * @param abortable abortable in case of fatal error 066 */ 067 public MasterAddressTracker(ZKWatcher watcher, Abortable abortable) { 068 super(watcher, watcher.znodePaths.masterAddressZNode, abortable); 069 } 070 071 /** 072 * Get the address of the current master if one is available. Returns null 073 * if no current master. 074 * @return Server name or null if timed out. 075 */ 076 public ServerName getMasterAddress() { 077 return getMasterAddress(false); 078 } 079 080 /** 081 * Get the info port of the current master of one is available. 082 * Return 0 if no current master or zookeeper is unavailable 083 * @return info port or 0 if timed out 084 */ 085 public int getMasterInfoPort() { 086 try { 087 final ZooKeeperProtos.Master master = parse(this.getData(false)); 088 if (master == null) { 089 return 0; 090 } 091 return master.getInfoPort(); 092 } catch (DeserializationException e) { 093 LOG.warn("Failed parse master zk node data", e); 094 return 0; 095 } 096 } 097 /** 098 * Get the info port of the backup master if it is available. 099 * 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 = ZNodePaths.joinZNode(watcher.znodePaths.backupMasterAddressesZNode, 105 sn.toString()); 106 try { 107 byte[] data = ZKUtil.getData(watcher, backupZNode); 108 final ZooKeeperProtos.Master backup = parse(data); 109 if (backup == null) { 110 return 0; 111 } 112 return backup.getInfoPort(); 113 } catch (Exception e) { 114 LOG.warn("Failed to get backup master: " + sn + "'s info port.", e); 115 return 0; 116 } 117 } 118 119 /** 120 * Get the address of the current master if one is available. Returns null 121 * if no current master. If refresh is set, try to load the data from ZK again, 122 * otherwise, cached data will be used. 123 * 124 * @param refresh whether to refresh the data by calling ZK directly. 125 * @return Server name or null if timed out. 126 */ 127 public ServerName getMasterAddress(final boolean refresh) { 128 try { 129 return ProtobufUtil.parseServerNameFrom(super.getData(refresh)); 130 } catch (DeserializationException e) { 131 LOG.warn("Failed parse", e); 132 return null; 133 } 134 } 135 136 /** 137 * Get master address. 138 * Use this instead of {@link #getMasterAddress()} if you do not have an 139 * instance of this tracker in your context. 140 * @param zkw ZKWatcher to use 141 * @return ServerName stored in the the master address znode or null if no 142 * znode present. 143 * @throws KeeperException if a ZooKeeper operation fails 144 * @throws IOException if the address of the ZooKeeper master cannot be retrieved 145 */ 146 public static ServerName getMasterAddress(final ZKWatcher zkw) 147 throws KeeperException, IOException { 148 byte [] data; 149 try { 150 data = ZKUtil.getData(zkw, zkw.znodePaths.masterAddressZNode); 151 } catch (InterruptedException e) { 152 throw new InterruptedIOException(); 153 } 154 // TODO javadoc claims we return null in this case. :/ 155 if (data == null){ 156 throw new IOException("Can't get master address from ZooKeeper; znode data == null"); 157 } 158 try { 159 return ProtobufUtil.parseServerNameFrom(data); 160 } catch (DeserializationException e) { 161 KeeperException ke = new KeeperException.DataInconsistencyException(); 162 ke.initCause(e); 163 throw ke; 164 } 165 } 166 167 /** 168 * Get master info port. 169 * Use this instead of {@link #getMasterInfoPort()} if you do not have an 170 * instance of this tracker in your context. 171 * @param zkw ZKWatcher to use 172 * @return master info port in the the master address znode or null if no 173 * znode present. 174 * // TODO can't return null for 'int' return type. non-static verison returns 0 175 * @throws KeeperException if a ZooKeeper operation fails 176 * @throws IOException if the address of the ZooKeeper master cannot be retrieved 177 */ 178 public static int getMasterInfoPort(final ZKWatcher zkw) throws KeeperException, IOException { 179 byte[] data; 180 try { 181 data = ZKUtil.getData(zkw, zkw.znodePaths.masterAddressZNode); 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 ZKWatcher 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 if a ZooKeeper operation fails 208 */ 209 public static boolean setMasterAddress(final ZKWatcher 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 if the parsing fails 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(ZKWatcher 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.znodePaths.masterAddressZNode, stat); 269 ServerName sn = ProtobufUtil.parseServerNameFrom(data); 270 if (sn != null && content.equals(sn.toString())) { 271 return (ZKUtil.deleteNode(zkw, zkw.znodePaths.masterAddressZNode, 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}