package com.linkedin.databus2.core.container.monitoring.mbean; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * Licensed 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 java.io.IOException; import java.io.OutputStream; import java.net.ConnectException; import java.util.HashSet; import java.util.Hashtable; import java.util.concurrent.locks.Lock; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.apache.avro.io.JsonEncoder; import org.apache.avro.specific.SpecificDatumWriter; import org.apache.log4j.Logger; import org.jboss.netty.handler.timeout.TimeoutException; import com.linkedin.databus.core.monitoring.mbean.AbstractMonitoringMBean; import com.linkedin.databus.core.monitoring.mbean.DatabusMonitoringMBean; import com.linkedin.databus2.core.container.monitoring.events.ContainerTrafficTotalStatsEvent; public class ContainerTrafficTotalStats extends AbstractMonitoringMBean<ContainerTrafficTotalStatsEvent> implements ContainerTrafficTotalStatsMBean { public static final String MODULE = ContainerTrafficTotalStats.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); private final HashSet<Object> _clients; private final String _dimension; /** Used to keep track of the timestamp for the last open connection for thread-unsafe (per * connection) beans*/ private long _lastConnOpenTimestamp; private long _lastConnOpenMergeTimestamp; public ContainerTrafficTotalStats(int containerId, String dimension, boolean enabled, boolean threadSafe, ContainerTrafficTotalStatsEvent initData) { super(enabled, threadSafe, initData); _clients = new HashSet<Object>(1000); _dimension = dimension; _event.containerId = containerId; _event.dimension = dimension; reset(); } public ContainerTrafficTotalStats clone(boolean threadSafe) { return new ContainerTrafficTotalStats(_event.containerId, _dimension, _enabled.get(), threadSafe, getStatistics(null)); } @Override public long getNumBytes() { Lock readLock = acquireReadLock(); long result = 0; try { result = _event.numBytes; } finally { releaseLock(readLock); } return result; } @Override public int getNumClients() { Lock readLock = acquireReadLock(); int result = 0; try { result = _event.numClients; } finally { releaseLock(readLock); } return result; } @Override public long getNumClosedConns() { Lock readLock = acquireReadLock(); long result = 0; try { result = _event.numClosedConns; } finally { releaseLock(readLock); } return result; } @Override public long getNumOpenConns() { Lock readLock = acquireReadLock(); long result = 0; try { result = _event.numOpenConns - _event.numClosedConns; } finally { releaseLock(readLock); } return result; } @Override public long getTimeSinceLastResetMs() { Lock readLock = acquireReadLock(); try { return System.currentTimeMillis() - _event.timestampLastResetMs; } finally { releaseLock(readLock); } } @Override public long getTimestampLastResetMs() { Lock readLock = acquireReadLock(); try { return _event.timestampLastResetMs; } finally { releaseLock(readLock); } } @Override public long getTimeClosedConnLifeMs() { Lock readLock = acquireReadLock(); try { return _event.timeClosedConnLifeMs; } finally { releaseLock(readLock); } } @Override public long getTimeOpenConnLifeMs() { Lock writeLock = acquireWriteLock(); try { long now = (0 == _lastConnOpenTimestamp) ? 0 : System.currentTimeMillis(); long currentOpenTime = (0 == _lastConnOpenTimestamp) ? 0 : now - _lastConnOpenMergeTimestamp; _event.timeOpenConnLifeMs += currentOpenTime; _lastConnOpenMergeTimestamp = now; return _event.timeOpenConnLifeMs - _event.timeClosedConnLifeMs; } finally { releaseLock(writeLock); } } @Override public long getErrorTotalCount() { Lock readLock = acquireReadLock(); try { return _event.errorTotalCount; } finally { releaseLock(readLock); } } @Override public long getErrorConnectCount() { Lock readLock = acquireReadLock(); try { return _event.errorConnectCount; } finally { releaseLock(readLock); } } @Override public long getErrorTimeoutCount() { Lock readLock = acquireReadLock(); try { return _event.errorTimeoutCount; } finally { releaseLock(readLock); } } @Override public long getOpenConnsRate() { return getNumOpenConns(); } @Override public long getClosedConnsRate() { return getNumClosedConns(); } @Override public int getClientsRate() { return getNumClients(); } @Override public long getLatencyOpenConn() { long openConns = getOpenConnsRate(); return 0 == openConns ? 0 : getTimeOpenConnLifeMs() / openConns; } @Override public long getLatencyClosedConn() { long closedConns = getClosedConnsRate(); return 0 == closedConns ? 0 : getTimeClosedConnLifeMs() / closedConns; } public String getDimension() { return _dimension; } public void registerConnectionClose() { if (! _enabled.get()) return; Lock writeLock = acquireWriteLock(); try { long now = isThreadSafe() ? 0 : System.currentTimeMillis(); long connLifespanMs = isThreadSafe() ? 0 : now - _lastConnOpenTimestamp; long unmergedConnLifespanMs = isThreadSafe() ? 0 : now - _lastConnOpenMergeTimestamp; _event.numClosedConns++; _event.timeOpenConnLifeMs += connLifespanMs; _event.timeClosedConnLifeMs += unmergedConnLifespanMs; _lastConnOpenTimestamp = 0; _lastConnOpenMergeTimestamp = 0; } finally { releaseLock(writeLock); } } public void registerConnectionOpen(String client) { if (! _enabled.get()) return; Lock writeLock = acquireWriteLock(); try { _lastConnOpenTimestamp = isThreadSafe() ? 0 : System.currentTimeMillis(); _lastConnOpenMergeTimestamp = _lastConnOpenTimestamp; _event.numOpenConns++; _clients.add(client); _event.numClients = _clients.size(); } finally { releaseLock(writeLock); } } public void registerConnectionError(Throwable error) { if (! _enabled.get()) return; Lock writeLock = acquireWriteLock(); try { ++_event.errorTotalCount; if (error instanceof TimeoutException) ++_event.errorTimeoutCount; else if (error instanceof ConnectException) ++_event.errorConnectCount; } finally { releaseLock(writeLock); } } public void addResponseSize(int bytesSent) { if (! _enabled.get()) return; Lock writeLock = acquireWriteLock(); try { _event.numBytes += bytesSent; } finally { releaseLock(writeLock); } } @Override protected void resetData() { _event.timestampLastResetMs = System.currentTimeMillis(); _event.timeSinceLastResetMs = 0; _event.numBytes = 0; _event.numClients = 0; _event.numClosedConns = 0; _event.numOpenConns = 0; _event.timeClosedConnLifeMs = 0; _event.timeOpenConnLifeMs = 0; _clients.clear(); _lastConnOpenTimestamp = 0; _lastConnOpenMergeTimestamp = 0; _event.errorConnectCount = 0; _event.errorTimeoutCount = 0; _event.errorTotalCount = 0; } @Override public JsonEncoder createJsonEncoder(OutputStream out) throws IOException { return new JsonEncoder(_event.getSchema(), out); } @Override protected void cloneData(ContainerTrafficTotalStatsEvent event) { event.containerId = _event.containerId; event.dimension = _event.dimension; event.timestampLastResetMs = _event.timestampLastResetMs; event.timeSinceLastResetMs = System.currentTimeMillis() - _event.timestampLastResetMs; event.numBytes = _event.numBytes; event.numClients = _event.numClients; event.numClosedConns = _event.numClosedConns; event.numOpenConns = _event.numOpenConns; event.timeClosedConnLifeMs = _event.timeClosedConnLifeMs; event.timeOpenConnLifeMs = _event.timeOpenConnLifeMs; event.errorConnectCount = _event.errorConnectCount; event.errorTimeoutCount = _event.errorTimeoutCount; event.errorTotalCount = _event.errorTotalCount; } @Override protected ContainerTrafficTotalStatsEvent newDataEvent() { return new ContainerTrafficTotalStatsEvent(); } @Override protected SpecificDatumWriter<ContainerTrafficTotalStatsEvent> getAvroWriter() { return new SpecificDatumWriter<ContainerTrafficTotalStatsEvent>( ContainerTrafficTotalStatsEvent.class); } @Override public void mergeStats(DatabusMonitoringMBean<ContainerTrafficTotalStatsEvent> other) { super.mergeStats(other); if (other instanceof ContainerTrafficTotalStats) { ContainerTrafficTotalStats o = (ContainerTrafficTotalStats)other; o.getLatencyOpenConn(); //hack to update the open connections latency which is computed //on demand mergeClients(o); } } @Override protected void doMergeStats(Object eventData) { if (! (eventData instanceof ContainerTrafficTotalStatsEvent)) { LOG.warn("Attempt to merge unknown event class: " + eventData.getClass().getName()); return; } ContainerTrafficTotalStatsEvent e = (ContainerTrafficTotalStatsEvent)eventData; /** Allow use negative relay IDs for aggregation across multiple relays */ if (_event.containerId > 0 && e.containerId != _event.containerId) { LOG.warn("Attempt to data for a different relay " + e.containerId); return; } _event.numBytes += e.numBytes; _event.numClosedConns += e.numClosedConns; _event.numOpenConns += e.numOpenConns; _event.timeClosedConnLifeMs += e.timeClosedConnLifeMs; _event.timeOpenConnLifeMs += e.timeOpenConnLifeMs; _event.errorConnectCount += e.errorConnectCount; _event.errorTotalCount += e.errorTotalCount; _event.errorTimeoutCount += e.errorTimeoutCount; // numClients cannot be merged } /** A bit of a hack to merge state outside the event state */ private void mergeClients(ContainerTrafficTotalStats other) { Lock otherReadLock = other.acquireReadLock(); Lock writeLock = acquireReadLock(otherReadLock); try { _clients.addAll(other._clients); _event.numClients = _clients.size(); } finally { releaseLock(writeLock); releaseLock(otherReadLock); } } @Override public ObjectName generateObjectName() throws MalformedObjectNameException { Hashtable<String, String> mbeanProps = generateBaseMBeanProps(); mbeanProps.put("containerId", Integer.toString(_event.containerId)); mbeanProps.put("dimension", getDimension()); return new ObjectName(AbstractMonitoringMBean.JMX_DOMAIN, mbeanProps); } }