Java tutorial
/** * 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.htrace.impl; import java.io.IOException; import java.io.File; import java.net.InetSocketAddress; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.apache.htrace.core.HTraceConfiguration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * The configuration of the HTracedSpanReceiver. * * This class extracts all the relevant configuration information for * HTracedSpanReceiver from the HTraceConfiguration. It performs parsing and * bounds-checking for the configuration keys. * * It is more efficient to store the configuration as final values in this * structure than to access the HTraceConfiguration object directly. This is * especially true when the HTraceConfiguration object is a thin shim around a * Hadoop Configuration object, which requires synchronization to access. */ class Conf { private static final Log LOG = LogFactory.getLog(Conf.class); /** * Address of the htraced server. */ final static String ADDRESS_KEY = "htraced.receiver.address"; /** * The minimum number of milliseconds to wait for a read or write * operation on the network. */ final static String IO_TIMEOUT_MS_KEY = "htraced.receiver.io.timeout.ms"; final static int IO_TIMEOUT_MS_DEFAULT = 60000; final static int IO_TIMEOUT_MS_MIN = 50; /** * The minimum number of milliseconds to wait for a network * connection attempt. */ final static String CONNECT_TIMEOUT_MS_KEY = "htraced.receiver.connect.timeout.ms"; final static int CONNECT_TIMEOUT_MS_DEFAULT = 60000; final static int CONNECT_TIMEOUT_MS_MIN = 50; /** * The minimum number of milliseconds to keep alive a connection when it's * not in use. */ final static String IDLE_TIMEOUT_MS_KEY = "htraced.receiver.idle.timeout.ms"; final static int IDLE_TIMEOUT_MS_DEFAULT = 60000; final static int IDLE_TIMEOUT_MS_MIN = 0; /** * Configure the retry times to use when an attempt to flush spans to * htraced fails. This is configured as a comma-separated list of delay * times in milliseconds. If the configured value is empty, no retries * will be made. */ final static String FLUSH_RETRY_DELAYS_KEY = "htraced.flush.retry.delays.key"; final static String FLUSH_RETRY_DELAYS_DEFAULT = "1000,30000"; /** * The maximum length of time to go in between flush attempts. * Once this time elapses, a flush will be triggered even if we don't * have that many spans buffered. */ final static String MAX_FLUSH_INTERVAL_MS_KEY = "htraced.receiver.max.flush.interval.ms"; final static int MAX_FLUSH_INTERVAL_MS_DEFAULT = 60000; final static int MAX_FLUSH_INTERVAL_MS_MIN = 10; /** * Whether or not to use msgpack for span serialization. * If this key is false, JSON over REST will be used. * If this key is true, msgpack over custom RPC will be used. */ final static String PACKED_KEY = "htraced.receiver.packed"; final static boolean PACKED_DEFAULT = true; /** * The size of the span buffers. */ final static String BUFFER_SIZE_KEY = "htraced.receiver.buffer.size"; final static int BUFFER_SIZE_DEFAULT = 16 * 1024 * 1024; static int BUFFER_SIZE_MIN = 4 * 1024 * 1024; // The maximum buffer size should not be longer than // PackedBuffer.MAX_HRPC_BODY_LENGTH. final static int BUFFER_SIZE_MAX = 32 * 1024 * 1024; /** * Set the fraction of the span buffer which needs to fill up before we * will automatically trigger a flush. This is a fraction, not a percentage. * It is between 0 and 1. */ final static String BUFFER_SEND_TRIGGER_FRACTION_KEY = "htraced.receiver.buffer.send.trigger.fraction"; final static double BUFFER_SEND_TRIGGER_FRACTION_DEFAULT = 0.5; final static double BUFFER_SEND_TRIGGER_FRACTION_MIN = 0.1; /** * The length of time which receiveSpan should wait for a free spot in a * span buffer before giving up and dropping the span */ final static String SPAN_DROP_TIMEOUT_MS_KEY = "htraced.max.buffer.full.retry.ms.key"; final static int SPAN_DROP_TIMEOUT_MS_DEFAULT = 5000; /** * The length of time we should wait between displaying log messages on the * rate-limited loggers. */ final static String ERROR_LOG_PERIOD_MS_KEY = "htraced.error.log.period.ms"; final static long ERROR_LOG_PERIOD_MS_DEFAULT = 30000L; final static String DROPPED_SPANS_LOG_PATH_KEY = "htraced.dropped.spans.log.path"; final static String DROPPED_SPANS_LOG_PATH_DEFAULT = new File(System.getProperty("java.io.tmpdir", "/tmp"), "htraceDropped").getAbsolutePath(); final static String DROPPED_SPANS_LOG_MAX_SIZE_KEY = "htraced.dropped.spans.log.max.size"; final static long DROPPED_SPANS_LOG_MAX_SIZE_DEFAULT = 1024L * 1024L; @JsonProperty("ioTimeoutMs") final int ioTimeoutMs; @JsonProperty("connectTimeoutMs") final int connectTimeoutMs; @JsonProperty("idleTimeoutMs") final int idleTimeoutMs; @JsonProperty("flushRetryDelays") final int[] flushRetryDelays; @JsonProperty("maxFlushIntervalMs") final int maxFlushIntervalMs; @JsonProperty("packed") final boolean packed; @JsonProperty("bufferSize") final int bufferSize; @JsonProperty("spanDropTimeoutMs") final int spanDropTimeoutMs; @JsonProperty("errorLogPeriodMs") final long errorLogPeriodMs; @JsonProperty("triggerSize") final int triggerSize; @JsonProperty("endpointStr") final String endpointStr; @JsonProperty("endpoint") final InetSocketAddress endpoint; @JsonProperty("droppedSpansLogPath") final String droppedSpansLogPath; @JsonProperty("droppedSpansLogMaxSize") final long droppedSpansLogMaxSize; private static int getBoundedInt(final HTraceConfiguration conf, String key, int defaultValue, int minValue, int maxValue) { int val = conf.getInt(key, defaultValue); if (val < minValue) { LOG.warn( "Can't set " + key + " to " + val + ". Using minimum value " + "of " + minValue + " instead."); return minValue; } else if (val > maxValue) { LOG.warn( "Can't set " + key + " to " + val + ". Using maximum value " + "of " + maxValue + " instead."); return maxValue; } return val; } private static long getBoundedLong(final HTraceConfiguration conf, String key, long defaultValue, long minValue, long maxValue) { String strVal = conf.get(key, Long.toString(defaultValue)); long val = 0; try { val = Long.parseLong(strVal); } catch (NumberFormatException nfe) { throw new IllegalArgumentException("Bad value for '" + key + "': should be long"); } if (val < minValue) { LOG.warn( "Can't set " + key + " to " + val + ". Using minimum value " + "of " + minValue + " instead."); return minValue; } else if (val > maxValue) { LOG.warn( "Can't set " + key + " to " + val + ". Using maximum value " + "of " + maxValue + " instead."); return maxValue; } return val; } private static double getBoundedDouble(final HTraceConfiguration conf, String key, double defaultValue, double minValue, double maxValue) { String strVal = conf.get(key, Double.toString(defaultValue)); double val = 0; try { val = Double.parseDouble(strVal); } catch (NumberFormatException nfe) { throw new IllegalArgumentException("Bad value for '" + key + "': should be double"); } if (val < minValue) { LOG.warn( "Can't set " + key + " to " + val + ". Using minimum value " + "of " + minValue + " instead."); return minValue; } if (val > maxValue) { LOG.warn( "Can't set " + key + " to " + val + ". Using maximum value " + "of " + maxValue + " instead."); return maxValue; } return val; } private static int parseColonPort(String portStr) throws IOException { int colonPosition = portStr.indexOf(':'); if (colonPosition != 0) { throw new IOException("Invalid port string " + portStr); } int port = Integer.parseInt(portStr.substring(1), 10); if ((port < 0) || (port > 65535)) { throw new IOException("Invalid port number " + port); } return port; } /** * Parse a hostname:port or ip:port pair. * * @param str The string to parse. * @return The socket address. */ InetSocketAddress parseHostPortPair(String str) throws IOException { str = str.trim(); if (str.isEmpty()) { throw new IOException("No hostname:port pair given."); } int bracketBegin = str.indexOf('['); if (bracketBegin == 0) { // Parse an ipv6-style address enclosed in square brackets. int bracketEnd = str.indexOf(']'); if (bracketEnd < 0) { throw new IOException("Found left bracket, but no corresponding " + "right bracket, in " + str); } String host = str.substring(bracketBegin + 1, bracketEnd); int port = parseColonPort(str.substring(bracketEnd + 1)); return InetSocketAddress.createUnresolved(host, port); } else if (bracketBegin > 0) { throw new IOException( "Found a left bracket that wasn't at the " + "start of the host:port pair in " + str); } else { int colon = str.indexOf(':'); if (colon <= 0) { throw new IOException("No port component found in " + str); } String host = str.substring(0, colon); int port = parseColonPort(str.substring(colon)); return InetSocketAddress.createUnresolved(host, port); } } static int[] getIntArray(String arrayStr) { String[] array = arrayStr.split(","); int nonEmptyEntries = 0; for (String str : array) { if (!str.trim().isEmpty()) { nonEmptyEntries++; } } int[] ret = new int[nonEmptyEntries]; int i = 0; for (String str : array) { if (!str.trim().isEmpty()) { ret[i++] = Integer.parseInt(str); } } return ret; } Conf(HTraceConfiguration conf) throws IOException { this.ioTimeoutMs = getBoundedInt(conf, IO_TIMEOUT_MS_KEY, IO_TIMEOUT_MS_DEFAULT, IO_TIMEOUT_MS_MIN, Integer.MAX_VALUE); this.connectTimeoutMs = getBoundedInt(conf, CONNECT_TIMEOUT_MS_KEY, CONNECT_TIMEOUT_MS_DEFAULT, CONNECT_TIMEOUT_MS_MIN, Integer.MAX_VALUE); this.idleTimeoutMs = getBoundedInt(conf, IDLE_TIMEOUT_MS_KEY, IDLE_TIMEOUT_MS_DEFAULT, IDLE_TIMEOUT_MS_MIN, Integer.MAX_VALUE); this.flushRetryDelays = getIntArray(conf.get(FLUSH_RETRY_DELAYS_KEY, FLUSH_RETRY_DELAYS_DEFAULT)); this.maxFlushIntervalMs = getBoundedInt(conf, MAX_FLUSH_INTERVAL_MS_KEY, MAX_FLUSH_INTERVAL_MS_DEFAULT, MAX_FLUSH_INTERVAL_MS_MIN, Integer.MAX_VALUE); this.packed = conf.getBoolean(PACKED_KEY, PACKED_DEFAULT); this.bufferSize = getBoundedInt(conf, BUFFER_SIZE_KEY, BUFFER_SIZE_DEFAULT, BUFFER_SIZE_MIN, BUFFER_SIZE_MAX); double triggerFraction = getBoundedDouble(conf, BUFFER_SEND_TRIGGER_FRACTION_KEY, BUFFER_SEND_TRIGGER_FRACTION_DEFAULT, BUFFER_SEND_TRIGGER_FRACTION_MIN, 1.0); this.spanDropTimeoutMs = conf.getInt(SPAN_DROP_TIMEOUT_MS_KEY, SPAN_DROP_TIMEOUT_MS_DEFAULT); this.errorLogPeriodMs = getBoundedLong(conf, ERROR_LOG_PERIOD_MS_KEY, ERROR_LOG_PERIOD_MS_DEFAULT, 0, Long.MAX_VALUE); this.triggerSize = (int) (this.bufferSize * triggerFraction); try { this.endpointStr = conf.get(ADDRESS_KEY, ""); this.endpoint = parseHostPortPair(endpointStr); } catch (IOException e) { throw new IOException("Error reading " + ADDRESS_KEY + ": " + e.getMessage()); } this.droppedSpansLogPath = conf.get(DROPPED_SPANS_LOG_PATH_KEY, DROPPED_SPANS_LOG_PATH_DEFAULT); this.droppedSpansLogMaxSize = getBoundedLong(conf, DROPPED_SPANS_LOG_MAX_SIZE_KEY, DROPPED_SPANS_LOG_MAX_SIZE_DEFAULT, 0, Long.MAX_VALUE); } @Override public String toString() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); try { return mapper.writeValueAsString(this); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }