package org.apache.blur.thrift; /** * 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. */ import static org.apache.blur.metrics.MetricsConstants.CPU_USED; import static org.apache.blur.metrics.MetricsConstants.HEAP_USED; import static org.apache.blur.metrics.MetricsConstants.JVM; import static org.apache.blur.metrics.MetricsConstants.LOAD_AVERAGE; import static org.apache.blur.metrics.MetricsConstants.ORG_APACHE_BLUR; import static org.apache.blur.metrics.MetricsConstants.SYSTEM; import static org.apache.blur.utils.BlurConstants.BLUR_HDFS_TRACE_PATH; import static org.apache.blur.utils.BlurConstants.BLUR_HOME; import static org.apache.blur.utils.BlurConstants.BLUR_SERVER_SECURITY_FILTER_CLASS; import static org.apache.blur.utils.BlurConstants.BLUR_TMP_PATH; import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_CONNECTION; import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_TIMEOUT; import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_TIMEOUT_DEFAULT; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.lang.management.OperatingSystemMXBean; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ExecutorService; import org.apache.blur.BlurConfiguration; import org.apache.blur.concurrent.Executors; import org.apache.blur.log.Log; import org.apache.blur.log.LogFactory; import org.apache.blur.manager.indexserver.BlurServerShutDown.BlurShutdown; import org.apache.blur.server.ServerSecurityFilter; import org.apache.blur.server.ServerSecurityFilterFactory; import org.apache.blur.thirdparty.thrift_0_9_0.protocol.TBinaryProtocol; import org.apache.blur.thirdparty.thrift_0_9_0.protocol.TCompactProtocol; import org.apache.blur.thirdparty.thrift_0_9_0.server.TServer; import org.apache.blur.thirdparty.thrift_0_9_0.server.TServerEventHandler; import org.apache.blur.thirdparty.thrift_0_9_0.server.TThreadPoolServer; import org.apache.blur.thirdparty.thrift_0_9_0.server.TThreadPoolServer.Args; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TFramedTransport; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TNonblockingServerSocket; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TNonblockingServerTransport; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TServerSocket; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TServerTransport; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TTransportException; import org.apache.blur.thrift.generated.Blur; import org.apache.blur.thrift.generated.Blur.Iface; import org.apache.blur.thrift.sasl.SaslHelper; import org.apache.blur.thrift.sasl.TSaslServerTransport; import org.apache.blur.thrift.server.TThreadedSelectorServer; import org.apache.blur.thrift.server.TThreadedSelectorServer.Args.AcceptPolicy; import org.apache.blur.trace.LogTraceStorage; import org.apache.blur.trace.TraceStorage; import org.apache.blur.trace.hdfs.HdfsTraceStorage; import org.apache.blur.utils.BlurUtil; import org.apache.blur.zookeeper.ZkUtils; import org.apache.hadoop.conf.Configuration; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Gauge; import com.yammer.metrics.core.MetricName; public class ThriftServer { private static final Log LOG = LogFactory.getLog(ThriftServer.class); private String _nodeName; private Iface _iface; private TServer _server; private boolean _closed; private int _threadCount; private BlurShutdown _shutdown; private ExecutorService _executorService; private ExecutorService _queryExexutorService; private ExecutorService _mutateExecutorService; private TServerEventHandler _eventHandler; private TServerTransport _serverTransport; private int _acceptQueueSizePerThread = 4; private long _maxReadBufferBytes = Long.MAX_VALUE; private int _selectorThreads = 2; private int _maxFrameSize = 16384000; private BlurConfiguration _configuration; public int getMaxFrameSize() { return _maxFrameSize; } public void setMaxFrameSize(int maxFrameSize) { _maxFrameSize = maxFrameSize; } public TServerTransport getServerTransport() { return _serverTransport; } public void setServerTransport(TServerTransport serverTransport) { _serverTransport = serverTransport; } public static String getCommandLibPath() { String blurHomeDir = getBlurHomeDir(); if (blurHomeDir == null) { return null; } File file = new File(blurHomeDir, "commands"); file.mkdirs(); if (file.exists()) { return file.toURI().toString(); } return null; } public static String getBlurHomeDir() { return System.getenv(BLUR_HOME); } public static File getTmpPath(BlurConfiguration configuration) throws IOException { File defaultTmpPath = getDefaultTmpPath(BLUR_TMP_PATH); String configTmpPath = configuration.get(BLUR_TMP_PATH); File tmpPath; if (!(configTmpPath == null || configTmpPath.isEmpty())) { tmpPath = new File(configTmpPath); } else { tmpPath = defaultTmpPath; } return tmpPath; } public static File getDefaultTmpPath(String propName) throws IOException { String blurHomeDir = getBlurHomeDir(); File tmp; if (blurHomeDir == null) { tmp = getTmpDir(); LOG.info("Attempting to use default tmp directory [{0}]", tmp); } else { tmp = new File(blurHomeDir, "tmp"); LOG.info("Attempting to use configured tmp directory [{0}]", tmp); if (!tmp.exists() && !tmp.mkdirs()) { tmp = getTmpDir(); LOG.info("Attempting to use default tmp directory [{0}]", tmp); } } if (!tmp.exists() && !tmp.mkdirs()) { throw new IOException("Cannot create tmp directory [" + tmp.toURI() + "], please create directory or configure property [" + propName + "]."); } File file = new File(tmp, UUID.randomUUID().toString()); if (!file.createNewFile()) { throw new IOException("Cannot create tmp file in [" + tmp.toURI() + "]."); } file.delete(); return tmp; } private static File getTmpDir() { return new File(System.getProperty("java.io.tmpdir"), "blur_tmp"); } public static TraceStorage setupTraceStorage(BlurConfiguration configuration, Configuration conf) throws IOException { String hdfsPath = configuration.get(BLUR_HDFS_TRACE_PATH); if (hdfsPath != null) { HdfsTraceStorage hdfsTraceStorage = new HdfsTraceStorage(configuration); hdfsTraceStorage.init(conf); return hdfsTraceStorage; } else { return new LogTraceStorage(configuration); } } public static void printUlimits() throws IOException { ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", "ulimit -a"); Process process; try { process = processBuilder.start(); } catch (Exception e) { LOG.warn("Could not run ulimit command to retrieve limits.", e); return; } InputStream inputStream = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { LOG.info("ulimit: " + line); } reader.close(); } public static void setupJvmMetrics() { final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); Metrics.newGauge(new MetricName(ORG_APACHE_BLUR, SYSTEM, LOAD_AVERAGE), new Gauge<Double>() { @Override public Double value() { return operatingSystemMXBean.getSystemLoadAverage(); } }); Metrics.newGauge(new MetricName(ORG_APACHE_BLUR, JVM, HEAP_USED), new Gauge<Long>() { @Override public Long value() { MemoryUsage usage = memoryMXBean.getHeapMemoryUsage(); return usage.getUsed(); } }); Method processCpuTimeMethod = null; for (Method method : operatingSystemMXBean.getClass().getDeclaredMethods()) { if (method.getName().equals("getProcessCpuTime")) { method.setAccessible(true); processCpuTimeMethod = method; } } final double availableProcessors = operatingSystemMXBean.getAvailableProcessors(); if (processCpuTimeMethod != null) { final Method pctm = processCpuTimeMethod; Metrics.newGauge(new MetricName(ORG_APACHE_BLUR, JVM, CPU_USED), new Gauge<Double>() { private long start = System.nanoTime(); private long lastCpuTime = getProcessCputTime(pctm, operatingSystemMXBean); @Override public Double value() { long now = System.nanoTime(); long cpuTime = getProcessCputTime(pctm, operatingSystemMXBean); long time = now - start; long processTime = cpuTime - lastCpuTime; try { return ((processTime / (double) time) / availableProcessors) * 100.0; } finally { lastCpuTime = cpuTime; start = System.nanoTime(); } } }); } } private static long getProcessCputTime(Method processCpuTimeMethod, OperatingSystemMXBean operatingSystemMXBean) { try { return (Long) processCpuTimeMethod.invoke(operatingSystemMXBean, new Object[] {}); } catch (Exception e) { LOG.error("Unknown Error", e); return 0; } } public synchronized void close() { if (!_closed) { _closed = true; _shutdown.shutdown(); _server.stop(); if (_executorService != null) { _executorService.shutdownNow(); } if (_queryExexutorService != null) { _queryExexutorService.shutdownNow(); } if (_mutateExecutorService != null) { _mutateExecutorService.shutdownNow(); } } } protected static int getServerIndex(String[] args) { for (int i = 0; i < args.length; i++) { if ("-s".equals(args[i])) { if (i + 1 < args.length) { return Integer.parseInt(args[i + 1]); } } } return 0; } public void start() throws TTransportException, IOException { Blur.Processor<Blur.Iface> processor = new Blur.Processor<Blur.Iface>(_iface); if (SaslHelper.isSaslEnabled(_configuration)) { _executorService = Executors.newThreadPool("thrift-processors", _threadCount, false); TSaslServerTransport.Factory saslTransportFactory = SaslHelper.getTSaslServerTransportFactory(_configuration); Args args = new TThreadPoolServer.Args(_serverTransport); args.executorService(_executorService); args.processor(processor); args.protocolFactory(new TCompactProtocol.Factory()); args.transportFactory(saslTransportFactory); _server = new TThreadPoolServer(args); _server.setServerEventHandler(_eventHandler); } else { _executorService = Executors.newThreadPool("thrift-processors", _threadCount); TThreadedSelectorServer.Args args = new TThreadedSelectorServer.Args( (TNonblockingServerTransport) _serverTransport); args.processor(processor); args.executorService(_executorService); args.transportFactory(new TFramedTransport.Factory(_maxFrameSize)); args.protocolFactory(new TBinaryProtocol.Factory(true, true)); args.selectorThreads = _selectorThreads; args.maxReadBufferBytes = _maxReadBufferBytes; args.acceptQueueSizePerThread(_acceptQueueSizePerThread); args.acceptPolicy(AcceptPolicy.FAIR_ACCEPT); _server = new TThreadedSelectorServer(args); _server.setServerEventHandler(_eventHandler); } LOG.info("Starting server [{0}]", _nodeName); _server.serve(); } public static InetSocketAddress getBindInetSocketAddress(String bindAddress, int bindPort) { return new InetSocketAddress(bindAddress, bindPort); } public static String isEmpty(String str, String name) { if (str == null || str.trim().isEmpty()) { throw new IllegalArgumentException("Property [" + name + "] is missing or blank."); } return str; } public Iface getIface() { return _iface; } public void setIface(Iface iface) { this._iface = iface; } public String getNodeName() { return _nodeName; } public void setNodeName(String nodeName) { this._nodeName = nodeName; } public static String getNodeName(BlurConfiguration configuration, String hostNameProperty) throws UnknownHostException { String hostName = configuration.get(hostNameProperty); if (hostName == null) { hostName = ""; } hostName = hostName.trim(); if (hostName.isEmpty()) { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { String message = e.getMessage(); int index = message.indexOf(':'); if (index < 0) { throw new RuntimeException("Nodename cannot be determined."); } String nodeName = message.substring(0, index); LOG.warn("Hack to get nodename from exception [" + nodeName + "]"); return nodeName; } } return hostName; } public void setThreadCount(int threadCount) { _threadCount = threadCount; } public BlurShutdown getShutdown() { return _shutdown; } public void setShutdown(BlurShutdown shutdown) { _shutdown = shutdown; } public TServerEventHandler getEventHandler() { return _eventHandler; } public void setEventHandler(TServerEventHandler eventHandler) { _eventHandler = eventHandler; } public int getAcceptQueueSizePerThread() { return _acceptQueueSizePerThread; } public void setAcceptQueueSizePerThread(int acceptQueueSizePerThread) { _acceptQueueSizePerThread = acceptQueueSizePerThread; } public long getMaxReadBufferBytes() { return _maxReadBufferBytes; } public void setMaxReadBufferBytes(long maxReadBufferBytes) { _maxReadBufferBytes = maxReadBufferBytes; } public int getSelectorThreads() { return _selectorThreads; } public void setSelectorThreads(int selectorThreads) { _selectorThreads = selectorThreads; } public BlurConfiguration getConfiguration() { return _configuration; } public void setConfiguration(BlurConfiguration configuration) { this._configuration = configuration; } public static TServerTransport getTServerTransport(String bindAddress, int bindPort, BlurConfiguration configuration) throws TTransportException { InetSocketAddress bindInetSocketAddress = getBindInetSocketAddress(bindAddress, bindPort); if (SaslHelper.isSaslEnabled(configuration)) { return new TServerSocket(bindInetSocketAddress); } else { return new TNonblockingServerSocket(bindInetSocketAddress); } } public static int getBindingPort(TServerTransport serverTransport) { if (serverTransport instanceof TNonblockingServerSocket) { TNonblockingServerSocket nonblockingServerSocket = (TNonblockingServerSocket) serverTransport; return nonblockingServerSocket.getServerSocket().getLocalPort(); } else if (serverTransport instanceof TServerSocket) { TServerSocket serverSocket = (TServerSocket) serverTransport; return serverSocket.getServerSocket().getLocalPort(); } else { throw new RuntimeException("Server Transport [" + serverTransport + "] not supported."); } } @SuppressWarnings("unchecked") public static List<ServerSecurityFilter> getServerSecurityList(BlurConfiguration configuration, ServerSecurityFilterFactory.ServerType type) { Map<String, String> properties = configuration.getProperties(); Map<String, String> classMap = new TreeMap<String, String>(); for (Entry<String, String> e : properties.entrySet()) { String property = e.getKey(); String value = e.getValue(); if (value == null || value.isEmpty()) { continue; } if (property.startsWith(BLUR_SERVER_SECURITY_FILTER_CLASS)) { classMap.put(property, value); } } if (classMap.isEmpty()) { return null; } List<ServerSecurityFilter> result = new ArrayList<ServerSecurityFilter>(); for (Entry<String, String> entry : classMap.entrySet()) { String className = entry.getValue(); try { LOG.info("Loading factory class [{0}]", className); Class<? extends ServerSecurityFilterFactory> clazz = (Class<? extends ServerSecurityFilterFactory>) Class .forName(className); ServerSecurityFilterFactory serverSecurityFactory = clazz.newInstance(); result.add(serverSecurityFactory.getServerSecurity(type, configuration)); } catch (Exception e) { throw new RuntimeException(e); } } return result; } public static ZooKeeper setupZookeeper(BlurConfiguration conf, String cluster) throws IOException, InterruptedException, KeeperException { String zkConnectionStr = conf.getExpected(BLUR_ZOOKEEPER_CONNECTION); int sessionTimeout = conf.getInt(BLUR_ZOOKEEPER_TIMEOUT, BLUR_ZOOKEEPER_TIMEOUT_DEFAULT); int slash = zkConnectionStr.indexOf('/'); if ((slash != -1) && (slash != zkConnectionStr.length()-1)) { ZooKeeper rootZk = ZkUtils.newZooKeeper(zkConnectionStr.substring(0, slash), sessionTimeout); String rootPath = zkConnectionStr.substring(slash, zkConnectionStr.length()); if (!ZkUtils.exists(rootZk, rootPath)) { LOG.info("Rooted ZooKeeper path [{0}] did not exist, creating now.", rootPath); ZkUtils.mkNodesStr(rootZk, rootPath); } rootZk.close(); } ZooKeeper zooKeeper = ZkUtils.newZooKeeper(zkConnectionStr, sessionTimeout); BlurUtil.setupZookeeper(zooKeeper, cluster); return zooKeeper; } }