/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs.server.blockmanagement; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.blur.thirdparty.thrift_0_9_0.TException; import org.apache.blur.thrift.BlurClient; import org.apache.blur.thrift.generated.Blur.Iface; import org.apache.blur.thrift.generated.BlurException; import org.apache.blur.thrift.generated.ShardState; import org.apache.blur.thrift.generated.TableDescriptor; import org.apache.blur.utils.BlurConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class DefaultServerLookup extends ServerLookup { public static final String BLUR_ZK_CONNECTIONS = "blur.zk.connections"; private static final String SHARD_PATTERN = "/" + BlurConstants.SHARD_PREFIX; private static Log LOG = LogFactory.getLog(DefaultServerLookup.class); private final Host2NodesMap _host2datanodeMap; private final Map<String, Iface> _clients = new ConcurrentHashMap<String, Iface>(); private final Map<Path, ShardTableInfo> _pathToShardMapping = new ConcurrentHashMap<Path, ShardTableInfo>(); private final String _baseUri; private final List<Thread> _daemons = new ArrayList<Thread>(); private final AtomicBoolean _running = new AtomicBoolean(); private final long _pollTime; static class ShardTableInfo { final String tableName; final String shardServer; final String zkConnectionStr; ShardTableInfo(String tableName, String shardServer, String zkConnectionStr) { this.tableName = tableName; this.shardServer = shardServer; this.zkConnectionStr = zkConnectionStr; } @Override public String toString() { return "tableName=" + tableName + ", shardServer=" + shardServer + ", zkConnectionStr=" + zkConnectionStr + ""; } } public DefaultServerLookup(Configuration conf, Host2NodesMap host2datanodeMap) { super(conf, host2datanodeMap); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { _running.set(false); } })); _running.set(true); _pollTime = 1000; try { FileSystem fileSystem = FileSystem.get(conf); _baseUri = fileSystem.getUri().toString(); } catch (IOException e) { throw new RuntimeException(e); } _host2datanodeMap = host2datanodeMap; String[] cons = conf.getStrings(BLUR_ZK_CONNECTIONS); if (cons != null) { for (String con : cons) { LOG.info("Blur client setup for [" + con + "]"); _clients.put(con, BlurClient.getClientFromZooKeeperConnectionStr(con)); startWatchDaemon(con); startCleanupDaemon(con); } } } private void startCleanupDaemon(final String con) { Runnable runnable = new Runnable() { @Override public void run() { while (_running.get()) { try { runLayoutCleanup(con); } catch (BlurException e) { LOG.error("Unknown error.", e); } catch (TException e) { LOG.error("Unknown error.", e); } try { Thread.sleep(_pollTime); } catch (InterruptedException e) { LOG.error("Unknown interruption.", e); return; } } } }; Thread thread = new Thread(runnable); thread.setDaemon(true); thread.setName("Blur Cleanup for [" + con + "]"); thread.start(); _daemons.add(thread); } private void startWatchDaemon(final String con) { Runnable runnable = new Runnable() { @Override public void run() { while (_running.get()) { try { runLayoutUpdate(con); } catch (BlurException e) { LOG.error("Unknown error.", e); } catch (TException e) { LOG.error("Unknown error.", e); } try { Thread.sleep(_pollTime); } catch (InterruptedException e) { LOG.error("Unknown interruption.", e); return; } } } }; Thread thread = new Thread(runnable); thread.setDaemon(true); thread.setName("Blur Watch for [" + con + "]"); thread.start(); _daemons.add(thread); } protected void runLayoutCleanup(String zkConnectionStr) throws BlurException, TException { Iface iface = _clients.get(zkConnectionStr); List<String> tableList = iface.tableList(); Collection<String> enabledTables = new HashSet<String>(); for (String table : tableList) { TableDescriptor tableDescriptor = iface.describe(table); String name = tableDescriptor.getName(); if (tableDescriptor.isEnabled()) { enabledTables.add(name); } } cleanUp(enabledTables); } private void cleanUp(Collection<String> enabledTables) { Set<Entry<Path, ShardTableInfo>> entrySet = _pathToShardMapping.entrySet(); Iterator<Entry<Path, ShardTableInfo>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Entry<Path, ShardTableInfo> entry = iterator.next(); ShardTableInfo shardTableInfo = entry.getValue(); String tableName = shardTableInfo.tableName; if (!enabledTables.contains(tableName)) { LOG.info("Removing ShardTableInfo [" + shardTableInfo + "]"); iterator.remove(); } } } private void runLayoutUpdate(String zkConnectionStr) throws BlurException, TException { Iface iface = _clients.get(zkConnectionStr); List<String> tableList = iface.tableList(); for (String table : tableList) { TableDescriptor tableDescriptor = iface.describe(table); if (!tableDescriptor.isEnabled()) { continue; } String tableUri = tableDescriptor.getTableUri(); Path tablePath = new Path(tableUri); String name = tableDescriptor.getName(); Map<String, Map<String, ShardState>> shardServerLayoutState = iface.shardServerLayoutState(name); for (Entry<String, Map<String, ShardState>> entry : shardServerLayoutState.entrySet()) { String shardId = entry.getKey(); Path shardPath = new Path(tablePath, shardId); Map<String, ShardState> serverStateMap = entry.getValue(); String shardServer = getShardServer(serverStateMap); if (shardServer != null) { String shardServerHostName = getHostName(shardServer); ShardTableInfo shardTableInfo = new ShardTableInfo(name, shardServerHostName, zkConnectionStr); LOG.info("Adding mapping [" + shardPath + "] to ShardTableInfo [" + shardTableInfo + "]"); _pathToShardMapping.put(shardPath, shardTableInfo); } } } } private String getHostName(String shardServer) { int indexOf = shardServer.indexOf(':'); if (indexOf < 0) { return shardServer; } return shardServer.substring(0, indexOf); } private String getShardServer(Map<String, ShardState> serverStateMap) { for (Entry<String, ShardState> entry : serverStateMap.entrySet()) { String server = entry.getKey(); ShardState shardState = entry.getValue(); if (shardState == ShardState.OPEN) { return server; } } return null; } @Override public String getShardServer(String srcPath) { int indexOf = srcPath.indexOf(SHARD_PATTERN); if (indexOf < 0) { return null; } int end = srcPath.indexOf('/', indexOf + 1); if (end < 0) { return null; } else { Path shardPath = new Path(_baseUri + srcPath.substring(0, end)); ShardTableInfo shardTableInfo = _pathToShardMapping.get(shardPath); LOG.info("Path [" + srcPath + "] Resolved to [" + shardPath.toString() + "] on ShardTableInfo [" + shardTableInfo + "]"); if (shardTableInfo == null) { return null; } return shardTableInfo.shardServer; } } @Override public boolean isPathSupported(String srcPath) { if (!srcPath.contains(SHARD_PATTERN)) { return false; } return getShardServer(srcPath) != null; } @Override public DatanodeDescriptor getDatanodeDescriptor(String shardServer) { return _host2datanodeMap.getDataNodeByHostName(shardServer); } }