001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.zookeeper; 020 021import java.io.IOException; 022import java.util.Map.Entry; 023import java.util.Properties; 024 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.HConstants; 027import org.apache.hadoop.util.StringUtils; 028import org.apache.yetus.audience.InterfaceAudience; 029 030import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 031 032/** 033 * Utility methods for reading, and building the ZooKeeper configuration. 034 * 035 * The order and priority for reading the config are as follows: 036 * (1). Property with "hbase.zookeeper.property." prefix from HBase XML 037 * (2). other zookeeper related properties in HBASE XML 038 */ 039@InterfaceAudience.Private 040public final class ZKConfig { 041 042 private static final String VARIABLE_START = "${"; 043 044 private ZKConfig() { 045 } 046 047 /** 048 * Make a Properties object holding ZooKeeper config. 049 * Parses the corresponding config options from the HBase XML configs 050 * and generates the appropriate ZooKeeper properties. 051 * @param conf Configuration to read from. 052 * @return Properties holding mappings representing ZooKeeper config file. 053 */ 054 public static Properties makeZKProps(Configuration conf) { 055 return makeZKPropsFromHbaseConfig(conf); 056 } 057 058 /** 059 * Make a Properties object holding ZooKeeper config. 060 * Parses the corresponding config options from the HBase XML configs 061 * and generates the appropriate ZooKeeper properties. 062 * 063 * @param conf Configuration to read from. 064 * @return Properties holding mappings representing ZooKeeper config file. 065 */ 066 private static Properties makeZKPropsFromHbaseConfig(Configuration conf) { 067 Properties zkProperties = new Properties(); 068 069 // Directly map all of the hbase.zookeeper.property.KEY properties. 070 // Synchronize on conf so no loading of configs while we iterate 071 synchronized (conf) { 072 for (Entry<String, String> entry : conf) { 073 String key = entry.getKey(); 074 if (key.startsWith(HConstants.ZK_CFG_PROPERTY_PREFIX)) { 075 String zkKey = key.substring(HConstants.ZK_CFG_PROPERTY_PREFIX_LEN); 076 String value = entry.getValue(); 077 // If the value has variables substitutions, need to do a get. 078 if (value.contains(VARIABLE_START)) { 079 value = conf.get(key); 080 } 081 zkProperties.setProperty(zkKey, value); 082 } 083 } 084 } 085 086 // If clientPort is not set, assign the default. 087 if (zkProperties.getProperty(HConstants.CLIENT_PORT_STR) == null) { 088 zkProperties.put(HConstants.CLIENT_PORT_STR, 089 HConstants.DEFAULT_ZOOKEEPER_CLIENT_PORT); 090 } 091 092 // Create the server.X properties. 093 int peerPort = conf.getInt("hbase.zookeeper.peerport", 2888); 094 int leaderPort = conf.getInt("hbase.zookeeper.leaderport", 3888); 095 096 final String[] serverHosts = conf.getStrings(HConstants.ZOOKEEPER_QUORUM, 097 HConstants.LOCALHOST); 098 String serverHost; 099 String address; 100 String key; 101 for (int i = 0; i < serverHosts.length; ++i) { 102 if (serverHosts[i].contains(":")) { 103 serverHost = serverHosts[i].substring(0, serverHosts[i].indexOf(':')); 104 } else { 105 serverHost = serverHosts[i]; 106 } 107 address = serverHost + ":" + peerPort + ":" + leaderPort; 108 key = "server." + i; 109 zkProperties.put(key, address); 110 } 111 112 return zkProperties; 113 } 114 115 /** 116 * Return the ZK Quorum servers string given the specified configuration 117 * 118 * @param conf 119 * @return Quorum servers String 120 */ 121 private static String getZKQuorumServersStringFromHbaseConfig(Configuration conf) { 122 String defaultClientPort = Integer.toString( 123 conf.getInt(HConstants.ZOOKEEPER_CLIENT_PORT, HConstants.DEFAULT_ZOOKEEPER_CLIENT_PORT)); 124 125 // Build the ZK quorum server string with "server:clientport" list, separated by ',' 126 final String[] serverHosts = 127 conf.getStrings(HConstants.ZOOKEEPER_QUORUM, HConstants.LOCALHOST); 128 return buildZKQuorumServerString(serverHosts, defaultClientPort); 129 } 130 131 /** 132 * Return the ZK Quorum servers string given the specified configuration. 133 * @return Quorum servers 134 */ 135 public static String getZKQuorumServersString(Configuration conf) { 136 return getZKQuorumServersStringFromHbaseConfig(conf); 137 } 138 139 /** 140 * Build the ZK quorum server string with "server:clientport" list, separated by ',' 141 * 142 * @param serverHosts a list of servers for ZK quorum 143 * @param clientPort the default client port 144 * @return the string for a list of "server:port" separated by "," 145 */ 146 public static String buildZKQuorumServerString(String[] serverHosts, String clientPort) { 147 StringBuilder quorumStringBuilder = new StringBuilder(); 148 String serverHost; 149 for (int i = 0; i < serverHosts.length; ++i) { 150 if (serverHosts[i].contains(":")) { 151 serverHost = serverHosts[i]; // just use the port specified from the input 152 } else { 153 serverHost = serverHosts[i] + ":" + clientPort; 154 } 155 if (i > 0) { 156 quorumStringBuilder.append(','); 157 } 158 quorumStringBuilder.append(serverHost); 159 } 160 return quorumStringBuilder.toString(); 161 } 162 163 /** 164 * Verifies that the given key matches the expected format for a ZooKeeper cluster key. 165 * The Quorum for the ZK cluster can have one the following formats (see examples below): 166 * 167 * <ol> 168 * <li>s1,s2,s3 (no client port in the list, the client port could be obtained from 169 * clientPort)</li> 170 * <li>s1:p1,s2:p2,s3:p3 (with client port, which could be same or different for each server, 171 * in this case, the clientPort would be ignored)</li> 172 * <li>s1:p1,s2,s3:p3 (mix of (1) and (2) - if port is not specified in a server, it would use 173 * the clientPort; otherwise, it would use the specified port)</li> 174 * </ol> 175 * 176 * @param key the cluster key to validate 177 * @throws IOException if the key could not be parsed 178 */ 179 public static void validateClusterKey(String key) throws IOException { 180 transformClusterKey(key); 181 } 182 183 /** 184 * Separate the given key into the three configurations it should contain: 185 * hbase.zookeeper.quorum, hbase.zookeeper.client.port 186 * and zookeeper.znode.parent 187 * @param key 188 * @return the three configuration in the described order 189 * @throws IOException 190 */ 191 public static ZKClusterKey transformClusterKey(String key) throws IOException { 192 String[] parts = key.split(":"); 193 194 if (parts.length == 3) { 195 if (!parts[2].matches("/.*[^/]")) { 196 throw new IOException("Cluster key passed " + key + " is invalid, the format should be:" + 197 HConstants.ZOOKEEPER_QUORUM + ":" + HConstants.ZOOKEEPER_CLIENT_PORT + ":" 198 + HConstants.ZOOKEEPER_ZNODE_PARENT); 199 } 200 return new ZKClusterKey(parts [0], Integer.parseInt(parts [1]), parts [2]); 201 } 202 203 if (parts.length > 3) { 204 // The quorum could contain client port in server:clientport format, try to transform more. 205 String zNodeParent = parts [parts.length - 1]; 206 if (!zNodeParent.matches("/.*[^/]")) { 207 throw new IOException("Cluster key passed " + key + " is invalid, the format should be:" 208 + HConstants.ZOOKEEPER_QUORUM + ":" + HConstants.ZOOKEEPER_CLIENT_PORT + ":" 209 + HConstants.ZOOKEEPER_ZNODE_PARENT); 210 } 211 212 String clientPort = parts [parts.length - 2]; 213 214 // The first part length is the total length minus the lengths of other parts and minus 2 ":" 215 int endQuorumIndex = key.length() - zNodeParent.length() - clientPort.length() - 2; 216 String quorumStringInput = key.substring(0, endQuorumIndex); 217 String[] serverHosts = quorumStringInput.split(","); 218 219 // The common case is that every server has its own client port specified - this means 220 // that (total parts - the ZNodeParent part - the ClientPort part) is equal to 221 // (the number of "," + 1) - "+ 1" because the last server has no ",". 222 if ((parts.length - 2) == (serverHosts.length + 1)) { 223 return new ZKClusterKey(quorumStringInput, Integer.parseInt(clientPort), zNodeParent); 224 } 225 226 // For the uncommon case that some servers has no port specified, we need to build the 227 // server:clientport list using default client port for servers without specified port. 228 return new ZKClusterKey( 229 buildZKQuorumServerString(serverHosts, clientPort), 230 Integer.parseInt(clientPort), 231 zNodeParent); 232 } 233 234 throw new IOException("Cluster key passed " + key + " is invalid, the format should be:" + 235 HConstants.ZOOKEEPER_QUORUM + ":" + HConstants.ZOOKEEPER_CLIENT_PORT + ":" 236 + HConstants.ZOOKEEPER_ZNODE_PARENT); 237 } 238 239 /** 240 * Get the key to the ZK ensemble for this configuration without 241 * adding a name at the end 242 * @param conf Configuration to use to build the key 243 * @return ensemble key without a name 244 */ 245 public static String getZooKeeperClusterKey(Configuration conf) { 246 return getZooKeeperClusterKey(conf, null); 247 } 248 249 /** 250 * Get the key to the ZK ensemble for this configuration and append 251 * a name at the end 252 * @param conf Configuration to use to build the key 253 * @param name Name that should be appended at the end if not empty or null 254 * @return ensemble key with a name (if any) 255 */ 256 public static String getZooKeeperClusterKey(Configuration conf, String name) { 257 String ensemble = conf.get(HConstants.ZOOKEEPER_QUORUM).replaceAll( 258 "[\\t\\n\\x0B\\f\\r]", ""); 259 StringBuilder builder = new StringBuilder(ensemble); 260 builder.append(":"); 261 builder.append(conf.get(HConstants.ZOOKEEPER_CLIENT_PORT)); 262 builder.append(":"); 263 builder.append(conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT)); 264 if (name != null && !name.isEmpty()) { 265 builder.append(","); 266 builder.append(name); 267 } 268 return builder.toString(); 269 } 270 271 /** 272 * Standardize the ZK quorum string: make it a "server:clientport" list, separated by ',' 273 * @param quorumStringInput a string contains a list of servers for ZK quorum 274 * @param clientPort the default client port 275 * @return the string for a list of "server:port" separated by "," 276 */ 277 @VisibleForTesting 278 public static String standardizeZKQuorumServerString(String quorumStringInput, 279 String clientPort) { 280 String[] serverHosts = quorumStringInput.split(","); 281 return buildZKQuorumServerString(serverHosts, clientPort); 282 } 283 284 // The Quorum for the ZK cluster can have one the following format (see examples below): 285 // (1). s1,s2,s3 (no client port in the list, the client port could be obtained from clientPort) 286 // (2). s1:p1,s2:p2,s3:p3 (with client port, which could be same or different for each server, 287 // in this case, the clientPort would be ignored) 288 // (3). s1:p1,s2,s3:p3 (mix of (1) and (2) - if port is not specified in a server, it would use 289 // the clientPort; otherwise, it would use the specified port) 290 @VisibleForTesting 291 public static class ZKClusterKey { 292 private String quorumString; 293 private int clientPort; 294 private String znodeParent; 295 296 ZKClusterKey(String quorumString, int clientPort, String znodeParent) { 297 this.quorumString = quorumString; 298 this.clientPort = clientPort; 299 this.znodeParent = znodeParent; 300 } 301 302 public String getQuorumString() { 303 return quorumString; 304 } 305 306 public int getClientPort() { 307 return clientPort; 308 } 309 310 public String getZnodeParent() { 311 return znodeParent; 312 } 313 } 314 315 /** 316 * Get the client ZK Quorum servers string 317 * @param conf the configuration to read 318 * @return Client quorum servers, or null if not specified 319 */ 320 public static String getClientZKQuorumServersString(Configuration conf) { 321 String clientQuromServers = conf.get(HConstants.CLIENT_ZOOKEEPER_QUORUM); 322 if (clientQuromServers == null) { 323 return null; 324 } 325 int defaultClientPort = 326 conf.getInt(HConstants.ZOOKEEPER_CLIENT_PORT, HConstants.DEFAULT_ZOOKEEPER_CLIENT_PORT); 327 String clientZkClientPort = 328 Integer.toString(conf.getInt(HConstants.CLIENT_ZOOKEEPER_CLIENT_PORT, defaultClientPort)); 329 // Build the ZK quorum server string with "server:clientport" list, separated by ',' 330 final String[] serverHosts = StringUtils.getStrings(clientQuromServers); 331 return buildZKQuorumServerString(serverHosts, clientZkClientPort); 332 } 333}